Show the working in a UI
A roll that prints only its total asks to be trusted. This guide renders one that shows every die it threw, and marks the ones that did not count.
The examples use the package, and every one of them works the same on a roll that came back
from the API — the result is the same shape either way. Get yours from
roll() or from a request, then pick up here.
The one difference is keptFlags. Over HTTP it arrives as a field on the group; in the
package it is a function you call, because the package does not build the list unless you ask
for it.
Get the flags
Section titled “Get the flags”Each entry in dice is one group — one run of dice with the same number of sides. Two things
matter here: results, every die in the order it was thrown, and a list of flags aligned to
it one for one.
import { keptFlags, roll } from 'opendice'
const result = roll('4d6kh3')const group = result.dice[0]
group.results // [5, 4, 5, 3]keptFlags(group) // [true, true, true, false]On a result from the API that last line is group.keptFlags instead.
Render a group
Section titled “Render a group”Walk results and read the flag at the same index. Never filter — the position is the point.
function renderGroup(results, flags) { return results.map((die, i) => ({ die, kept: flags[i] }))}{ renderGroup(group.results, keptFlags(group)).map(({ die, kept }, i) => ( <span key={i} className={kept ? 'die' : 'die die--dropped'}> {die} </span> ))}Style die--dropped however you like — dimmed, struck through, half opacity. What matters is
that it stays where it was rolled.
Count entries, not dice
Section titled “Count entries, not dice”A formula that sets a floor or a ceiling puts that bound in results beside the dice, so
1d6min3 comes back with two entries for one die. The flags still line up one for one and
what they mark still adds up to total, so the loop above renders it correctly without
changing.
What breaks is anything that reads results.length as a number of dice, or labels the
entries “die 1” and “die 2”. Read the count from the formula, or leave it off — an exploding
die rolls more times than it was asked for anyway.
Handle several groups and a modifier
Section titled “Handle several groups and a modifier”1d8+1d4+3 comes back as two groups and a modifier. Render each group in turn, then the flat
numbers.
const parts = result.dice.map((group) => renderGroup(group.results, keptFlags(group)))
result.modifiers // [3] — each flat number, in the order writtenresult.modifier // 3 — their sumresult.total // 12Use modifiers rather than modifier when you are showing the working. A roll written
+1 -6 has a modifier of -5, which is true and says nothing about where it came from.
A group can also subtract. Check sign, which is 1 or -1, before you print a + in
front of its total.
Mark a critical, if your game has them
Section titled “Mark a critical, if your game has them”naturalHigh and naturalLow are set only when a group kept exactly one die and that die
showed its highest face, or a 1. A group that kept a bound instead of a die sets neither, so
1d20min20 never claims a natural 20.
group.naturalHigh // true on a kept, single d20 showing 20They are deliberately not set when several dice were kept. A top face among four dice is not a critical on its own, and deciding otherwise is your game’s business rather than this API’s — see What Rollful does not know.
Where to go next
Section titled “Where to go next”- Result fields — every field, not just the ones used here.
- Why every die comes back — the reasoning behind the shape you have just rendered.