Everyday

How Random Number Generators Work: PRNG, CSPRNG and True Randomness

What separates pseudo-random, cryptographically secure and true random number generators, how browsers make random numbers, and the bias mistake to avoid.

Random Number Generator topic photo

Computers can't flip coins

A computer follows instructions, and the same instructions with the same input give the same output. So most random numbers on a computer come from a formula: a pseudo-random number generator (PRNG). It starts from a value called a seed and keeps scrambling its internal state to produce numbers that look random.

Give a PRNG the same seed and you get the same sequence every time. That's a feature for testing and simulations, where you want to rerun an experiment exactly. It's a problem anywhere an attacker could work out the seed or the state and predict what comes next.

Three kinds of generator

It helps to sort generators by what they promise, because the names get mixed up a lot.

  • PRNG: fast, repeatable from a seed, statistically random-looking, but predictable if you know the state. Good for games, simulations and tests.
  • CSPRNG (cryptographically secure PRNG): designed so that seeing past outputs doesn't help you predict future ones. Seeded from real entropy. Used for passwords, keys and tokens.
  • True random number generator: measures a physical process, such as electrical noise, as its entropy source. Usually used to seed a CSPRNG rather than handed out directly.

What your browser actually uses

JavaScript gives web pages two options. Math.random() returns a decimal from 0 up to (but not including) 1. MDN's reference says it isn't cryptographically secure and that its seed can't be chosen or reset. In Chrome's V8 engine it switched to the xorshift128+ algorithm in a 2015 update, which has a period of 2^128 - 1 and passes the TestU01 statistical tests. That's plenty for animations and games, but a fast PRNG with no security promise.

The other option is crypto.getRandomValues() from the Web Crypto API. MDN explains that browsers don't call a true random source for every request. They run a cryptographically suitable PRNG seeded with entropy from the operating system, such as /dev/urandom on Unix-like systems. A single call can fill up to 65,536 bytes. Our random number generator uses this method whenever you leave the seed blank.

Turning random bits into a range

Getting random bytes is half the job. You still need a number from 1 to 10, and the obvious shortcut adds bias. Take one random byte (256 possible values) and use the remainder after dividing by 10. Since 256 = 25 x 10 + 6, the remainders 0 to 5 each come from 26 byte values and 6 to 9 from only 25. So 0 to 5 show up 10.16% of the time and 6 to 9 only 9.77%.

That's called modulo bias. The fix is rejection sampling: take just enough bits to cover the range, and if the value falls outside it, throw it away and draw again. For 1 to 10 you'd read 4 bits (0 to 15), keep 0 to 9 and redraw on 10 to 15. Every result is then exactly 10%.

How to tell if numbers are random enough

You can't judge randomness from a short list. Ten draws from 1 to 10 with repeats allowed will almost always include a duplicate, and long runs of high or low numbers happen in genuinely random data. Statistical test suites like TestU01 check millions of outputs for patterns instead.

For security, passing statistical tests isn't enough. A CSPRNG also has to resist prediction, which is why NIST publishes approved designs for deterministic random bit generators in SP 800-90A and guidance on entropy sources in SP 800-90B.

Which generator to use

Match the generator to what's at stake. The question to ask is what happens if someone can predict the next number.

  • Games, animations and shuffling a playlist: Math.random() or any decent PRNG is fine.
  • Simulations and software tests: a seeded PRNG, so you can rerun the exact same sequence and check results.
  • Passwords, keys, tokens and anything security related: a CSPRNG, such as crypto.getRandomValues() in a browser.
  • Lotteries, paid prize draws and legal selection: an audited process with documented equipment, records and oversight, as the rules require.

Common mistakes

Using Math.random() to make passwords or session tokens is the classic one. Others include seeding a generator with the current time (easy to guess), using the remainder trick on a small random range and getting modulo bias, and reusing a fixed seed in production code after testing. For casual picks none of this matters much. For anything with money or security attached, use the platform's cryptographic generator.

Frequently asked questions

Is Math.random() truly random?

No. It's a pseudo-random generator. Its output looks random for everyday use, but MDN warns it isn't cryptographically secure and shouldn't be used for security.

What is a seed in a random number generator?

The starting value for a pseudo-random generator. The same seed always produces the same sequence, which makes results repeatable for testing.

What is modulo bias?

The unevenness you get when you squeeze random values into a range using a remainder, and the source range doesn't divide evenly. Some results end up slightly more likely than others.

Are hardware random number generators better?

They supply real entropy from physical processes, but they're slower. Most systems use them to seed a cryptographically secure PRNG rather than for every number.

How many random bytes can a browser give at once?

crypto.getRandomValues() accepts up to 65,536 bytes per call. Ask for more and it throws a QuotaExceededError.

References

These sources support the method or guidance used for Random Number Generator. Verify time-sensitive rules at the source.

Try the calculator

Open Random Number Generator, enter your scenario, and compare its supporting rows with this guide's method and checks.

Open Random Number Generator