Honestly, I am surprised the first didn't work. The differences between this function and the ones above are almost all stylistic and, in fact, in hindsight I made at least a couple of mistakes. I'll give a brief breakdown of what's happening in this piece of code, so you can follow along. I'm going to include some links for greater clarity. They're all quite short articles providing better technical clarity than I am able to in a forum post. I'd recommend at least reading the beginning of each, or the linked section, but they're not strictly necessary to follow along. Also, this is going to look long and complicated, but it isn't. Well, it isn't complicated, at least. on is a function provided to us by Roll20. In this instance, accepts two arguments, which you can visualize like this: on(argument1, argument2) . The arguments we provide it are a string, containing a list of triggers and a function, that will be executed when one of the trigger requirements are met. For instance, a change to MonsterIntCat triggers us to run the function in the second argument. First Mistake: on is, luckily for my code, case insensitive. This means that monsterintcat and MonsterIntCat are functionally equivalent, but it's still good practice to make sure that these are cased consistently. on("change:MonsterIntCat change:Intelligence sheet:opened"... callbacks are functions we pass as an argument to a function that are then executed by that function. This is only important to us right now in that I will refer to the second argument of the on function as a callback function. eventInfo is a default object that the trigger provides us when the function is run. It contains some contextual information that might be useful to our function. We can access them with dot notation So, for instance, eventInfo.newValue tells you the new value of the changed field. There's a short table here listing the contents. Second Mistake: In this instance, we never use eventInfo, and I only included it as a force of habit. It's needlessly muddying the waters here. You could replace [ (eventInfo) => { ] with [ () => { ] or even [ function () { ] and it would be functionality (haha.) equivalent. Which leads me to arrow functions . An arrow function is a slightly different way of defining a function. There are some differences between an arrow function [ () => {} ] and a regular function [ function() {} ] but in this instance, none of those are relevant, so it's just a stylistic choice. I happen to find arrow functions quicker to write and less visually confusing to read, YMMV. getAttrs is very similar to on . They're both defined by Roll20, both take two arguments. The first argument in this case is an array of "attrs" that we wish to get the values of. The second is a function, but in this case instead of providing us with eventInfo, it provides us with values, which is an object containing the names and values we requested in the first argument. You can imagine it like this: values = { "Intelligence":"10", "OtherExampleAttr":"12", "Etc":"14" } Third Mistake: Again, stylistic, but if I used an arrow function before, I should have used it here, too, for consistency and readability. getAttrs(["Intelligence",], (values) => { Okay, so now we have our object values which contains the data we want to operate on. Variables requested from Roll20 will often, but not always, arrive to us as a string . It can honestly be quite hard to predict which form they're going to come in, and so it's generally a good idea to convert them into the precise type of variable we want. In this instance, we want a number, and more specifically an Integer . So we define a new variable and use [ parseInt() ] to make it an integer. Defining variables. When defining variables, we have three choices. We can define a let , a const or a var . The rule is this: if you're intending to redefine the value of a variable, use a let. If you're defining a variable once, and it will not change, we use const. There are a few very edge cases to use a var, but a good working rule of thumb is to never use a var. Fourth Mistake: I should have actually made modifier a const. It's defined once, and never redefined. const modifier = (intelligence === 0) ? "Non" : ... Ternary operators. The main difference between my code and the code above is the conditional chain I used to set modifier's value. This is super simple: const value = (condition1) ? value1 : (condition2) ? value2 : (condition3) ? value3 : value4; // Is equivalent to: if (condition1) const value = value1; else if (condition2) const value = value2; else if (condition3) const value = value3; else const value = value4; Simply put, if all you're doing is declaring a variable, a ternary operator is a quicker and cleaner way to write a complicated if/else statement. Again, it's mostly a stylistic preference, but I find the former easier to read. And that's basically it. We use setAttrs to tell Roll20 to update the new value, but this is pretty self explanatory.