How random are online random number generators?
Good enough for a raffle, not good enough for a password. Here is the actual difference.
Most online generators call Math.random(), a pseudo-random generator: a deterministic formula that produces well-distributed numbers from a hidden seed. It is fine for a raffle and useless for anything an adversary can predict. Browsers also offer crypto.getRandomValues(), which draws from the operating system entropy pool and is unpredictable in practice. The second one costs nothing extra to use.
Pseudo-random versus cryptographic
Math.random() in modern V8 uses xorshift128+. It has a period of about 2^128 and passes standard statistical tests, so the numbers look random by every distributional measure. But the internal state is 128 bits, and researchers demonstrated years ago that observing a modest run of outputs is enough to recover that state and predict everything that follows. The specification explicitly says it must not be used for security.
crypto.getRandomValues() is fed by the operating system CSPRNG, seeded from hardware noise: interrupt timings, RDRAND, device entropy. Outputs cannot be used to reconstruct the state.
| Math.random() | crypto.getRandomValues() | |
|---|---|---|
| Deterministic from a seed | yes | no |
| Predictable from outputs | yes, with enough samples | no |
| Speed | faster | fast enough for anything interactive |
| Fit for passwords, tokens, keys | no | yes |
| Fit for a giveaway or a dice roll | yes | yes |
Our random number generator and name picker use the cryptographic source, because the only reason not to is that it takes one more line of code.
Modulo bias: the bug almost every generator has
Suppose you want a number from 1 to 10 and your source gives you a byte, 0 to 255. The obvious code is (byte % 10) + 1. There are 256 possible bytes and 256 is not divisible by 10: values 0 to 5 occur 26 times each in the range, values 6 to 9 occur 25 times. So the numbers 1 to 6 come up about 4 percent more often than 7 to 10.
Four percent is invisible in ten spins and obvious in ten thousand. For a wheel of 6 names it is not a rounding error, it is a systematic advantage to whoever sits early in the list.
The fix is rejection sampling: compute the largest multiple of your range that fits in the source range, and throw away any value above it, drawing again. For 10 out of 256, discard anything at 250 or above — that is a 2.3 percent chance of one extra draw, and the result is exactly uniform. It costs microseconds.
You can check any picker for this yourself. Run it a few thousand times over a list of 6 and count the results. If the first entries win noticeably more, it is doing modulo. Paste the run into a line sorter and count the groups, or drop it into a spreadsheet with COUNTIF.
Shuffling is where the other bugs live
The correct shuffle is Fisher-Yates: walk the list from the end, and for each position i swap with a random index between 0 and i inclusive. Every one of the n! orderings is equally likely.
The common wrong version is array.sort(() => Math.random() - 0.5). It looks clever and is badly biased: sort algorithms assume a consistent comparator, and this one is not, so results depend on the engine sort implementation and array length. Measured on small arrays, some orderings come up several times more often than others. It also produces different bias in different browsers, which is why the same shuffle page behaves differently on two machines.
A second subtlety: shuffling with a 32-bit seed cannot produce more than 2^32 distinct orderings. A deck of 52 cards has about 8×10^67 arrangements. Most of them are unreachable by a seeded shuffle — irrelevant for choosing who buys coffee, decisive for an online card game.
What usually goes wrong
- Re-rolling until you like the answer. No generator can defend against this, and it is by far the most common way a draw becomes unfair. Screen-record it, or commit to the first result publicly.
- Duplicate entries in the list. A name that appears three times has three times the chance. Run the list through a duplicate remover before drawing, unless multiple entries are the rule you announced.
- Blank lines and stray whitespace. Empty lines become entries that can win. Clean the list first.
- Believing an animated wheel is the draw. Nearly every wheel picks the winner first and animates toward it. The spin is theatre; the fairness is entirely in the code behind it.
- Using a generator for passwords. If the page does not say it uses the cryptographic source, assume it does not. Use your password manager.
- Confusing random with representative. Ten random names from a 500-person list will not mirror the gender or department split. If you need that, stratify first: split the list, draw from each part.
- Sorting instead of shuffling. Sorting by a random column in a spreadsheet is fine; sorting with a random comparator in code is not.
When you need true randomness, and when you do not
Pseudo-random is enough for classroom cold calling, team splits, dice, deciding where to eat, sample selection in casual analysis, and giveaways where nobody has an incentive to attack you.
You need cryptographic randomness for passwords, API tokens, session identifiers, password reset links, coupon codes with real value, and anything a motivated person could profit from predicting.
You need something else entirely for regulated lotteries and licensed gambling: hardware entropy sources, published seeds, third-party audits and, increasingly, provably fair schemes where the operator commits to a hashed seed before the draw and reveals it afterwards so anyone can verify. A browser tool cannot do that, and no browser tool should claim to.
For a public draw the honest procedure is boring and effective: publish the entrant list before drawing, record the screen from list to result in one take, and publish both. That defends against the failure mode that matters, which is not the algorithm.
Related tools
Frequently asked questions
No. It is a deterministic algorithm producing statistically well-distributed numbers, and its internal state can be recovered from enough outputs. The specification says not to use it for security.
A browser API that draws bytes from the operating system cryptographic random source, seeded from hardware noise. It is unpredictable and suitable for tokens and passwords.
Using a remainder to squeeze a wide random range into a smaller one makes the first values slightly more likely, because the ranges do not divide evenly. Rejection sampling removes it.
Reputable ones are not, but the animation is decoration: the winner is chosen before the wheel moves. Fairness lives in the code, not the spin.
Only if it states that it uses the cryptographic source. A password manager is a better answer in every case.
Fisher-Yates, walking from the end and swapping with a random earlier index. Sorting with a random comparator is measurably biased and browser dependent.