💻 Coding

Random Number Generator

Generate unbiased random integers within a custom range using real cryptographic randomness.

About the Random Number Generator

Random integers are drawn from crypto.getRandomValues using rejection sampling, a technique that sidesteps the modulo bias a naive Math.random() * range calculation would quietly introduce. That keeps every value in your chosen range equally likely to come up, and you can choose whether repeats are allowed or every result has to be unique.

What modulo bias actually looks like with real numbers

Say you want a number from 1 to 10, and you generate it by pulling a random byte (0-255) and computing value % 10. That seems reasonable, but 256 isn't evenly divisible by 10, it's 25 full groups of 10 plus 6 left over. That means the remainders 0 through 5 each occur 26 times across the 256 possible byte values, while remainders 6 through 9 only occur 25 times, a small but real, measurable skew toward the lower half of your range. It's subtle enough to go unnoticed in casual use, but it's a genuine statistical bias, not a rounding quirk, and it compounds the smaller your range is relative to the byte space you're drawing from.

How rejection sampling eliminates that bias entirely

Rather than accepting whatever value comes back and living with the skew, this tool first calculates the largest multiple of your requested range that still fits within the space of possible random values it draws (limit = maxValue − (maxValue % range)), then draws random bytes in a loop, discarding and redrawing any result that falls above that limit. Every value it actually accepts falls into one of an equal number of buckets, so the final result is genuinely, mathematically uniform across your range, with zero measurable bias, at the cost of occasionally needing to draw more than once, which happens rarely and is imperceptibly fast.

Why the number of random bytes pulled changes with your range

Rather than always drawing a fixed 4 bytes regardless of range size, this tool calculates the minimum number of bytes actually needed to comfortably cover your requested range using Math.ceil(Math.log2(range) / 8), a range of 1 to 100 needs just a single byte, while a range spanning millions needs several. Sizing the draw to match the actual range keeps the rejection rate low and the computation efficient regardless of whether you're picking a small dice-roll-sized range or a very wide one.

How uniqueness is guaranteed without risking an infinite loop

When "Allow duplicates" is off, the tool collects results into a JavaScript Set (which automatically ignores duplicate values) and keeps drawing until the set reaches your requested quantity. Before doing that, it explicitly checks whether your range even contains enough distinct numbers to satisfy the request, if you ask for 50 unique values from a range of only 20 possible numbers, it stops immediately with a clear error rather than looping forever trying to find 50 unique values that don't exist.

Why sorting uses an explicit comparator instead of the default

JavaScript's built-in array .sort() defaults to comparing elements as strings, which produces famously wrong results for numbers, sorting [10, 2, 1] without a comparator gives you [1, 10, 2], since "10" sorts before "2" alphabetically. This tool explicitly passes (a, b) => a - b as the comparator when the Sort Results option is checked, forcing a genuine numeric ascending sort instead of falling into that well-known JavaScript pitfall.

Why Min and Max are validated before anything is generated

Before drawing a single random number, the tool checks that Min is genuinely less than Max and that both are valid numbers at all, rejecting a malformed or backwards range immediately with a clear message rather than attempting to generate from a nonsensical or empty range and producing a confusing downstream error. This upfront validation, along with the separate duplicate-count check, means any failure you see points directly at what's actually wrong with your inputs rather than a generic error further down the process.

Frequently Asked Questions

What exactly is modulo bias, in concrete terms?

If you generate a random byte (0-255) and compute value % 10 to get a number 1-10, remainders 0-5 occur 26 times across the 256 possible bytes while remainders 6-9 occur only 25 times, since 256 isn't evenly divisible by 10. That's a real, measurable statistical skew toward the lower part of the range.

How does this tool avoid that bias?

It uses rejection sampling: it calculates the largest multiple of your range that fits the random values it draws, then discards and redraws any result falling outside that clean multiple. Every accepted value falls into an equally-sized bucket, producing a genuinely uniform result with zero measurable bias.

Why does the number of random bytes drawn change based on my range?

The tool calculates the minimum bytes needed to comfortably cover your specific range using a log2 calculation, rather than always pulling a fixed number of bytes. This keeps the rejection rate low and computation efficient whether your range is small or very large.

What happens if I ask for more unique numbers than my range contains?

The tool checks upfront whether your range has enough distinct values to satisfy the request. If not, for example asking for 50 unique numbers from a range of only 20, it shows a clear error immediately instead of looping forever trying to find values that don't exist.

Why does the Sort Results option need special handling?

JavaScript's default array sort compares values as strings, which sorts numbers incorrectly, treating "10" as coming before "2". This tool explicitly uses a numeric comparator (a, b) => a - b when sorting is enabled, avoiding that well-known JavaScript pitfall.

Is this generator suitable for things like raffles or drawing lots?

Yes, since it draws from a cryptographically secure random source and eliminates modulo bias through rejection sampling, every number in your range has a genuinely equal chance of appearing, which is exactly what a fair drawing requires.