How Random Number Generators Work
Picking a random number sounds like the simplest thing a computer could do. It is not. Computers are deterministic machines — they follow instructions exactly, every time — so producing genuine unpredictability is fundamentally at odds with what they are. The workarounds are clever, mostly good enough, and they fail in specific ways that have cost people real money.
The paradox
A computer given the same input always produces the same output. That is the entire point of a computer. So where does randomness come from?
There are two answers, and the difference between them matters enormously.
Pseudo-random: predictable by design
A pseudo-random number generator (PRNG) starts with a value called the seed and runs it through a mathematical formula that scrambles it thoroughly. The output looks random — it passes statistical tests for randomness — but it is completely determined by the seed.
Give it the same seed and you get exactly the same “random” sequence. Every time.
This is not a flaw. It is often precisely what you want: reproducible simulations, game worlds you can regenerate from a seed, tests that fail the same way twice. JavaScript's Math.random() works like this.
But it means the sequence is predictable to anyone who can work out the state. And that is not theoretical — given enough consecutive outputs from a typical PRNG, its internal state can be reconstructed, after which every future “random” number is known in advance.
Never use Math.random() for anything that matters: passwords, tokens, session IDs, shuffles in a game with money on the line, or a raffle where someone would benefit from winning.
Cryptographic randomness: genuinely unpredictable
A cryptographically secure generator draws from the operating system's entropy pool — a reservoir of genuine physical unpredictability gathered from the real world:
- The precise microsecond timings of your keystrokes and mouse movements.
- Timing jitter in disk and network activity.
- Electrical noise in the hardware itself.
- On modern CPUs, a dedicated hardware random number generator sampling thermal noise.
These sources are physically chaotic, not merely complicated. There is no formula to reverse. In the browser this is exposed as crypto.getRandomValues(), and it is what you should use for anything with consequences.
Modulo bias: the bug hiding in plain sight
Here is a subtle failure that appears in an enormous amount of otherwise-careful code.
You have a random number from 0 to 255 and you want one from 1 to 10. The obvious move:
(random % 10) + 1
This is biased, and here is why. 256 does not divide evenly by 10 — it gives 25 remainder 6. So when you wrap the 256 possible values around a range of 10, the first six results (1–6) get 26 chances each, while the remaining four (7–10) get only 25.
Numbers 1 through 6 are about 4% more likely than 7 through 10. The generator was perfectly fair; the way it was squeezed into the range was not.
Four percent sounds trivial. In a raffle with a valuable prize, in a lottery, or in a game where money changes hands, a 4% systematic edge is enormous — and it is the kind of thing that gets found and exploited.
The fix is rejection sampling: discard the small number of values that would cause the uneven wrap, and draw again. In the example, reject anything above 249, leaving exactly 250 values that divide perfectly by 10. It costs a negligible number of extra draws and makes every outcome exactly equally likely.
When RNG bugs get expensive
These are not hypothetical concerns.
Online poker sites have been broken by predictable shuffles — in one well-known case, the number of possible shuffles was limited by a 32-bit seed, and an attacker who observed a few cards could reconstruct the deck and know every player's hand in real time.
Cryptocurrency wallets have been drained because their private keys were generated with weak randomness, making the keys guessable. Session tokens generated from the current time have been predicted, letting attackers hijack accounts.
The pattern is always the same: the randomness looked random, passed casual inspection, and was predictable to anyone who looked properly.
With duplicates, or without
A practical distinction when you are actually drawing numbers:
- With duplicates — each draw is independent, like rolling a die repeatedly. The same number can and will come up again. Correct for dice, simulations and sampling.
- Without duplicates — like drawing names from a hat. Once used, a number is gone. Correct for raffles, lottery-style picks and assigning people to teams.
An obvious but easily-missed constraint: you can never draw more unique values than the range contains. Six unique numbers from 1–5 is impossible, and a tool that appears to do it is lying to you.
A common intuition error worth naming: in a genuinely random sequence with duplicates allowed, repeats are expected. People see 7 come up twice in a row and conclude the generator is broken. It is not — truly random data is streakier than human intuition expects. A generator with no repeats at all would in fact be the suspicious one.
Choosing a good range
- Check the endpoints. Is the maximum included? An off-by-one here means the last raffle ticket can never win — a bug nobody notices until someone checks.
- Match the range to reality. If tickets run 1–500, draw from 1–500, not 0–500.
- For raffles, draw unique numbers, one per winner.
Frequently asked questions
Is Math.random() good enough? For animations and non-critical shuffles, yes. For anything involving security, money or fairness, no — use a cryptographic generator.
Can a computer produce true randomness? Not from software alone. It must draw on physical entropy — hardware noise, timing jitter — which is exactly what the OS entropy pool provides.
Why did the same number come up twice? Because that is what randomness looks like. If duplicates are allowed, repeats are expected.
Is an online random picker fair? Only if it uses a secure source and avoids modulo bias. Most do neither and simply hope nobody checks.
Generate fair random numbers now
Our Random Number Generator uses the Web Crypto API with rejection sampling — so every value in your range is exactly equally likely, with no modulo bias — and offers a unique-only mode for raffles. It runs entirely in your browser. For random strings rather than numbers, see the Password Generator, and for unique identifiers, the UUID Generator.