Randomness and RNG
Every modern browser ships a cryptographic randomness source: crypto.getRandomValues(). We trace where the bytes actually come from — the operating system's CSPRNG — and what the W3C spec does and doesn't promise.
When a web page needs randomness that can survive an adversary — a session token, an encryption key, or a lottery line someone might want to predict — it has exactly one correct primitive: crypto.getRandomValues(). Here is what it is, where the bytes come from, and why it is the foundation of our number generator.
crypto.getRandomValues() takes a typed array (for example Uint8Array or Uint32Array) and fills it with random bytes:
const buf = new Uint32Array(1);
crypto.getRandomValues(buf);
// buf[0] is now a uniformly random 32-bit integer, 0 to 4,294,967,295
The function is defined by the W3C Web Cryptography API specification. Two details from the spec are worth quoting rather than paraphrasing:
QuotaExceededError. This is a denial-of-service guard, not a quality limit — you can call the function as often as you like.Note the honest wording: the spec mandates the intent (cryptographic strength) but leaves the mechanism to the platform. That is a strength, not a dodge — it means every browser inherits decades of operating-system entropy engineering instead of reinventing it.
Follow the call down the stack and you land in the operating system's kernel:
getrandom(2) system call serves bytes from the kernel's random pool — the same source as /dev/urandom. The pool is a CSPRNG continuously reseeded from hardware events (interrupt timing, device noise, CPU hardware randomness where available); since kernel 4.8 its output stage is built on the ChaCha20 stream cipher (LWN's write-up covers the design).BCryptGenRandom, the Cryptography API: Next Generation primitive that fills a buffer from the system's preferred random algorithm.So a call to crypto.getRandomValues() in a page is, in practice, a thin bridge to the same generator the operating system uses to create your Wi-Fi session keys and TLS secrets. If that generator were predictable, HTTPS itself would be broken — the whole security economy is staked on it. That is a categorically different trust level from Math.random(), whose engine-level generator has a published state-recovery attack.
Three properties line up exactly with what a fair draw needs:
1. Unpredictability against observers. The OS pool is a CSPRNG in the strict sense (see the three tiers of random): reconstructing its state from outputs would require breaking modern cryptography. Nobody who watches our generator produce lines can improve their guess about the next line.
2. No seedable, no replayable. There is no API to seed crypto.getRandomValues() — neither the page, nor this site, nor an extension can set its starting point. For reproducible simulations that's a limitation; for fairness it's precisely the guarantee you want: we couldn't rig our own generator if we tried, and you don't have to take our word for the seed hygiene because there is no seed to mishandle.
3. Client-side and auditable. The call runs in your browser, on your operating system's entropy. The numbers never touch our servers, and the JavaScript that turns raw 32-bit words into lottery numbers is inspectable in your developer tools. The remaining engineering problem — mapping uniform 32-bit words onto a range like 1–45 without introducing bias — is real, and solving it wrongly quietly skews draws. That mapping problem and its fix are covered in modulo bias and rejection sampling, and the complete assembled design in how we built an unbiased generator.
Being exact matters, so here are the limits:
crypto.getRandomValues() is the only randomness primitive in the browser that is specified to be cryptographically strong and implemented on top of the operating system's CSPRNG. It is unpredictable, unseedable and universally available. That is why it is the first line of our generator's source code — and why any browser-based draw tool that uses Math.random() instead has already told you everything you need to know about its fairness engineering.
Last verified: 2026-08-29