Random Number Generator
Cryptographically secure random values with rejection sampling to eliminate modulo bias — plus list picking, Fisher-Yates shuffling and team splitting.
Photo by Riho Kroll on Unsplash
Random number generator
Pick from a list
Key takeaways
- crypto.getRandomValues() draws from the operating system CSPRNG; Math.random() is predictable and must never be used for secrets.
- Naive modulo arithmetic skews small ranges — rejection sampling is what makes the output genuinely uniform.
- Sorting with a random comparator does not shuffle; Fisher-Yates is the correct algorithm.
True randomness versus pseudorandomness
A pseudorandom generator is a deterministic algorithm. Given the same starting seed it produces the same sequence forever. That is a feature for simulations and reproducible tests, and a fatal flaw for anything secret.
A cryptographically secure generator is seeded from physical entropy the operating system collects — interrupt timings, hardware noise, on-chip instructions such as RDRAND — and is designed so that observing past output tells you nothing about future output.
This tool uses crypto.getRandomValues(), which is the browser’s interface to that CSPRNG. Math.random() is not, and its output has been reverse-engineered from a handful of observed values in every major engine.
Never use Math.random() for passwords, session tokens, password reset links, lottery draws, shuffle-based games with money at stake, or anything else where predicting the next value would matter.
Modulo bias, and why this tool avoids it
The obvious way to get a number in a range is random() % range. It is also subtly wrong.
Suppose your source produces values 0–255 and you want 1–10. There are 256 possible inputs and 10 outputs, and 256 is not divisible by 10. Values 0–5 each occur 26 times across the range while 6–9 occur 25 times, so the low outcomes are about 4% more likely.
The fix is rejection sampling: discard any source value that falls in the uneven tail and draw again.
function randInt(lo, hi) {
const range = hi - lo + 1;
const limit = Math.floor(2**32 / range) * range;
let r;
do {
r = crypto.getRandomValues(new Uint32Array(1))[0];
} while (r >= limit); // reject the biased tail
return lo + (r % range);
}The loop almost never runs twice, and the result is exactly uniform. This is the algorithm behind every number on this page.
Shuffling correctly
The list shuffle uses the Fisher-Yates algorithm, walking backwards through the array and swapping each element with a randomly chosen earlier one. Every one of the n! orderings is equally likely.
The common broken alternative is array.sort(() => Math.random() - 0.5). It looks elegant and produces a badly non-uniform distribution, because sort algorithms assume a consistent comparator and an inconsistent one leaves elements near their original positions. Microsoft used this exact approach in a 2008 browser ballot screen and Internet Explorer appeared in the first position far more often than chance allows.
// Correct: Fisher-Yates
for (let i = a.length - 1; i > 0; i--) {
const j = randInt(0, i);
[a[i], a[j]] = [a[j], a[i]];
}What each mode is for
- Integers with no repeats — raffle draws, sampling rows from a dataset, assigning unique slots. Implemented as a full shuffle then a slice, so it is uniform even when you draw most of the range.
- Decimals — Monte Carlo inputs, jitter values, synthetic test data.
- Dice and coins — tabletop games and teaching probability. The summary line reports the actual heads/tails split so you can watch the law of large numbers at work.
- Hex bytes — nonces, initialisation vectors and API key material. For passwords use the dedicated password generator, which reports entropy.
- Pick and teams — fair selection from a list, and splitting a group into balanced teams by dealing round-robin from a shuffled list.
Frequently Asked Questions
Are these numbers truly random?
They come from your operating system's cryptographically secure generator, seeded from physical entropy. That is as close to true randomness as software gets, and it is unpredictable in practice.
Why not use Math.random()?
It is a fast pseudorandom generator with no security guarantees. Its internal state can be recovered from a few observed outputs, which makes it unsuitable for anything secret.
What is modulo bias?
When the size of the random source is not an exact multiple of your target range, some outcomes become slightly more likely. This tool rejects the uneven tail and redraws, so every value is equally probable.
How do I pick numbers without repeats?
Tick “No repeats”. The full range is shuffled with Fisher-Yates and the first n values taken, which stays uniform even when you draw nearly the whole range.
Is this suitable for a prize draw?
The generation is sound. For anything with legal or financial weight you also need auditability — a published seed, a witnessed draw or a certified RNG service — which no client-side tool can provide.
Can I reproduce the same sequence?
No. A cryptographic generator has no user-settable seed by design. If you need reproducibility, use a seeded PRNG such as Mersenne Twister in your own code.
Sources & further reading
- MDN: crypto.getRandomValues — the CSPRNG interface used throughout this tool
- Fisher-Yates shuffle — the only correct in-place shuffle algorithm
- NIST SP 800-90A Rev. 1 — recommendations for random number generation
- Russ Cox on random sorting — why sort-based shuffles are non-uniform