PixelTable

[ // 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.

try it now
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.

bash
curl "https://pixeltable.net/api/roll?dice=2d6+3"

Embed a widget

Drop an iframe anywhere HTML runs. No script tag needed.

html
<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.

html
<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.

notation checkervalid

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
curl "https://pixeltable.net/api/roll?dice=2d6+3"
live request
GET /api/roll?dice=

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.

Live 2d6+3 dice badge, rerolled on each load

Reload the page — it rolls again. The response is sent no-store so caches never freeze a result.

markdown
![dice roll](https://pixeltable.net/badge/2d6+3.svg)
html
<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.

theme
accent
copy this
<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.

theme
accent
copy this
<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.

theme
accent
size

The goblin lunges against your AC.

copy this
<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.