Technology · 8 min read
Why True Randomness Is Hard: Inside Pseudo-Random Number Generators
What actually happens inside Math.random and similar functions, why their outputs are not truly random, and what cryptographic generators do differently.
Every modern programming language has a function that returns a random number. In JavaScript it is Math.random. In Python it is random.random. In C it is rand. They all work, in the sense that they return numbers that look random enough for most purposes. They are also, all of them, lying. There is nothing random inside. They are pseudo-random number generators — deterministic algorithms that produce sequences designed to look random while being entirely predictable to anyone who knows their inner state.
What a PRNG Actually Is
A pseudo-random number generator is a function that takes a state and returns a number plus a new state. Call it repeatedly, and you get a sequence of numbers that pass statistical tests for randomness — uniform distribution, no obvious autocorrelation, no apparent patterns. But because the generator is deterministic, the same starting state always produces the same sequence. This is, depending on your perspective, either a useful feature or a fatal flaw.
For simulation, gaming, and most general programming, determinism is a feature. A game that uses a seeded PRNG for procedural world generation can recreate the same world from the same seed, making bug reports reproducible and enabling features like shared seed worlds. A statistical simulation that uses a seeded PRNG can be re-run by other researchers to verify results.
For security applications — encryption keys, session tokens, password salts — determinism is catastrophic. If an attacker can guess the state of the generator, they can predict every output, present and future. This is the line that separates ordinary PRNGs from cryptographically secure pseudo-random number generators (CSPRNGs).
The Linear Congruential Generator
The simplest practical PRNG is the linear congruential generator (LCG). Its formula is just X_{n+1} = (a * X_n + c) mod m, where a, c, and m are carefully chosen constants. With good constants, an LCG can have a period — the number of iterations before it cycles back to its starting state — of billions. With bad constants, it can have catastrophic structural defects.
The most famous example of bad constants was IBM's RANDU generator, used widely in the 1960s scientific computing world. RANDU's choice of a = 65539 and m = 2^31 caused its outputs to fall on just 15 hyperplanes when plotted in three dimensions. Years of simulation results, published in respected journals, were quietly compromised before researchers noticed.
The Mersenne Twister
The Mersenne Twister, published in 1997, replaced LCGs as the workhorse PRNG for most languages. Its period — 2^19937 - 1, an inconceivably large number — guarantees that you will never see a repeated cycle in any realistic program. It passes virtually every standard statistical test for randomness, runs quickly on modern hardware, and is the default Math.random implementation in many JavaScript engines.
It is also, for cryptographic purposes, broken. Given any 624 consecutive 32-bit outputs from a Mersenne Twister, an attacker can mathematically invert the algorithm and recover the internal state, then predict every subsequent output. For password generation, session tokens, or cryptographic nonces, the Mersenne Twister cannot be used.
What Makes a Generator Cryptographically Secure
A CSPRNG must satisfy two properties that ordinary PRNGs do not. First, the next-bit test: given a sequence of past outputs, no polynomial-time algorithm should be able to predict the next bit with probability significantly better than 50%. Second, state compromise resistance: even if an attacker recovers the internal state at one point in time, they should not be able to reconstruct previous outputs.
These properties are achieved by building the generator on top of cryptographic primitives — typically a stream cipher or a hash function. The modern Linux kernel uses a generator based on ChaCha20, the same stream cipher used in TLS 1.3. Apple's CoreCrypto uses an AES-based design. The Web Crypto API's getRandomValues delegates to the operating system's CSPRNG, which is one of the above on any modern platform.
The Entropy Problem
Even a perfect CSPRNG needs a starting state — a seed — that the attacker cannot guess. If the seed is predictable, the entire sequence is predictable, no matter how well-designed the algorithm. This is where entropy collection comes in: the operating system continuously gathers unpredictable inputs from hardware sources (keystroke timings, mouse jitter, disk I/O, network packet timing, thermal noise on supported CPUs) and mixes them into an entropy pool. The CSPRNG is reseeded from this pool, ensuring that an attacker who has not been watching every keystroke since boot cannot reconstruct the seed.
Failures of entropy collection have caused real security disasters. In 2008, a bug in the Debian OpenSSL package reduced the entropy of seed generation to just 32,768 possible values. Cryptographic keys generated on affected systems could be brute-forced in seconds. Years of accumulated SSH keys, SSL certificates, and other secrets had to be regenerated.
Practical Implications
For everyday programming, Math.random is fine. For games, simulations, and unimportant random selection, even a basic LCG would work. But the moment your random number is used for something that needs to be unpredictable to an adversary — picking a winner in a high-stakes giveaway, generating a session token, or selecting where a spinner wheel will land in front of a live audience — you should be using a CSPRNG, accessed in JavaScript via crypto.getRandomValues. The performance cost is negligible. The integrity benefit is total.
Recommended Reading
If you found this article useful, these books go deeper into the same topics. Each title is hand-picked for the material covered above.
- Random Number Generation and Monte Carlo Methods by James E. Gentle — The deep reference on PRNG internals, including Mersenne Twister and CSPRNG construction. View on Amazon
- Applied Cryptography: Protocols, Algorithms, and Source Code in C by Bruce Schneier — Classic treatment of cryptographically secure random number generation. View on Amazon
As an Amazon Associate, Roulety earns from qualifying purchases. This does not change the price you pay and helps support the writing on this site.
© 2026 Roulety. Free online spinner wheel for decisions, games, and fun.