Math.random() vs crypto.getRandomValues(): Why the Difference Matters
Last updated: August 12, 2026
If you have ever written "Math.random()" in JavaScript to get a random number, you used a pseudorandom generator — not a truly random one. The difference between that and crypto.getRandomValues() looks like a technicality, but it has real consequences for security, fairness and reliability that are worth understanding, especially if you build something other people will use.
What a pseudorandom generator (PRNG) really is
Math.random() does not produce randomness "out of nowhere". Internally it uses a deterministic algorithm (in most modern JavaScript engines, a variant of xorshift128+) that starts from an internal state — a seed — and produces a sequence of numbers that looks statistically random but is completely reproducible if you know the algorithm and the initial state.
This is not an implementation flaw: it is exactly what it was designed for. A PRNG is fast, efficient, and perfect for cases where "visual randomness" is enough — animations, visual effects, non-critical data sampling, simple games with no real stakes. The problem appears when someone uses it where fairness or unpredictability are security requirements, not just appearance.
What changes with crypto.getRandomValues()
The Web Crypto API, specifically crypto.getRandomValues(), does not depend on a mathematical formula with predictable internal state. It draws its entropy from operating-system sources — hardware random number generators, system event timing noise, and other physical sources that are, in practice, impossible to reproduce or predict from the outside.
It is the same underlying technology browsers use to generate cryptographic keys, secure session tokens, and other values where a successful prediction by an attacker would have real consequences. That is why it is the recommended standard for passwords, tokens, raffles, and any case where "someone could try to predict or manipulate the result" is a real threat.
When to use each one in practice
Use Math.random() when speed matters more than cryptographic fairness and the consequences of a "not perfectly random" result are low: animations, random positions of decorative elements, shuffling a personal playlist, quick prototypes.
Use crypto.getRandomValues() (or crypto.randomUUID() for identifiers) when the result has real consequences: passwords, session or API tokens, prize raffles, any decision where a participant could benefit from predicting or manipulating the outcome, or simply when you want to be able to say with technical certainty that your process is fair.
All the generators on this site use crypto.getRandomValues() or crypto.randomUUID() for exactly this reason — not because Math.random() is "wrong", but because for raffles, passwords and decisions people take seriously, the bar for fairness should be higher.
Frequently asked questions
- Is Math.random() insecure?
- It is not "insecure" for its original purpose (visual, non-critical randomness), but it is inappropriate for passwords, security tokens or raffles where predictability matters.
- Is crypto.getRandomValues() slower?
- It is marginally slower than Math.random(), but the difference is imperceptible to the user in almost any use case — the extra security is worth that minimal cost.
- Can I "seed" crypto.getRandomValues() to get reproducible results?
- No, and that is precisely the point: unlike a seeded PRNG, you cannot reproduce the sequence, which is a security feature, not a limitation.
- Does Node.js have an equivalent?
- Yes, Node.js's crypto module offers randomBytes() and randomUUID() with the same cryptographic guarantees as the browser's Web Crypto API.