How random number generators actually work
Most random number code is subtly biased. Here is where, and why it rarely matters until it does.
Pseudo-random versus cryptographically secure
A PRNG is a deterministic algorithm with internal state. Given the same seed it produces the same sequence — which is a feature for simulations and a fatal flaw for passwords. Math.random() is one: seeded once per page, predictable if you observe enough output.
A CSPRNG draws from the operating system's entropy pool and is designed so that observing past output does not let you predict future output. Browsers expose it as crypto.getRandomValues().
The modulo bias problem
The common shortcut is `randomUint32() % n`. It looks fine, but 2^32 is not divisible by most values of n. If n is 3, the values 0, 1 and 2 get 1,431,655,765, 1,431,655,765 and 1,431,655,766 opportunities respectively — a tiny bias towards 2 that never disappears no matter how many samples you take.
The fix is rejection sampling: compute the largest multiple of n that fits in 2^32, discard any draw above it, and only then take the remainder. It costs a rare re-draw and removes the bias entirely.
Floating point makes it worse
`Math.floor(Math.random() * n)` has the same class of problem for large n, plus the fact that a double has 53 bits of mantissa — above 2^53 some integers cannot be represented at all, so a range wider than that silently loses values.
For decimals, the reliable approach is to scale to integers, draw, and divide back. Working in floating point throughout introduces rounding that can push a result just outside the requested range.
Why uniform matters for a raffle
A 0.00000002% bias is irrelevant for a dice roll and still irrelevant for a raffle. But the same biased function used to generate session tokens or encryption keys is a different matter: bias is exactly the kind of structure an attacker can exploit to reduce a search space.
The practical rule: use Math.random() for cosmetic effects, and a CSPRNG with rejection sampling for anything a person could benefit from predicting.
Frequently asked questions
Is Math.random() good enough for a random picker?
For picking a restaurant, yes. For a draw with a prize, use a cryptographic source so the result cannot be predicted or reproduced by anyone watching the page.
What is modulo bias in one sentence?
When a random integer range does not divide evenly into the generator's output range, some results become slightly more likely than others.
Can a browser generate true randomness?
No. True randomness needs a physical entropy source. The OS collects entropy from hardware events and seeds the CSPRNG, which is the practical standard for security.