How Random Number Generators Work: From Lottery Tickets to Cryptography
Published: July 11, 2025 · 6 min read
Here's an unsettling fact: most "random" numbers generated by computers aren't random at all. They're the product of mathematical formulas — predictable, deterministic, and reproducible if you know the starting conditions. For shuffling a playlist or rolling dice in a video game, that's perfectly fine. But for generating encryption keys, session tokens, or password reset codes? Using the wrong kind of randomness can be catastrophic.
Let's unpack how random number generators actually work, why there are different types, and when you must use each one.
The Two Families of Randomness
Random number generators fall into two broad categories: Pseudo-Random Number Generators (PRNGs) and Cryptographically Secure Pseudo-Random Number Generators (CSPRNGs). Despite the intimidating names, the distinction is straightforward: PRNGs are fast but predictable; CSPRNGs are slower but genuinely unpredictable. A third category — True Random Number Generators (TRNGs) — uses physical hardware to harvest randomness from the real world, but these are rare in consumer software.
Pseudo-Random Number Generators (PRNGs)
A PRNG takes a starting value called a seed and runs it through a mathematical algorithm to produce a sequence of numbers that look random. The key word is "look." Given the same seed, a PRNG will always produce the exact same sequence — every single time. This reproducibility is both a feature (useful for debugging, testing, and procedural generation in games like Minecraft) and a fatal flaw (for anything security-related).
JavaScript's Math.random() is the most widely used PRNG in web development. Under the hood, most browsers implement it using the xorshift128+ algorithm, which is fast — fractions of a microsecond per call — and produces numbers that pass basic statistical randomness tests. Here's what it looks like in practice:
// Generate a random number between 0 and 1
const value = Math.random(); // e.g., 0.7348829103458321
// Random integer between 1 and 10
const roll = Math.floor(Math.random() * 10) + 1;
// Shuffle an array (Fisher-Yates)
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
For games, UI animations, A/B test assignments, and non-security use cases, Math.random() is perfectly adequate. It's fast, convenient, and the statistical distribution is good enough for these purposes.
The Danger: Why Math.random() Must Never Touch Security
In 2014, security researchers discovered a vulnerability in the Bitcoin blockchain: several wallet implementations were using a weak PRNG to generate private keys. The result? Attackers were able to predict the "random" keys and steal funds. The same class of bug has been found in casino slot machines, online poker sites, and password reset systems.
The problem with PRNGs like Math.random() is that if an attacker can determine the seed — or even narrow it down — they can predict every "random" value your application will ever generate. The seed in most browsers comes from the system time (milliseconds since epoch), which has very limited entropy. An attacker who knows roughly when your server started can brute-force the seed in seconds. Once they have the seed, they can reproduce your session tokens, password reset links, and API keys.
Cryptographically Secure Generators (CSPRNGs)
A CSPRNG solves this problem by drawing entropy from multiple unpredictable sources: mouse movements, keyboard timings, network packet arrival times, hard drive seek latencies, and dedicated hardware entropy sources built into modern CPUs (like Intel's RDRAND instruction). The operating system collects and mixes this entropy into an "entropy pool," which the CSPRNG uses to seed its algorithm. The result is a sequence of numbers that is computationally infeasible to predict or reproduce — even if you know the algorithm.
In the browser, the CSPRNG is accessed through crypto.getRandomValues():
// Generate a cryptographically secure random value
const array = new Uint32Array(1);
crypto.getRandomValues(array);
console.log(array[0]); // Truly unpredictable
// Generate a secure random hex string (e.g., for session tokens)
const bytes = new Uint8Array(32);
crypto.getRandomValues(bytes);
const token = Array.from(bytes, b => b.toString(16).padStart(2, '0')).join('');
In Node.js, you'd use crypto.randomBytes(). In Python, os.urandom() or the secrets module. Every language has a CSPRNG — the key is knowing that it exists and using it for the right situations.
When to Use Which
| Use Case | Use This | Why |
|---|---|---|
| Rolling dice in a game | Math.random() | Speed matters; predictability is irrelevant |
| Shuffling a playlist | Math.random() | No security implication |
| A/B testing assignment | Math.random() | Distribution quality matters, not unpredictability |
| Generating password reset tokens | crypto.getRandomValues() | Predictable tokens = account takeover |
| Creating API keys | crypto.getRandomValues() | Must be unguessable |
| Generating encryption keys (AES, RSA) | crypto.getRandomValues() | The entire security of the encryption depends on key unpredictability |
| Session IDs | crypto.getRandomValues() | Predictable session IDs enable session hijacking |
Hardware Random Number Generators
For applications that demand the highest possible entropy — lottery systems, cryptographic key generation for certificate authorities, or scientific simulations requiring true randomness — hardware random number generators (HRNGs) sample physical phenomena directly. These devices measure quantum effects, thermal noise in resistors, or radioactive decay to produce genuinely non-deterministic bits. Intel and AMD CPUs include on-chip HRNGs (RDRAND and RDSEED instructions) that use thermal noise from the silicon itself. Cloudflare famously uses a wall of lava lamps — yes, lava lamps — as an entropy source, photographing the chaotic motion and converting it into random bits.
Try It Yourself
The best way to internalize the difference is to experiment. Our free Random Number Generator lets you generate numbers within any range, choose between different generation methods, and instantly see the results. Whether you need a quick dice roll for game night or a batch of secure random numbers for a project, it's ready when you are.
Remember the golden rule: if the randomness you're generating could ever be used against you or your users — passwords, tokens, keys, identifiers — reach for crypto.getRandomValues() or its equivalent. The milliseconds of extra computation time are the cheapest security insurance you'll ever buy.