When a teacher picks a student to answer, a team picks who presents first, or a shop picks a giveaway winner, the people involved care about one thing: did everyone have the same chance? "It's random" is not quite an answer. A picker can be unfair in small, invisible ways, and a draw can be fair and still look rigged. This article explains how a fair picker chooses, works through the classic mistake with real numbers, and ends with a routine for draws that other people have to trust.
The method described is the one our random name picker uses, and the numbers were computed by hand and checked with a script.
Step one: a good source of randomness
Browsers offer two random sources. Math.random() is fast and fine for animations and games, but it was not designed to be unpredictable: its internal state can in principle be worked out from enough outputs. crypto.getRandomValues() draws from the operating system's cryptographic generator, the same one used to make encryption keys. For anything with a prize or a consequence, use the second. Our picker does, and nothing about the draw leaves your browser.
Step two: turning random bits into a name
The secure generator gives you a 32-bit number, anywhere from 0 to 4,294,967,295. You have, say, 30 names. The obvious move is the remainder: x % 30. That is the classic mistake, known as modulo bias.
It is easiest to see with a small source. Suppose you only had one random byte, 0 to 255, and wanted a digit from 0 to 9. 256 divided by 10 is 25 remainder 6. So the values 0 to 249 cover each digit exactly 25 times, and the six leftover values, 250 to 255, map to the digits 0 to 5 once more:
| Digit | Byte values that give it | Probability |
|---|---|---|
| 0 to 5 | 26 each | 26 ÷ 256 ≈ 10.16% |
| 6 to 9 | 25 each | 25 ÷ 256 ≈ 9.77% |
The low digits win about 4% more often than the high ones. In a class raffle with a byte-sized random source, the first six names on the list would have a measurable advantage.
With 32 bits the effect shrinks but does not vanish. 2³² divided by 30 leaves a remainder of 16, so with x % 30 the first 16 names get 143,165,577 chances each and the other 14 get 143,165,576. Nobody will ever notice a difference of one in 143 million, but it is not equal, and the fix costs nothing.
The fix: rejection sampling
Throw away the leftover values and draw again. For 30 names, accept x only if it is below 4,294,967,280 (the largest multiple of 30 that fits), and take x % 30. Every name then has exactly 143,165,576 accepting values. The chance of having to draw again is 16 in 4.3 billion, so in practice it almost never happens. This is what our picker does, and it is the same technique our dice and coin roller uses for its dice.
The common alternative, Math.floor(Math.random() * n), has a much smaller bias of the same kind because Math.random() has more bits, but it still uses the non-cryptographic source.
The wheel is decoration
A spinning wheel feels fairer than a name appearing instantly, which is why pickers have one. In a well-built picker, the wheel does not decide anything. Our picker chooses the winner first, from the secure source, and then calculates where the wheel must stop so that the winner's slice ends under the pointer, a random distance across the slice and after several full turns. The animation is computed from the result, never the other way round.
That matters because a wheel whose physics decides the result is only as fair as its physics: friction, frame rate and how long the button was held can all skew where it stops.
Repeats, removals and several winners
- A name listed twice has twice the chance. If a list is pasted from a sign-up form, someone who signed up twice gets two slices. Turn on "ignore repeated names" unless repeated entries are the rule (one entry per ticket bought, for example).
- Drawing several winners: remove each one. With 30 names and 3 prizes, removing each winner after the spin gives every person exactly a 3 in 30, or 10%, chance of winning something. If you leave winners in, there is about a 9.8% chance that the same person is drawn twice in three spins (1 − 30 × 29 × 28 ÷ 30³), and then you have to decide what a re-draw means.
- The order of the list does not matter when the selection is uniform. Nobody gains by being first or last.
Fair is not the same as looking fair
Truly random results include streaks. A student picked two lessons in a row is not evidence of a broken picker: with 30 students, the chance that the same student is picked in two consecutive lessons is 1 in 30 each time, so over a term it is likely to happen to someone. If the goal is that everyone gets a turn, that is a rotation, not a random draw, and "remove the winner after each spin" turns the picker into one.
For public draws, the process is what people trust:
- Publish the full list of entries before the draw.
- Say the rules in advance: one entry per person, how many winners, what happens if a winner does not respond.
- Draw live or record the screen, with the list visible.
- Keep the list and the result.
Common mistakes
- Using
x % non a small random number. - Using
Math.random()for a draw with a real prize. - Leaving duplicate entries in a list that was meant to be one per person.
- Re-spinning until the result "looks random", which is choosing, not drawing.
- Drawing several winners without removing them, then improvising when the same name comes up twice.
Paste your list into the random name picker, decide on the repeat and removal settings before you spin, and let the wheel do the theatre. The choice itself is already made, fairly, before the wheel starts to turn. For the odds behind dice, coins and advantage rolls, see dice probability for 2d6 and advantage.