Using Sheet Workers instead of AutoCalcs

In this post, you will learn how to use Sheet Workers to replace AutoCalc attributes, without understanding anything about sheet workers!

This is how most people start to use Sheet Workers. Later posts explain in detail how and why a sheet worker works and how you can do other things, but if all you want to do is use Sheet Workers instead of AutoCalc attributes, this post is all you need.

Some advantages of sheet workers over AutoCalc fields:

  • You can use them for token bar values.
  • You can calculate multiple values in a single worker.
  • They are a lot more efficient and cause less lag.
  • You can use attributes created by sheet workers in other sheet workers.

That last part is important. You cannot use AutoCalc attributes in a sheet worker, so after you start using sheet workers, you’ll often have to update many of your AutoCalcs to sheet workers. Later posts will show how to do that very quickly, but this post is about the basics.

Two Examples of Sheet Workers

Here are two basic sheet workers. Have a look and see what they have in common.

on('change:strength', function() { getAttrs(['strength'], function(values) { let score = parseInt(values.strength) || 0; let modifier = Math.floor(score/2) -5 setAttrs({ strength_mod: modifier }); }); });
Code language: JavaScript (javascript)
on('change:health change:size', function() { getAttrs(['health', 'size'], function(values) { let con = parseInt( || 0; let size = parseInt(values.size) || 0; let hit_points = con + size; setAttrs({ hit_points: hit_points }); }); });
Code language: JavaScript (javascript)

The left sheet worker calculates a D&D-style stat bonus, and the right size adds two attributes together to calculate hit points the way you would in RuneQuest or Call of Cthulhu.

You’ll notice those sheet workers have a lot in common. In fact, each sheet worker is divided into five distinct sections as shown in these garish pictures:

The five sections are Event, Collection, Naming, Calculation, and Saving. Posts following this will describe each section in detail, and describe why they work the way they do. But in this post, you’ll just learn how to use the sections without needing to understand them.


So you have a sheet worker in mind. Does it calculate a stat modifier, add multiple stats together to calculate hit points or attack bonus, or look at a table to get speed or carrying capacity?

Whatever it does, make a note of which attributes are needed in the calculation, and then proceed.

In the event line, you tell Roll20 what events to watch for. For now, the only events we are concerned with are changes in attributes. If strength changes from 11 to 12, its modifier needs to be recalculated, and when health or size change, hit points need to update.

Every event line starts like this:

on('', function() {
Code language: JavaScript (javascript)

Then add the attributes you need between the quotes, with the word change: before them, and if there are more than one, with a space between them, like so:

on('change:health change:size', function() {
Code language: JavaScript (javascript)

That’s all there is to it. There’s one small gotcha:

Attributes must be in lower case here (and only here) – if your attribute is named Strength, you must still write change:strength here. This only applies to the event line, but it’s a good idea to make all your attribute names lower-cased, so you don’t have to remember this rule.

A sheet worker knows nothing about the character sheet it is part of. Sheet workers don’t automatically know the attributes and their values on the sheet – you need to tell the sheet worker which ones to get. That’s what this part of the sheet worker does.

The first line of each collection section starts off like this:

getAttrs([ ], function(values) {
Code language: JavaScript (javascript)

All you need to do is put the attributes between the [ ]. Each attribute must be between quotes, and separated by a comma (the space is optional), like so:

getAttrs(['health', 'size'], function(values) {
Code language: JavaScript (javascript)

And that’s it. One thing to realise: you only need to put attributes whose values you need to read from the sheet. If you are creating or overwriting attributes, you might not need to grab their values, so they aren’t needed here.

In the Collection phase, the sheet worker collected all the attributes and stored their name and value in the values object. That’s what the function(values) at the end of that line represents.

But to actually use the attributes, it’s easiest to extract the attributes from the values object and store them in a variable. That’s what lines like this are for:

let score = parseInt(values.strength) || 0;
Code language: JavaScript (javascript)
let con = parseInt( || 0; let size = parseInt(values.size) || 0;
Code language: JavaScript (javascript)

These lines create variables called score, con, and size respectively. You’ll see how they are used in the next tab. The format is always the same:

let variable = parseInt(values.attribute) || 0;
Code language: JavaScript (javascript)

Simply replace the word variable with whatever name you want to use in the calculation, and attribute with the attribute’s name.

You can name the variable the same as the attribute – I probably should have done that in these examples. So this is fine:

let strength = parseInt(values.strength) || 0;
Code language: JavaScript (javascript)
let health = parseInt( || 0; let size = parseInt(values.size) || 0;
Code language: JavaScript (javascript)

Note that you end each line with a semi-colon.

Here you perform any arithmetic you’d perform in a sheet worker, using variables in place of attribute names. The usual arithmetic operators (+-*/) are available, and BODMAS rules are in effect, so you can do things like (3+2) *3 +7 and get the correct answer.

You need to store the result of any calculation in a variable. For example:

let modifier = Math.floor(score/2) -5;
Code language: JavaScript (javascript)
let hit_points = con + size;
Code language: JavaScript (javascript)

For simple sheet workers, the format is the same. You start with this:

let variable = ;
Code language: JavaScript (javascript)

Change variable to whatever name you want to use. Then include the arithmetic you need before the semi-colon, using the variables you created in the previous section.

There are some special functions. For rounding, use Math.floor() to round down, Math.ceil() to round up, and Math.round() to round to the nearest.

There are a bunch of other functions, but for replacing AutoCalcs, that should cover nearly everything you need to do.

Multiple Results

One advantage of sheet workers over AutoCalcs is that you can calculate multiple results in the same sheet worker. Say carrying capacity, damage bonus, and a general strength bonus are all based on your strength score. You could calculate them all together like:

let modifier = Math.floor(score/2) -5; let carry = strength *2 + 100; let damage_bonus = Math.round(score/5);
Code language: JavaScript (javascript)

These aren’t meant to represent any game system but are just examples of calculations.

As with Naming, end each line with a semi-colon.

The final step: you have calculated a result. That result now needs to be saved to the character sheet. This section of the sheet starts off like this:

setAttrs({ }); }); });
Code language: CSS (css)

All you need to do is enter the attributes name on the sheet, with the result after a colon, like

setAttrs({ strength_mod: modifier }); }); });
Code language: CSS (css)

The name of the attribute on the sheet goes on the left, and the name of your calculated value goes on the right.

If you calculated multiple results in the same worker, separate them with a comma and a line break like this:

setAttrs({ strength_mod: modifier, carry_cap: carry, damage_mod: damage_bonus }); }); });
Code language: CSS (css)

The last one doesn’t have a comma after it. Don’t do this:

setAttrs({ strength_mod: modifier, carry_cap: carry, damage_mod: damage_bonus, }); }); });
Code language: CSS (css)

It seems like a small thing – but that will break the worker.


And that’s it. When you are starting out, every sheet worker is created the same way. The event, collection, naming, and saving lines all follow the same format, and you just need to follow the guidelines listed here. The Calculation is the part that varies, but is pretty simple for this post. The next posts will show how this can become much more complex.

  1. Event: on(‘change:attribute1 change:attribute2’), function({ – change:attribute in lower case. The whole list of attributes goes inside one set of quotes.
  2. Collection: getAttrs([‘attribute1’, ‘attribute’], function(values) { – each attribute in its own quotes, separated by commas.
  3. Naming: let stat1 = parseInt(values.attribute1) || 0;
  4. Calculation: perform simple arithmetic: let result = stat1 + stat2;
  5. Updating: setAttrs({attribute3: result}); – multiple attributes are separated with commas, and don’t put a comma on the last one.

Naming Advice

When creating attribute names in the HTML, it’s a good idea to follow these rules:

  • Always use lower case.
  • Don’t use spaces, and use only letters, numbers, and underscores.
  • Never start an attribute name with a number.

None of these rules are required but using them will make things a lot easier for you when writing sheet workers. I’ll explain why in the next post.

Series NavigationVariables in JavaScript and Grabbing Attributes >>

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.