Most sheets have a custom rollTemplate – sometimes more than one. It’s a good idea to think about the roll(s) you’ll need early on, and plan the rollTemplate(s) as soon as you can. We are going to make one for TurboPulp now, and figure out what is needed for a roll.
The Abilities section starts with this roll buttton – each skill has one of these. The important part is the value.
<button type="roll" name="roll_Audacity" class="nod20 Audacity tokenaction"
value="&{template:ladder} {{header=1}} {{who=@{character_name} }} {{skill=Audacity }} {{die1=[[1d10]] }} {{die2=[[1d10]] }} {{score=[[@{audacity_score} ]] }}">
<span>Audacity</span>
</button>
Code language: JavaScript (javascript)
This creates a roll and sends its output to the rolltemplate, which we will look at now.
How Are Rolls Made
A roll (rough mock up) can look like this:
- Person [name of person] Rolls Skill [name of skill]
- Person has a skill Rank which gives a Target (number that is usually 3-7, but can theoretically be 1-10) vs Challenge of two d10s
- Result: if the Target is equal to or greater than either die, it is a Weak Hit. If it is greater than or equal both dice, it is a Strong Hit. If it is neither, that is it is less than both dice, if is a Miss.
- Comment: Include a space for entering descriptive text
Roll Variations
The roll might omit name of Person, Skill, Target, and maybe even the Challenge Roll if you just want a comment, and can omit comment.
Only players make the primary roll, for their characters, and that simplifies one part of the roll – getting a character name. We don’t have to think about the token (which you do, if you want to supply a name and the character sheet can represent different characters who whose name is set in the token).
Starting The rollTemplate
We will start by building the template’s HTML. We’ll called the template ladder (for reasons which will become apparent later), so we need a basic framework, like this:
<rolltemplate class="sheet-rolltemplate-ladder">
</rolltemplate>
Code language: HTML, XML (xml)
Everything inside that rollTemplate tags will be the rollTemplate output. Lets test it works by changing the template to this:
<rolltemplate class="sheet-rolltemplate-ladder">
a test
</rolltemplate>
Code language: HTML, XML (xml)
Now we can create a simple roll macro, like &{template:ladder} ?
– every rollTemplate must be invoked with a start like this, and you need something, anything, after the curly brackets – that’s why the ? is there. Once we trigger that we see

so we know it is working (so far). When coding, the adage, “test early, test often” could not be more true. You cannot test too much. But to keep this article short, we’ll only show tests when relevant – assume I’m testing everything!
Now we want to think about what a roll looks like, and how the template must be shaped.
Building The Header
We know the section might need a title, a header. In roll20 rolltemplates, class names must be preceded with sheet- (at least in the CSS – its not required to include it in the HTML but we are including it here for simplicity and consistency.).
<rolltemplate class="sheet-rolltemplate-ladder">
{{#header}}
<div class="sheet-header">
{{header}}
</div>
{{/header}}
a rollTemplate
</rolltemplate>
Code language: HTML, XML (xml)
Look at that code. We have a div
for the header, and some logic that tests if the header exists. If so, it prints out the header here. Note ythat header is a simple value, and if anything is there, the header will get printed. This allows you to include a header of not.
If we wanted to include something that happens when the header does not exist, we can do that too, like:
{{^header}}
What happens if there is no header
{{/header}}
Code language: HTML, XML (xml)
We’d probably want to reorganize things to look more like this:
<rolltemplate class="sheet-rolltemplate-ladder">
<div class="sheet-header">
{{#header}}
{{header}}
{{/header}}
{{^header}}
What do we put here if there's no header property
{{/header}}
</div>
</rolltemplate>
Code language: HTML, XML (xml)
With this approach, the header banner will still exist even if there is no header, and this accounts for this. But in this approach, we aren’t doing that – the banner only exists if there is a header.
We can then include a {{who=}}
and a {{skill=}}
setting, to show what is being rolled and by who, and they’ll be shown in the header.
Handling The Roll
The roll itself is the reason the template exists, and is the main reason the template exists. We could have build this with a custom roll template (and an earlier version did), but the dependence on action buttons (which have some limitations) led to creating it as a roll button.
This section of the template depends on several things: a skill rank, the skill index, the challenge dice (two d10), and the result text – which is calculated entirely in the roll template.
Really only the skill index or rank is needed – everything else can be calculated in the roll, so we just need to figure out how that is done. But the dice roll properties need to be created for the roll. A typical roll looks something like this:
&{template:ladder} {{header=1}} {{who=@{default|character_name} }} {{skill=Daring }} {{die1=[[1d10]] }} {{die2=[[1d10]] }} {{score=[[@{default|Daring_score} ]] }}
You can enter any number with the score
, but die1
and die2
have to be there, exactly as shown.
Dealing With The D10’s
We want to display the dice as die icons, and roll20 does include several icon font. This is two steps. First we need to set the font in CSS, and choose this method:
.sheet-rolltemplate-ladder .sheet-content .sheet-value .sheet-d10 {
font-family: dicefontd10;
font-size: 350%;
}
.sheet-rolltemplate-ladder .sheet-content .sheet-value {
display: flex;
align-items: center;
}
Code language: CSS (css)
Here we set the font to the dicefontd10 font, and make it bigger so the numbers arereadable. Then we set the grid item containing them to use the flex property, so they are aligned better.
Then we return to the rollTemplate’s HTML, and make heavy use of the Logic Helpers to change each possible number to a letter – that meas it shows up as the proper dice icon. Remember nothing is shown up if there is no match, so there are 10 items for each die but only one is shown.
<div class="sheet-content">
<div class="sheet-key">{{#skill}}{{skill}}{{/skill}}{{^skill}}Ability{{/skill}}:</div>
<div class="sheet-value">
{{score}} vs
<span class="d10">
{{#rollTotal() die1 1}}a{{/rollTotal() die1 1}}
{{#rollTotal() die1 2}}b{{/rollTotal() die1 2}}
{{#rollTotal() die1 3}}c{{/rollTotal() die1 3}}
{{#rollTotal() die1 4}}d{{/rollTotal() die1 4}}
{{#rollTotal() die1 5}}e{{/rollTotal() die1 5}}
{{#rollTotal() die1 6}}f{{/rollTotal() die1 6}}
{{#rollTotal() die1 7}}g{{/rollTotal() die1 7}}
{{#rollTotal() die1 8}}h{{/rollTotal() die1 8}}
{{#rollTotal() die1 9}}i{{/rollTotal() die1 9}}
{{#rollTotal() die1 10}}j{{/rollTotal() die1 10}}
</span>
<span class="d10">
{{#rollTotal() die2 1}}a{{/rollTotal() die2 1}}
{{#rollTotal() die2 2}}b{{/rollTotal() die2 2}}
{{#rollTotal() die2 3}}c{{/rollTotal() die2 3}}
{{#rollTotal() die2 4}}d{{/rollTotal() die2 4}}
{{#rollTotal() die2 5}}e{{/rollTotal() die2 5}}
{{#rollTotal() die2 6}}f{{/rollTotal() die2 6}}
{{#rollTotal() die2 7}}g{{/rollTotal() die2 7}}
{{#rollTotal() die2 8}}h{{/rollTotal() die2 8}}
{{#rollTotal() die2 9}}i{{/rollTotal() die2 9}}
{{#rollTotal() die2 10}}j{{/rollTotal() die2 10}}
</span>
</div>
Code language: HTML, XML (xml)
Calculating The Result
Finally, we make heavy use of the rollTemplate Logic Helpers again. The dice mechanic is fairly simple – you start with a number (score), then compare two d10s to it. If the number beats both dice, it is a Strong Success; if the number beats only one die, it’s a Weak Hit, and if it beats none, it is a Miss. Because there are two dice, we have to check both dice in turn for each of these results. It’s a bit convoluted, but it works.
<div class="sheet-key">Result:</div>
<div class="sheet-value">
{{#rollGreater() die1 score}}
{{#rollGreater() die2 score}}
Miss
{{/rollGreater() die2 score}}
{{/rollGreater() die1 score}}
{{#^rollGreater() die1 score}}
{{#^rollGreater() die2 score}}
Strong Hit
{{/^rollGreater() die2 score}}
{{/^rollGreater() die1 score}}
{{#rollGreater() die1 score}}
{{#^rollGreater() die2 score}}
Weak Hit
{{/^rollGreater() die2 score}}
{{/rollGreater() die1 score}}
{{#^rollGreater() die1 score}}
{{#rollGreater() die2 score}}
Weak Hit
{{/rollGreater() die2 score}}
{{/^rollGreater() die1 score}}
</div>
Code language: HTML, XML (xml)
Finally we end with a comment box, followup the same logic as the header, and end up with:
The Full Roll Template
<rolltemplate class="sheet-rolltemplate-ladder">
{{#header}}
<div class="sheet-header">
{{who}} rolls {{#skill}}{{skill}}{{/skill}}{{^skill}}a Skill{{/skill}}
</div>
{{/header}}
<div class="sheet-content">
<div class="sheet-key">{{#skill}}{{skill}}{{/skill}}{{^skill}}Ability{{/skill}}:</div>
<div class="sheet-value">
{{score}} vs
<span class="d10">
{{#rollTotal() die1 1}}a{{/rollTotal() die1 1}}
{{#rollTotal() die1 2}}b{{/rollTotal() die1 2}}
{{#rollTotal() die1 3}}c{{/rollTotal() die1 3}}
{{#rollTotal() die1 4}}d{{/rollTotal() die1 4}}
{{#rollTotal() die1 5}}e{{/rollTotal() die1 5}}
{{#rollTotal() die1 6}}f{{/rollTotal() die1 6}}
{{#rollTotal() die1 7}}g{{/rollTotal() die1 7}}
{{#rollTotal() die1 8}}h{{/rollTotal() die1 8}}
{{#rollTotal() die1 9}}i{{/rollTotal() die1 9}}
{{#rollTotal() die1 10}}j{{/rollTotal() die1 10}}
</span>
<span class="d10">
{{#rollTotal() die2 1}}a{{/rollTotal() die2 1}}
{{#rollTotal() die2 2}}b{{/rollTotal() die2 2}}
{{#rollTotal() die2 3}}c{{/rollTotal() die2 3}}
{{#rollTotal() die2 4}}d{{/rollTotal() die2 4}}
{{#rollTotal() die2 5}}e{{/rollTotal() die2 5}}
{{#rollTotal() die2 6}}f{{/rollTotal() die2 6}}
{{#rollTotal() die2 7}}g{{/rollTotal() die2 7}}
{{#rollTotal() die2 8}}h{{/rollTotal() die2 8}}
{{#rollTotal() die2 9}}i{{/rollTotal() die2 9}}
{{#rollTotal() die2 10}}j{{/rollTotal() die2 10}}
</span>
</div>
<div class="sheet-key">Result:</div>
<div class="sheet-value">
{{#rollGreater() die1 score}}
{{#rollGreater() die2 score}}
Miss
{{/rollGreater() die2 score}}
{{/rollGreater() die1 score}}
{{#^rollGreater() die1 score}}
{{#^rollGreater() die2 score}}
Strong Hit
{{/^rollGreater() die2 score}}
{{/^rollGreater() die1 score}}
{{#rollGreater() die1 score}}
{{#^rollGreater() die2 score}}
Weak Hit
{{/^rollGreater() die2 score}}
{{/rollGreater() die1 score}}
{{#^rollGreater() die1 score}}
{{#rollGreater() die2 score}}
Weak Hit
{{/rollGreater() die2 score}}
{{/^rollGreater() die1 score}}
</div>
{{#comment}}
<div class="sheet-comment">
{{comment}}
</div>
{{/comment}}
</rolltemplate>
Code language: HTML, XML (xml)
Complication: Trait Rolls
When using a Trait, one of the Challenge Dice is changed to a 1. This is easily handled verbally – just increase the quality of hit by one level: Miss -> Weak Hit -> Strong Hit. But if want it to be represented in roll, will need to store anything reported in the original troll (which must include the Challenge Dice), and then change the highest die to a 1.
Also a modifier is sometimes applied to the score – this is easily handled with a query, like +({Modifier|0,-1,-2,1,2}). And that’s it, the roll is done.