When you roll a die, you get a genuinely unpredictable outcome. When a computer generates a "random" number, the process is almost always fundamentally different — and far more deterministic than most people assume. This distinction between true randomness and pseudo-randomness has profound consequences for security, cryptography, gaming, and scientific simulation. Getting it wrong has caused real-world disasters.
A traditional computer follows a completely deterministic process: given the same inputs and state, it always produces the same outputs. Determinism is desirable for reliability and reproducibility — but it is the exact opposite of randomness. A computer cannot generate true randomness from pure computation alone.
In 1994, security researchers discovered that Netscape's SSL implementation — the foundation of secure web browsing at the time — used a predictably seeded random number generator. The seed was derived from the current time, process ID, and parent process ID, all of which an attacker could guess or observe. This meant Netscape's "secure" session keys were predictable, completely undermining HTTPS security for millions of users.
A pseudo-random number generator (PRNG) is an algorithm that produces a sequence of numbers that appears statistically random — passing standard randomness tests — but is entirely determined by an initial value called the seed. Given the same seed, a PRNG always produces the identical sequence.
The most commonly encountered PRNG in programming is the Linear Congruential Generator (LCG), used (in various forms) by Math.random() in older JavaScript engines, rand() in C, and random.random() in Python (though Python uses the Mersenne Twister, a far more sophisticated PRNG).
PRNGs are fast, reproducible, and sufficient for many applications: game mechanics, statistical simulations, shuffle algorithms, Monte Carlo methods, and unit test data generation. The reproducibility is actually useful — setting the same seed gives you the same sequence, enabling repeatable experiments and debuggable test cases.
A true random number generator harvests randomness from genuinely unpredictable physical processes. Common entropy sources include:
TRNGs are slower than PRNGs and limited in output rate — you can only collect entropy as fast as the physical process generates it. For this reason, operating systems maintain an entropy pool that continuously accumulates randomness from hardware events and drains it on demand.
In practice, most security applications use a hybrid approach: a CSPRNG (Cryptographically Secure Pseudo-Random Number Generator). A CSPRNG is seeded with true randomness from the OS entropy pool, then uses a cryptographic algorithm to generate a long, deterministic-but-unpredictable sequence from that seed. Key properties of a CSPRNG:
| Type | Speed | Reproducible | Secure | Use Case |
|---|---|---|---|---|
| PRNG (Math.random) | Very fast | Yes (with seed) | No | Games, simulations, shuffles |
| TRNG (hardware) | Slow | No | Yes | Seeding CSPRNGs, key generation |
| CSPRNG (crypto.getRandomValues) | Fast | No | Yes | Passwords, tokens, cryptographic keys |
Math.random() returns a pseudo-random float between 0 and 1. The specification explicitly states it is "not suitable for security-sensitive applications." Different browsers implement it differently — V8 (Chrome/Node) uses xorshift128+, Firefox uses xoroshiro128+. Both are fast and statistically good but entirely predictable if the seed is known.
crypto.getRandomValues() is the Web Crypto API's CSPRNG. It fills a typed array with cryptographically secure random bytes, seeded from the OS entropy pool. It is 2–10× slower than Math.random() but generates values suitable for passwords, tokens, and any security-sensitive purpose. Always use crypto.getRandomValues() when randomness matters for security.
⚠️ Security rule: Never use Math.random() to generate passwords, session tokens, CSRF tokens, API keys, or any value where an attacker guessing the output would be harmful. Always use crypto.getRandomValues() or its equivalent in your language.
The consequences of weak randomness in security contexts are severe and well-documented:
java.util.Random (a PRNG) instead of SecureRandom (a CSPRNG) to generate transaction signatures. Attackers recovered private keys and stole coins.Not every use of randomness requires a CSPRNG. PRNGs are the right choice when: you need high-speed generation of large quantities of numbers (Monte Carlo simulations, procedural game world generation), you need reproducibility (seeding with a known value for testing), or security is not a concern (shuffling a display order, generating fake data for development). Choosing a CSPRNG for these cases adds unnecessary overhead with no benefit.
Our Random Number Generator uses crypto.getRandomValues() — cryptographically secure, browser-based, no signup.