Random

Behind the button

How this random number generator works

You tap, a number appears. Here is everything that happens in between, in plain words — plus a test you can run on us any time you like.

Skip to the test

By Stuck at Home, LLC · · Checked against the code and two specifications

Step one: ask the device for noise.

When you tap, the page calls a browser function named crypto.getRandomValues(). It is part of the Web Cryptography API, the same toolkit browsers use to protect passwords and payments. The W3C specification describes it as a cryptographically strong pseudo-random number generator seeded with truly random values, and asks browser makers to feed it from a high-quality source such as the operating system’s own entropy pool.

That pool is built from things no one can predict in advance: tiny timing differences in hardware, and on many devices a dedicated random-number circuit. Your browser stirs it into a stream of bits and hands us 64 of them per request.

Isn’t that “pseudo”-random, then?

Yes, and that is normal. MDN puts it plainly: to be fast enough, implementations use a pseudo-random generator seeded with enough entropy, and the algorithm is “suitable for cryptographic purposes”. The seed is the unpredictable part; the generator stretches it securely. For picking a raffle winner or a dice roll, this is far more than you need. The same page notes it is the only part of the Crypto interface that works on an ordinary (non-HTTPS) page, though we serve everything over HTTPS anyway.

Step two: fit the noise to your range — without bending it.

Sixty-four random bits give a number from 0 to about 18 quintillion. You asked for, say, 1 to 6. The tempting shortcut is to divide and keep the remainder. The trouble is that 18 quintillion is not a multiple of 6, so the remainders are not quite evenly shared: a few results would come up a hair more often than the others. That is called modulo bias.

We use the honest fix, rejection sampling. Work out the largest multiple of 6 that fits, and if the 64-bit number lands in the leftover sliver at the top, throw it away and ask for a fresh one. What survives is perfectly even. The sliver is so thin that a second try is almost never needed.

⚀ ⚁ ⚂ ⚃KeepAny result inside the even part
⚄ ⚅Roll againThe leftover that would tilt the odds
The same idea with a six-sided die and a range of 1–4: faces 1–4 count, 5 and 6 are rerolled. Every kept result has exactly the same chance.
Show me the actual rule

With a range of n numbers and a 64-bit draw v: let cutoff = 264 − (264 mod n). If v ≥ cutoff, draw again. Otherwise the result is low + (v mod n). The arithmetic runs on big integers, so even a range spanning both ends of JavaScript’s safe integers (±9,007,199,254,740,991) is handled exactly. You can read the twelve lines yourself in number-model.mjs.

What we don’t do.

  • No server. The number is made on your device. Nothing you type and nothing you draw is sent anywhere. (Optional, consent-based analytics record only that a tool was used, never the result.)
  • No memory. Each tap is a fresh draw. The generator does not know what came before and does not try to “balance” results — which is why repeats happen, as this story explains.
  • No Math.random() for the main picker. JavaScript’s everyday random function is fine for games, but MDN is explicit that it does not provide cryptographically secure numbers. The number generator, the dice, the coin and the lottery picker all use the Web Crypto path described above. A few of our list tools — the letter picker, name picker, list shuffler, team picker and raffle picker — still use Math.random() for their choice. For a classroom draw the difference is invisible; we say so on those pages, and this page will be updated when they move over.
  • No passwords. This is a tool for choosing numbers, not for making secrets. For passwords use a password manager; for keys, use software built for that job.
Don’t take our word for it

Test it yourself.

We’ll draw thousands of numbers right now, in your browser, and run the same first check statisticians use on any generator: are the counts as even as chance allows?

Press the button. Each bar is one number’s count; the dashed line is the even share.

Nothing has been drawn yet.

This is Pearson’s chi-square goodness-of-fit test. The p-value is the chance that a perfectly fair source would produce a spread at least this uneven. Small p-values are not proof of a problem — a fair source produces p < 0.05 one run in twenty. The draws stay on your device.

What that test can and can’t tell you.

An even count is necessary but not sufficient. A “generator” that simply counted 1, 2, 3, 4, 5, 6, 1, 2, 3… would pass this test perfectly and be useless. Serious evaluations, such as the battery described in NIST Special Publication 800-22, run many different tests: runs and streaks, patterns in blocks, how often templates appear, and more. The system source behind getRandomValues() is designed and reviewed against that kind of scrutiny; our contribution is simply not to spoil it on the way to your screen.

If you ever see the test fail repeatedly — not once, which is expected, but run after run — we would like to know. Here is how to reach us.

Three honest limits.

  1. We can’t audit your device. If an operating system’s random source were broken, every app on it would share the problem, and we would inherit it.
  2. We don’t certify draws. For a prize draw that must be provably fair to outsiders, keep a record and draw in front of witnesses; our raffle guide explains a sensible procedure.
  3. We don’t generate secrets. See “No passwords”, above. We mean it.
Where we looked things up