Random Number Generator
Uniform random numbers from crypto.getRandomValues by rejection sampling, with the modulo bias it avoids spelled out for your range.
generated in your browser
Range 1 to 100
How many 6
Repeats not allowed
The bias this avoids
byte % range up to 50.00% more likely for the low end
method used rejection sampling, so every value is equally likely
source crypto.getRandomValues
Taking a random byte modulo 100 would not be uniform: 256 is not a
multiple of 100, so the first values in the range get an extra chance
and come up about 50.00 percent more often. Rejection sampling discards
the values in the short final block, which costs a few bytes and buys a
flat distribution.
`Math.random` is not suitable for anything that functions as a secret.
It is a fast pseudo-random generator, its internal state can be
recovered from enough of its output, and nothing about it is guaranteed
between engines. For a shuffle in a game or a test fixture it is fine.
`crypto.getRandomValues` is the right source for a token, a password, a
draw with a prize or anything an adversary would benefit from
predicting. It is in every browser and there is no reason to use
anything else for those.
Random does not mean evenly spread. Six numbers from 1 to 100 will often
include two close together and leave a gap of thirty, and a sequence
that looks too tidy is usually the suspicious one. Clustering is what
randomness looks like.
With no repeats this is a draw rather than a sequence of rolls: the
whole range is shuffled with Fisher-Yates and the first few are taken,
which is the correct way to do it and the reason the range has to be
small enough to enumerate.
A random number is not an identifier. For an id, use a UUID or a long
random string: the point of those is that the space is large enough that
a collision is not a practical concern, which a range of a hundred is
not.
This cannot be seeded, on purpose. A seeded generator produces a
repeatable sequence, which is useful in testing and fatal anywhere the
numbers are supposed to be unpredictable, and offering both in one tool
invites using the wrong one.
Output is valid and updates as you type.
Fix the highlighted fields to update the output.
bytes[ 0 ] % 100 is not a random number from 1 to 100. It is a number from 1 to 100 where the first
56 values are 50 percent more likely than the rest, because 256 is not a multiple of 100.
That is modulo bias, and it is the standard bug in code that generates random numbers from bytes. The fix is rejection sampling: throw away the values that fall in the short final block and draw again. It costs a few discarded bytes and buys a flat distribution, and it is what this does.
The source is crypto.getRandomValues rather than Math.random, and the difference matters for
anything an adversary would benefit from predicting.
How to use
- Set the range. Both ends are included.
- Choose how many, and whether repeats are allowed.
- Read the bias figure to see what the naive method would have done to this range.
Example
Six different numbers from 1 to 100:
11, 15, 35, 45, 64, 97
Range 1 to 100, 100 possible values
How many 6, all different
Smallest 11
Largest 97
Mean 44.50
expected mean 50.50
The bias this avoids
byte % range up to 50.00% more likely for the low end
method used rejection sampling, so every value is equally likely
source crypto.getRandomValues
The mean of six numbers is 44.5 against an expected 50.5, and that is what six draws look like. Reading a bias into a sample that small is the other half of this subject.
Pitfalls
Modulo bias is worst where the range is close to the pool. For a die from one byte it is about 2.4 percent. For a range of 200 it is a factor of two: the first 56 values get twice the chance. The figure for your range is printed above.
Math.random is not for secrets. It is a fast pseudo-random generator, its internal state can be
recovered from enough output, and nothing about it is specified between engines. For a game shuffle
or a test fixture it is fine; for a token, a password reset or a prize draw it is not.
Clustering is what randomness looks like. Six numbers from 1 to 100 will often have two close together and a gap of thirty. A result that looks evenly spread is the suspicious one, and people asked to “act random” produce exactly that too-tidy pattern.
A repeat arrives sooner than you expect. Drawing from 100 values with repeats allowed, there is a better than even chance of a duplicate by the thirteenth draw. That is the birthday problem, and it is why a short random number is a poor unique identifier.
A random number is not an id. For an identifier use a UUID or a long random string, where the space is large enough that collisions are not a practical concern. A range of a hundred is not that.
Sorting does not change the draw. The sort here is presentation. If a list of “random” numbers has to come out in order, that is a display choice and not part of the randomness.
There is no seed, on purpose. A seeded generator gives a repeatable sequence, which is exactly right in a test and exactly wrong anywhere the numbers must be unpredictable. Offering both in one tool invites reaching for the wrong one.
Compatibility
Everything runs in the browser: nothing is uploaded and nothing is stored, and no number is ever sent anywhere.
crypto.getRandomValues is in every browser and has been for over a decade. The integer is drawn by
rejection sampling over as many bytes as the range needs, so a range of a thousand uses two bytes and
the distribution stays flat.
A draw with no repeats shuffles the whole range with Fisher-Yates and takes the first few, which is the correct method and the reason the range has to be small enough to enumerate. Past a million the tool says so rather than trying.
The flatness is checked in the test suite over 120,000 rolls of a six-sided range, with a tolerance tighter than the bias a modulo would have introduced. The rejection path itself is checked with a fixed byte sequence.
At build time there is no randomness available, so the page ships with a placeholder and the numbers appear when it loads. Numbers baked into the HTML would be identical for every visitor.
Frequently asked questions
Is this cryptographically secure?
crypto.getRandomValues is the right primitive for a token or a key. Whether the
whole thing is secure depends on what you do with the output, and pasting it into a web page is not
part of a secure workflow.How do I do this in JavaScript?
crypto.getRandomValues( new Uint32Array( 1 ) )[ 0 ] for a raw value, then reject anything at or
above Math.floor( 2 ** 32 / range ) * range before taking the modulo. Skipping the rejection step is
the bug this page is about.