[ // dice engine ]
Roll dice from anything that speaks HTTP.
A free, public dice API and three embeddable widgets. Full dice notation, cryptographic randomness, no keys, no sign-up, no tracking.
curl "https://pixeltable.net/api/roll?dice=2d6+3"none
api keys
CORS *
any origin
crypto
randomness
60/min
rate limit
live widget — tap to roll
[ // quickstart ]
Three ways in
Every path hits the same engine. Pick whichever fits where the dice need to land.
Call the API
One GET request. JSON back with every individual die.
curl "https://pixeltable.net/api/roll?dice=2d6+3"Embed a widget
Drop an iframe anywhere HTML runs. No script tag needed.
<iframe src="https://pixeltable.net/embed/dial?dice=d20" width="320" height="404" style="border:0" title="Dice dial"></iframe>Inline in prose
A custom element that rolls where the sentence needs it.
<script src="https://pixeltable.net/embed/tag.js" defer></script>
<p>The goblin lunges <pixel-dice dice="d20+4"></pixel-dice> against your AC.</p>[ // notation ]
Dice notation
Standard tabletop notation. Type anything below to check it — the parser runs in your browser, so there is no request to wait for.
roll 4 × d20, keep best
Checked in your browser by the same parser the API runs.
- d20
- One twenty-sided die.
- 2d6+3
- Two d6, then add 3.
- 4d20kh1
- Four d20, keep the highest — advantage.
- 2d20kl1
- Two d20, keep the lowest — disadvantage.
- 4d6kh3
- Four d6 keeping the best three — ability scores.
- d20+2d4+1
- Chain any number of dice terms and modifiers.
- d%
- Percentile, identical to d100.
up to 100 dice per roll · 2–1000 sides · kh/kl keep highest or lowest
[ // reference ]
GET /api/roll
Rolls an expression and returns every die it rolled, not just the total. Runs on the edge; no authentication of any kind.
curl "https://pixeltable.net/api/roll?dice=2d6+3"roll 2 × d6, add 3
Query parameters
- dice
- string
- Expression to roll. Write + literally; %2B works too.default 1d20
Response — 200
- expression
- string
- The expression exactly as you sent it.
- total
- number
- Sum of every kept die plus all modifiers.
- modifier
- number
- All flat modifiers combined; negative when they net out below zero.
- rolls[]
- object[]
- One entry per dice term, in the order written.
- rolls[].die
- string
- Die type, such as "d6".
- rolls[].count
- number
- How many dice this term rolled.
- rolls[].results
- number[]
- Every die rolled, including ones later dropped.
- rolls[].kept
- number[]
- The dice that counted, after kh/kl.same as results when no keep is used
- rolls[].keptIndices
- number[]
- Zero-based positions in results for the dice that counted.unambiguous when kept dice tie
- rolls[].subtotal
- number
- Sum of kept for this term.
- timestamp
- string
- ISO-8601 time the roll was made.
Errors — 400 and 429
Failures return { "error": "…" } with a message written for humans. Nothing is ever silently substituted — a bad expression never rolls something else instead.
- Empty dice expression.
- 400
- The dice param was present but blank.
- Unrecognized dice expression: "…".
- 400
- Nothing in the string parsed as dice.
- Dice sides must be between 2 and 1000.
- 400
- d1 and d1001 are both out of range.
- Roll at least 1 die per term.
- 400
- 0d6 has no dice to roll.
- At most 100 dice per roll.
- 400
- Counted across every term, so 60d6+41d6 fails.
- Keep count must be between 1 and N.
- 400
- 2d6kh3 keeps more dice than it rolls.
- Subtracting dice is not supported…
- 400
- d20-2d4 — only modifiers may be negative.
- Rate limit exceeded — slow your roll.
- 429
- Returned as 429 with a Retry-After header.
Rate limit
60 requests per minute per IP, with a burst of 20. Exceeding it returns 429 and a Retry-After header in seconds.
CORS
Access-Control-Allow-Origin: * on every response, errors included. Call it straight from the browser.
[ // reference ]
GET /badge/[expr].svg
An SVG rolled by the server on every request. Zero JavaScript, so it works in a GitHub README, a forum post, anywhere an image loads.
Reload the page — it rolls again. The response is sent no-store so caches never freeze a result.
<img src="https://pixeltable.net/badge/2d6+3.svg" alt="dice roll">The .svg suffix is optional. A bad expression still returns 200 with an error badge, so a broken image never appears in someone's README.
[ // widgets ]
Embeddable widgets
Configure one below and copy the snippet it writes. Every widget rolls through the same public API, so what you see here is what your readers get.
The Dial
320 × 404 · one die type
A spin-to-land roller for a single die type. The ring renumbers to match the expression and every kept die lights its numeral. Best as a centrepiece.
<iframe src="https://pixeltable.net/embed/dial?dice=d20" width="320" height="404" style="border:0" title="Dice dial"></iframe>The Module
372 × 220 · full notation
Takes any expression the API accepts, including mixed dice and keeps. Shows each die separately and strikes through the ones dropped by kh/kl.
<iframe src="https://pixeltable.net/embed/module?dice=4d20kh1" width="372" height="220" style="border:0" title="Dice roller"></iframe>The Tag
inline · custom element
Sits on the text baseline inside a sentence and rerolls on click. Sizes itself to the surrounding type, so it belongs in prose rather than beside it.
The goblin lunges
<script src="https://pixeltable.net/embed/tag.js" defer></script>
<p>The goblin lunges <pixel-dice dice="d20+4"></pixel-dice> against your AC.</p>[ // fair use ]
Limits
Generous enough for a campaign, bounded enough to stay free for everyone.
100
dice per roll
2–1000
sides per die
60/min
requests per IP
20
burst allowance
No keys, no accounts, no analytics on the endpoint. If you need more headroom than this, get in touch and we will work something out.
