Skip to content

Roll a set of ability scores

4d6kh3 — throw four six-sided dice, keep the highest three — is the most common way to roll a character’s ability scores, and a set is six of them. This guide rolls the set in one request and shows the working for each.

Rollful knows the notation and nothing about the game. What a score means, how many you need and what you do with them are yours; this guide only rolls the dice.

import { roll } from 'opendice'
const score = roll('4d6kh3')
score.total // 14

Over HTTP, send them as a batch rather than six separate calls. One round trip instead of six, and the whole set is counted against a single dice budget — 24 dice, well inside it.

Terminal window
curl -X POST https://api.rollful.dev/v1/roll/batch \
-H 'content-type: application/json' \
-d '{"rolls":[
{"formula":"4d6kh3"},{"formula":"4d6kh3"},{"formula":"4d6kh3"},
{"formula":"4d6kh3"},{"formula":"4d6kh3"},{"formula":"4d6kh3"}
]}'

The response is { "rolls": [...] } with one result per roll, in the order you sent them. There is no identifier to match on, so keep your own order.

const response = await fetch('https://api.rollful.dev/v1/roll/batch', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ rolls: Array.from({ length: 6 }, () => ({ formula: '4d6kh3' })) }),
})
const { rolls } = await response.json()
rolls.map((score) => score.total) // [14, 12, 16, 9, 13, 11]

In the package there is no batch to make — call roll() six times.

const scores = Array.from({ length: 6 }, () => roll('4d6kh3'))

Each result carries the four dice it threw, so a set can be printed with its working intact.

const line = (score) => {
const group = score.dice[0]
const shown = group.results.map((die, i) => (group.keptFlags[i] ? `${die}` : `(${die})`))
return `${shown.join(' ')} = ${score.total}`
}
rolls.map(line)
// [ '5 4 5 (3) = 14', '6 (1) 3 3 = 12', ... ]

In the package, keptFlags is a function rather than a field — keptFlags(group). See Show the working in a UI for the rendering in full.

The formula is the only thing that changes if your table rolls differently.

Formula What it does
4d6kh3 four dice, keep the highest three
4d6kl3 four dice, keep the lowest three
3d6 three dice, straight
2d6+6 three to twelve, plus six
5d6kh3 five dice, keep the highest three

Rerolling ones, dropping the lowest score of the set, or a floor on the total are rules rather than notation. Do them in your own code, on the results the API returns — every die is there to decide with.