Web Crypto getRandomValues: A Reference
What the browser API guarantees, what it does not, and the limits worth knowing.
`crypto.getRandomValues()` is the browser's cryptographic random source. It is synchronous, available in every current browser, and has a handful of limits and behaviours worth knowing before you build on it.
This is a practical reference rather than a cryptography lesson: what it takes, what it returns, what it refuses, and what it does not promise.
Everything below is observable behaviour of the API as specified, not implementation detail of any one browser.
The shape of the call
You pass in a typed array and it fills it in place with random values, then returns the same array. It does not allocate anything for you, and it does not return a number — the argument is both the input and the output, which catches people out on first use.
It accepts integer typed arrays: Uint8Array, Int8Array, Uint16Array, Int16Array, Uint32Array, Int32Array, and the BigInt64 variants. It does not accept Float32Array or Float64Array, and it throws if you pass one, because a float array of uniformly random bits would contain values that are not meaningfully uniform as numbers.
That last restriction is the source of a common workaround: to get a random float, fill a Uint32Array and divide by 2^32. That is what this site does, and it is the standard approach.
- Fills the array in place; the return value is the same array.
- Accepts integer typed arrays only — Float32Array and Float64Array throw.
- Synchronous, with no promise and no callback.
- Available in every current browser and in Node under `crypto.webcrypto`.
The 65,536-byte quota
The specification caps a single call at 65,536 bytes. Passing a larger array throws a QuotaExceededError rather than filling part of it, which is the right behaviour — a partially filled buffer of random values is a much worse failure than an exception.
For a Uint8Array that is 65,536 elements; for a Uint32Array it is 16,384, since each element is four bytes. Anything larger has to be filled in chunks in a loop, which is straightforward and is the thing people discover the first time they generate a large key stream.
The limit exists to stop a page draining the entropy pool with a single enormous request. In practice almost nothing interactive comes close to it — a hundred thousand coin flips at one byte each would need two calls.
Filling a large buffer
- Uint8Array: maximum 65,536 elements per call.
- Uint32Array: maximum 16,384 elements per call (4 bytes each).
- Larger buffers: loop in chunks of at most 65,536 bytes.
- Exceeding the cap throws QuotaExceededError — it does not silently truncate.
Why it is synchronous
Unlike most of the Web Crypto API, `getRandomValues` is synchronous. Every other operation in `crypto.subtle` returns a promise, and this one returns immediately, which looks inconsistent until you consider what it is doing.
The reason is that it does not block on entropy. The underlying generator is seeded once from the system entropy pool and then produces output algorithmically, so a call is a fast computation rather than a wait for physical randomness to accumulate. Making it asynchronous would impose a promise on every caller for no benefit.
This differs from some older system interfaces that genuinely could block waiting for entropy. Modern operating systems seed once at boot and do not block afterwards, which is what makes the synchronous API reasonable.
What it does not promise
It does not promise a specific algorithm. The specification requires a cryptographically strong source and leaves the implementation to the platform, so different browsers on different operating systems use different generators. Your code cannot depend on any particular sequence.
It does not promise reproducibility, and cannot be seeded. There is no way to make it produce the same sequence twice, which is correct for its purpose and is why a seeded generator has to be a separate thing.
It does not promise a uniform distribution over an arbitrary range. It gives you uniform bytes or uniform 32-bit integers; converting those to a range from 1 to 6 without introducing bias is your problem, and the standard solution is rejection sampling.
- No specified algorithm — platforms differ, and your code must not depend on the sequence.
- No seeding, and therefore no reproducibility. That is by design.
- Uniform over the type's full range only; narrowing to a custom range needs rejection sampling.
- No guarantee about performance under extreme load, though it is fast in practice.
The related APIs
`crypto.randomUUID()` generates a version 4 UUID as a string, using the same underlying source. It is the right call when you specifically need a UUID, and it saves you from the surprisingly fiddly business of setting the version and variant bits correctly by hand.
`crypto.subtle` covers the rest of Web Crypto — hashing, signing, key derivation — and is asynchronous. `crypto.subtle.digest` is what you would use to publish a commitment hash for a provably fair draw, which is a natural companion to a seeded generator.
In Node the same interface is available as `crypto.webcrypto.getRandomValues`, and Node additionally exposes `crypto.randomBytes` and `crypto.randomInt`, the latter of which handles rejection sampling for you.
Frequently asked questions
What does getRandomValues return?
The same array you passed in, filled with random values. It works in place rather than returning a new value.
Why does it reject Float32Array?
Because filling a float array with random bits produces values that are not meaningfully uniform as numbers, including NaNs and infinities.
How do I get a random float?
Fill a Uint32Array with one element and divide by 2^32. That is the standard approach and gives a uniform value in [0, 1).
What is the size limit?
65,536 bytes per call — 65,536 elements for a Uint8Array, 16,384 for a Uint32Array. Larger buffers must be filled in chunks.
What happens if I exceed it?
It throws QuotaExceededError. It does not partially fill the array, which would be a far worse failure.
Why is it synchronous when the rest of Web Crypto is not?
Because it does not block on entropy. The generator is seeded once and then produces output algorithmically, so a call is a fast computation.
Can I seed it for reproducible results?
No, and that is deliberate. For reproducibility you need a separate seeded generator, which is a different tool for a different job.
Does it give me a uniform number in a custom range?
No. It gives uniform bytes or integers over the full type range; narrowing to 1-to-6 without bias requires rejection sampling on your side.
Tools that use this
Related guides
Pseudo-Random vs Cryptographic Randomness
Two generators, two guarantees, and the surprisingly small list of cases where the difference bites.
Entropy and Password Strength
Bits of entropy, in plain terms, and why a longer random password beats a cleverer one.
Which Randomness Each Tool Here Uses
A tool-by-tool table of the exact draw behind every generator on this site.