npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

opendice

v1.4.0

Published

Roll dice in JavaScript or TypeScript. A formula parser and one roll() over a CSPRNG with modulo-bias rejection.

Readme

opendice

NPM

Roll dice in JavaScript or TypeScript. Write 2d6+3, get the total back along with every die that was rolled.

The randomness comes from the platform's cryptographic random number generator, with modulo bias removed. Nothing here adjusts a result, and nothing here decides what one means.

rollful.dev/docs is the reference — every suffix, every result field, every limit, and the HTTP API. This file is the short version.

MIT licence · no dependencies · browser and Node 20 or newer

Install

This is a Node.js module available through the npm registry.

Before installing, download and install Node.js. Node.js 20 or higher is required.

Installation is done using the npm install command:

npm install opendice

pnpm and yarn work the same way. There are no dependencies and no build step: the package ships as ES modules with TypeScript types.

Quick start

import { roll } from 'opendice'

const result = roll('2d6+3')

result.total // 11

roll() takes a formula as text and returns an object. total is the number; the rest describes how it was reached.

result.dice[0].results // [5, 3]  — the two dice that were rolled
result.dice[0].kept //    [5, 3]  — the ones that counted
result.modifier //        3       — the +3
result.formula //         '2d6+3' — what you asked for

Anything a formula cannot mean throws, so text somebody typed is safe to hand straight to it.

The formula language

| You write | It means | | -------------- | -------------------------------------------------------- | | 2d6 | Roll two six-sided dice and add them up. | | 1d20+7 | Roll a d20 and add 7. Use -7 to subtract. | | 1d8+1d4+3 | Mix as many dice and numbers as you like. | | 4d6kh3 | Roll four d6, keep the highest 3. | | 4d6kl3 | Same, but keep the lowest 3. | | 4d6kh | Leave the number out and it keeps 1. | | 2d20adv | Roll two d20 and keep the higher one. ("advantage") | | 2d20dis | Roll two d20 and keep the lower one. ("disadvantage") | | 2d6min3 | Every die counts at least 3. max caps instead. | | 2d6totalmin3 | The dice added up count at least 3. totalmax caps. | | 1d6! | Exploding: a top face rolls again and adds. | | 1d6!p | Penetrating: as !, but each extra roll counts 1 less. | | 1d6x10 | Roll a d6, multiply that group by 10. | | 2d10 fire | A label on the end, if you passed fire in tags. |

Spaces are ignored and capitals are accepted: 2D6 + 3 works. A suffix binds to its own group of dice, never to the whole sum: a 1d6x10+5 that rolled a 3 is 35 and not 55, and 2d6totalmin8+1 is never below 9 rather than never below 8.

Full grammar, including which suffixes combine and which do not, at rollful.dev/docs/reference/grammar.

API

| Function | What it does | | --------------------------- | ------------------------------------------------------ | | roll(formula, options?) | Rolls a formula and returns the result | | parseFormula(text, opts?) | Reads a formula without rolling it, for checking input | | rollDie(sides, source?) | Rolls one die and returns a number | | cryptoRandom() | The raw random number the dice are built on | | keptFlags(group) | Which dice counted, aligned to the roll order | | soleDieGroup(result) | The dice in a result, if it used only one kind |

Every signature, option and result field at rollful.dev/docs/reference/package.

Documentation

| Page | What it covers | | -------------------------------------------------------------------------------------- | ----------------------------------------------- | | Roll dice in JavaScript | Start here | | The formula grammar | Every suffix, and which ones combine | | Result fields | Everything a roll reports, and what it does not | | Limits | What a formula may ask for before it is refused | | Errors | What throws, and what each message says | | The package | Exports, types, module format | | Versioning | What a major version means here | | Validate a formula someone typed | Checking input before you roll it | | Show the working in a UI | Dimming the dice that were dropped | | Survive the limits | Handling a formula the library refuses | | Why every die comes back | Why a result is a record, not just a number | | What Rollful does not know | What the randomness can and cannot promise | | Roll dice over HTTP | The same dice without installing anything |

The randomness

All of this applies to roll() by default. There is nothing to switch on.

  • crypto.getRandomValues, not Math.random. The JavaScript standard permits Math.random to be low quality, and implementations differ.
  • Modulo bias is removed, not ignored. Reducing a 32-bit number to a d6 naively makes some faces slightly likelier. Draws that would cause that are discarded and redrawn, so every face is exactly as likely as every other.
  • One draw per die. Several dice are never derived from one number.
  • No result is ever adjusted. There is no "you have rolled badly, here is a good one" logic, and there will not be. Dice come up 1 three times in a row sometimes; so do these. Smoothing that out would make any record of the rolls inaccurate.
  • The options you pass are read as you passed them, so other code sharing the page cannot inject its own randomness, a label, or an extra die. The exception is rand, which replaces the randomness outright and is yours to guard.

The one thing it cannot promise, and why, at rollful.dev/docs/explanation/fairness.

What this library does not do

It has no rules of its own. It rolls dice and reports what happened. Whether a high roll is good, what a label stands for, whether a total passes or fails — all of that is the caller's.

Where it came from

Extracted from OpenFray, which routes every roll through one function so randomness has a single place to live and be checked. Everything that knew about OpenFray's subject matter was left behind. OpenFray is AGPL-3.0; these dice are MIT.

Contributing

Any constructive contribution is welcome — a bug fix, a new way of rolling, a fix to the documentation, a test, a typo.

Everything you need is in CONTRIBUTING.md: setup, the commands, the code style, and what "done" means. AGENTS.md covers the decisions that are easy to undo by accident.

Licence

MIT © Nicola Mustone — see LICENSE.