How Random Number Generators Actually Work
Every name picker, dice roller and raffle tool rests on the same component: a random number generator. Most of the time you can ignore it. But if you are drawing for something that matters, like a prize, a place on a team, or an order that somebody will feel is unfair, it is worth understanding what the machine is actually doing. “Random” covers at least three different things, and only one of them is what people assume.
Computers cannot produce randomness on their own
A processor is built to be deterministic. Feed it the same instructions and the same inputs and it produces the same output every time. That is the entire point, and it is why software is debuggable at all. Randomness is the opposite property. So a computer has to get it from somewhere else, and there are only two places to look: a formula that produces numbers which merely look unpredictable, or genuine physical noise from outside the processor.
Pseudorandom generators: a formula that looks random
A pseudorandom number generator (PRNG) starts with a number called the seed and applies arithmetic to it repeatedly. Each result becomes the input to the next step, producing a long stream of values with no obvious pattern. It is fast, needs nothing but the CPU, and passes most statistical tests for randomness.
The catch is in the name. The stream is entirely determined by the seed. Start from the same seed and you get the same sequence, every time, forever. That is genuinely useful, since it is how a game replays a level identically and how a scientific simulation is made reproducible, but it means a PRNG has no secrets. Someone who learns the seed knows every number the generator will ever produce. And because the internal state is finite, the sequence eventually repeats.
Math.random() in a browser is a PRNG, typically an algorithm called xorshift128+. It is well-suited to animations, shuffling a playlist and scattering particles on a canvas. It was never designed to be unpredictable to a motivated adversary, and browser vendors are explicit that it should not be relied on for anything security-sensitive.
Cryptographic generators: unpredictable on purpose
A cryptographically secure generator (CSPRNG) is built to a stricter standard. Given every number it has produced so far, it must remain computationally infeasible to work out the next one, or to reconstruct the internal state and run it backwards. It gets there by collecting entropy, meaning genuine unpredictability, from physical sources the operating system can observe: the precise timing of keystrokes and disk interrupts, thermal noise, and on modern processors a dedicated hardware instruction that samples electrical noise directly.
In a browser that generator is crypto.getRandomValues(). On a server it is the operating system’s entropy pool, reached in Node.js through crypto.randomInt() and its relatives. Both are slower than a PRNG, by an amount that is completely irrelevant when you are drawing one winner from a list.
Modulo bias: the mistake almost every naive picker makes
Suppose your generator hands you a whole number from 0 to 9, and you need to pick one of three names. The obvious move is to take the remainder: value % 3. Watch what that does:
- Values 0, 3, 6 and 9 all map to name 0, so four ways to win
- Values 1, 4 and 7 map to name 1, so three ways to win
- Values 2, 5 and 8 map to name 2, so three ways to win
The first name gets a 40% chance while the other two get 30% each. Nothing is wrong with the generator; the bias comes entirely from folding ten possibilities into three unevenly. The first names on the list win more often, permanently, and nobody would ever notice by eye.
The fix is rejection sampling. Work out the largest multiple of 3 that fits inside the range, which is 9, giving values 0 to 8, then throw away any value at or above it and draw again instead. You have discarded exactly one outcome in ten, and what remains divides perfectly into three equal groups. Occasionally you draw twice. In exchange the odds are exactly equal rather than approximately equal.
With a 32-bit generator and a handful of names the real-world bias is far too small to detect in a lifetime of spinning. The reason to correct it anyway is that it costs three lines of code, and “too small to measure” is a much weaker claim to make to somebody who just lost a raffle than “exactly equal”. The free wheel on the home page and the account-backed draw both do this.
Why a fair draw so often looks rigged
This is the part that causes arguments, and no amount of engineering fixes it. The human intuition for what randomness should look like is simply wrong. People expect random results to be evenly spread. Real random results clump.
Spin a wheel of ten names ten times and the chance that all ten names come up exactly once is about one in 3,600. Runs, gaps and repeats are not evidence of a broken draw; they are what a working draw looks like. If a picker never repeated a name and never left one out, that would be the sign something was interfering with it.
A related surprise: with 30 names on a wheel and 10 spins, there is roughly a 55% chance that at least one name comes up twice. Most people put that number far lower, so the second win reads as suspicious when it is more likely than not. If you need each name to come up once, do not argue with probability. Turn on elimination so the winner leaves the wheel, which is what that setting is for.
Where the draw runs matters as much as how
A perfect generator is not much comfort if the result is decided somewhere the audience cannot trust. If the pick happens in the organiser’s own browser, nothing stops them refreshing until a preferred name comes up and screenshotting only that spin. The generator was flawless; the process was not.
This is why the shared draws here are decided on the server and written to a timestamped history before the result is sent to any screen. A viewer reloading their page re-reads a decision that has already been made and recorded. That is a claim about process rather than mathematics, and it is the one that actually settles disputes. There is more on the mechanics in how the live spin works and on the record it leaves in why every draw gets logged.
How to sanity-check a picker before you trust it
- Run it a few hundred times with three names and count. Real bias shows up faster with fewer options. Wildly uneven totals after 300 draws are worth questioning; mild unevenness is expected.
- Check whether reloading changes a completed result. If it does, the draw was never recorded anywhere, so there is nothing to appeal to later.
- Look for a visible record. A tool that shows only the current winner cannot help you when somebody disputes the third draw of five.
- Check that the picture matches the odds. If entries can be weighted, the slices should be drawn to scale. Weighting hidden in a settings panel is weighting nobody in the room can verify. See weighted odds explained.
The short version
Use a cryptographic generator rather than Math.random, because the cost of doing so is nothing. Correct modulo bias for the same reason. Expect fair results to look streaky, and say so before you draw rather than after somebody complains. And decide the draw somewhere it can be recorded, because most arguments about fairness turn out to be arguments about evidence.
Next
- Running a raffle people trust, covering the process around the draw rather than just the draw.
- Wheel, dice, shuffle or coin flip, on picking the right instrument for the job.
- Frequently asked questions.