Skip to content

Random Number Generator

Estimates for educational purposes — not financial, medical, or legal advice. See terms.

Generate uniform random integers in any range, with or without repeats. Uses the browser’s crypto.getRandomValues API for real entropy (the same source used for cryptographic key generation) and rejection sampling to eliminate modulo bias. Useful for lottery picks, test fixtures, shuffling, dice rolls, and anywhere you need unbiased samples from a range.

How it works

Three inputs: min (lower bound, inclusive), max (upper bound, inclusive), count (how many values you want). One checkbox: unique — whether the values can repeat.

With replacement (unique = off): each value is drawn independently using rejection sampling to avoid modulo bias. Each draw is a fresh 32-bit random integer from crypto.getRandomValues, reduced to the target range by rejecting outcomes that fall into the biased tail. Over many draws, every value in [min, max] appears with equal probability.

Without replacement (unique = on): the result is guaranteed to be all-distinct. When count is small relative to range, the tool uses rejection sampling with a Set to track seen values. When count approaches range, it switches to a Fisher-Yates partial shuffle (which is faster when you’re picking most of the values). The crossover is at count / range = 0.3.

Example: dice rolls

Min = 1, max = 6, count = 10, unique = off. You get a list of 10 dice rolls — possibly with repeats, just like real dice. Flip the checkbox on and you’d only get 6 values (since 7 unique values from a 6-value range is impossible, the tool errors). For multi-die rolls with standard polyhedral sizes (d4, d6, d8, d10, d12, d20, d100) and history, the coin flip and dice roller is the tabletop-specific version.

Example: lottery draw

Min = 1, max = 49, count = 6, unique = on. You get six distinct numbers from 1 to 49 — a standard 6/49 lottery draw. Because the range is large (49) and the count is small (6), rejection sampling is fast: on average it needs about 6.4 draws to find 6 unique values.

Example: shuffling a deck

Min = 1, max = 52, count = 52, unique = on. This triggers the Fisher-Yates shuffle path because count / range = 1.0. The tool generates every integer 1–52 in random order — a complete shuffled permutation.

Example: picking a password character pool

Min = 33, max = 126, count = 16, unique = off. You get 16 random printable ASCII codepoints, which you could map to characters for a random password. (Though for actual password generation, prefer the password generator — this one doesn’t enforce character-class requirements or deduplicate based on readability.)

Why crypto.getRandomValues

Math.random is a pseudo-random generator — fast but predictable if you know the algorithm’s state. Browsers use xorshift-like algorithms that pass statistical tests but aren’t cryptographically secure. crypto.getRandomValues pulls from the operating system’s entropy pool (/dev/urandom on Linux/macOS, BCryptGenRandom on Windows) which is gathered from genuinely unpredictable sources (disk timing jitter, keyboard input timing, network interrupt timing, etc.).

For random dice or lottery picks, either source is fine — the difference is invisible. For anything security-adjacent (session tokens, temporary passwords, game seeds that shouldn’t be guessable), crypto.getRandomValues is the right default. The tool uses it unconditionally and only falls back to Math.random in the extraordinarily rare case where the Crypto API isn’t available.

Rejection sampling vs. modulo bias

A naive rand32() % range has a tiny bias toward smaller values when range doesn’t evenly divide 2³². For range = 7, each of the first 4 values (0–3) gets (2³²)/7 + 1 source values mapping to it; the last 3 (4–6) get only (2³²)/7. That’s a bias of roughly 1 in 600 million per draw — undetectable in casual use, but a mathematical defect that real RNG libraries avoid.

Rejection sampling: draw a uint32, if it’s below the largest multiple of range that fits, accept; otherwise draw again. Expected number of draws is < 2, the bias is gone, and the cost is negligible.

Reproducibility

Each generation click pulls fresh OS entropy, so you won’t get the same sequence twice — which is the right default for security but makes reproducing results for testing harder. If you need deterministic sequences, use the exported mulberry32 PRNG directly in code: it’s a small seeded PRNG that produces the same output for the same seed. That’s what the unit tests do.

What this tool does not do

It doesn’t generate non-integer numbers. Floats, decimals, and probabilities aren’t supported. The tool is strictly integer-valued. For floats, a separate uniform-in-[0,1) or normal-distribution tool would be needed.

It doesn’t weight values unequally. Every value in [min, max] is equally likely. For biased sampling (e.g., draw with probabilities [0.5, 0.3, 0.2]), you need a weighted-random-choice tool.

It doesn’t save generated sequences. Each click is a fresh draw; previous results disappear. If you need persistence, copy the result out of the UI.

It doesn’t support seeding through the UI. Fresh OS entropy on every click is the default. For reproducible sequences, use the calculator module directly in code with the mulberry32 PRNG.

It doesn’t generate characters or strings. The output is always numeric. For random strings, passwords, or tokens, a dedicated password generator is the right tool.

Frequently asked questions

Are these numbers really random?

They're as random as your browser's crypto.getRandomValues function provides, which is cryptographically strong entropy — the same API used for generating cryptographic keys and tokens. It pulls from OS-level entropy pools (on Linux that's /dev/urandom, on macOS it's getentropy(), on Windows it's BCryptGenRandom). For most purposes — games, lottery picks, test fixtures — this is overkill. For cryptographic use like generating keys or nonces, still prefer a dedicated crypto library that's been audited for side channels, but the underlying randomness source here is the same one those libraries use.

What does 'unique values only' mean?

With the checkbox off, each number is drawn independently, so you can get the same value twice in a row — useful for simulating dice rolls or any 'with replacement' sampling. With the checkbox on, the tool guarantees every number in the output is different, useful for lottery picks ('6 of 49'), shuffling, or picking N distinct items from a list of candidates. If you ask for more unique values than fit in the range (like 100 unique values from 1–50), you get an error.

What's 'modulo bias' and why does this tool avoid it?

A naive random-integer function does `Math.floor(Math.random() * range)` or `rand32() % range`, which biases slightly toward smaller values when `range` doesn't evenly divide the RNG's output space. If your RNG produces values 0–999 and you want 0–99, each of the 100 target values maps to 10 source values — fair. But if you want 0–6 (range of 7), the first 4 values each get 143 source values and the last 3 get only 142. Over millions of draws, those first 4 get picked more often. The tool uses rejection sampling — throw away out-of-range source values and redraw — which eliminates the bias entirely at the cost of occasionally needing a redraw (statistically rare).

How fast is 'unique' mode for large counts?

Depends on the ratio of count to range. When you pick a small number from a large range (say, 6 of 49), rejection sampling is fast: each draw has a ~88% chance of being new, so on average you need about 7 draws total. When you pick most of the range (say, 40 of 49), rejection sampling wastes draws on near-duplicates — so the tool switches to a Fisher-Yates partial shuffle instead. The crossover happens at count/range = 0.3. Both algorithms produce uniformly distributed samples; the algorithmic switch is just for speed.

Can the seed be fixed for reproducibility?

Not through the UI, no. The tool uses fresh OS entropy on every click, which is the security-sensible default but doesn't give reproducibility. If you need reproducible sequences for testing, import the calculator module directly and inject the exported `mulberry32` PRNG — it accepts a seed and produces the same sequence every time. That's what the unit tests do. For production use through the UI, the non-reproducible default is correct.