How random is a random number generator?
Updated 2026-08-27 ยท about 8 minute read
Ask a computer for a random number and you will get one. Whether it is random depends on which generator answered, and the difference is invisible until it matters โ at which point it can matter enormously.
Most random numbers are not random
A computer is a deterministic machine. Given the same input it produces the same output, every time, which is precisely the property that makes it useful and precisely the property that makes randomness hard.
So most "random" numbers are the output of an algorithm that produces a sequence
statistically resembling randomness while being entirely predictable if you know where it
started. These are pseudorandom number generators, and JavaScript's
Math.random() is one.
True randomness has to come from outside the algorithm โ physical processes like thermal noise, radioactive decay, or the timing jitter of interrupts. Modern processors have hardware instructions that sample such noise, and operating systems mix these sources into an entropy pool.
Pseudorandom generators and seeds
A PRNG starts from a seed and applies a formula repeatedly. Same seed, same sequence โ always.
This determinism is often a feature. Games use seeded generators so a world can be regenerated from a short code. Simulations use them so results are reproducible. Tests use them so a failure can be replayed.
The problem is that PRNGs are predictable by design. Observe enough output from a simple generator and you can determine its internal state, and from there predict every number it will ever produce. This has cost real money: online poker sites have been broken this way, with attackers reconstructing the shuffle from a handful of visible cards and then knowing every player's hand.
Math.random() makes no security promises whatsoever. The specification
explicitly declines to require any. It is fine for a visual effect, a random tip, a
demonstration โ and unfit for anything an adversary would benefit from predicting.
Cryptographic randomness
A cryptographically secure generator adds one guarantee: even given every previous output, an attacker cannot predict the next one better than by chance, and cannot work backwards to earlier outputs.
In the browser this is crypto.getRandomValues(), which draws from the
operating system's entropy pool. Every tool on this site that needs randomness uses it โ the
random number generator, the password
generator, the dice roller, the coin flip, the
random picker and the UUID generator.
Using the cryptographic generator even for a dice roll is not overkill; it costs nothing and removes an entire class of question about whether the result is fair.
Modulo bias: the invisible skew
Here is the subtle bug that appears in an enormous amount of production code, and it survives because the output still looks random.
Suppose you want a number from 1 to 6 and your generator gives you a byte, 0 to 255. The
obvious approach is (byte % 6) + 1.
But 256 does not divide by 6. It gives 42 complete cycles of six, using values 0 to 251, and then four left over: 252, 253, 254, 255 โ which map to 1, 2, 3, 4. So those four faces come up 43 times per 256 rolls while 5 and 6 come up 42 times. The die is loaded by about 2% in favour of the low numbers.
With a die that is a curiosity. Applied to password generation across a large character set, it systematically reduces entropy and skews which characters appear. Applied to a shuffle, it biases the deck.
The fix is rejection sampling: discard values that fall in the incomplete final cycle and draw again. In the example, throw away 252 to 255 and re-roll. It wastes a tiny fraction of draws and produces a genuinely uniform result. Every random tool on this site does this, which is why a run of twenty words from the random word generator is drawn uniformly rather than skewed toward the start of the list.
Shuffling is harder than it looks
The intuitive shuffle โ walk the list and swap each item with a random other item โ is subtly wrong. It produces nn equally likely outcomes, which does not divide evenly into the n! possible orderings, so some arrangements are more likely than others.
The correct algorithm is the FisherโYates shuffle: walk from the end, swapping each item with a random item at or before its position. That single constraint makes every permutation equally likely.
A famous case: in 2004 an online casino's shuffle used a 32-bit seed, giving about four billion possible shuffles against the 52! โ roughly 8 ร 1067 โ orderings of a real deck. The overwhelming majority of possible shuffles could never occur, and the ones that could were enumerable.
The random picker and team generator use FisherโYates with cryptographic randomness, which between them removes both failure modes.
When the difference actually matters
Being honest about this: for most everyday uses it does not.
It does not matter for picking a restaurant, choosing who goes first, splitting a class into teams, or rolling dice for a board game. Nobody is mounting a cryptographic attack on your family quiz, and a 2% bias in a coin flip will never be noticed.
It matters a great deal for anything security-related โ passwords, keys, tokens, session identifiers, password reset links โ and for anything with money or fairness at stake, such as prize draws, lotteries and gambling. It also matters for scientific simulation, where a poor generator can produce correlations that quietly invalidate results.
The practical rule: use a cryptographic generator by default. It is not slower in any way you would notice, and it means you never have to work out which category you are in. If a random value protects something, check the result with the password strength checker, and see how long a password should be for what that number means.
Pikkit has the full set of random tools, and Random & Fun collects them together.