Randomness method guide

Web Crypto randomness: what a browser wheel can and cannot prove

Web Crypto supplies strong random bytes from the browser, but bytes are only the start of a trustworthy selection pipeline. Correct range mapping, shuffling, weighting, input rules, failure behavior, and modest claims all matter to the result people actually use.

For
Developers, auditors, teachers, event operators, and anyone evaluating a browser randomizer
Reading time
11 minutes
Reviewed

Step 1

Start with the browser's cryptographic random source

A browser selection tool can request random bytes with crypto.getRandomValues in a secure context. The browser and operating system are responsible for seeding and maintaining that source. The application should use those bytes directly through a documented selection algorithm rather than deriving choices from timestamps, animation frames, pointer movement, or a predictable pseudo-random helper.

Secure delivery matters because an attacker who replaces the page can replace the selection code as well. HTTPS and a trusted deployment reduce that risk, but they do not prove that a specific past result came from an unchanged build. Code review, release controls, and independent observation answer different questions from the quality of the random byte source.

Step 2

Fail closed when secure randomness is unavailable

If the Web Crypto API is absent or throws an error, the tool should stop the draw and explain that secure randomness is unavailable. Falling back to a weaker source keeps the interface moving but silently changes the method the user relied on. That is especially dangerous when the page continues to display the same fairness language.

A good failure can be retried after reloading in a supported, secure browser. It should not generate a placeholder winner, reuse a previous buffer, or let the visual animation decide. Operators should have a documented alternative method for live events instead of learning about the browser requirement during the official draw.

Step 3

Map random bytes into a bounded range without modulo bias

A random unsigned integer does not always divide evenly into a requested range such as 0 through 9. Taking the raw value modulo 10 makes some outputs slightly more likely when the source range has a remainder. The difference is small per draw, but it is avoidable and violates the promise of equal discrete chances.

Rejection sampling calculates the largest source interval that is an exact multiple of the desired range. Values outside that interval are discarded and sampled again; accepted values can then be reduced safely. The implementation must also validate integer bounds, avoid overflow, and handle a one-value range without an unnecessary loop.

Run it in this order

  1. Validate that the requested span is a supported positive integer.
  2. Calculate an acceptance limit divisible by that span.
  3. Request a fresh unsigned value until it falls below the limit.
  4. Reduce the accepted value into the requested range.

Step 4

Build shuffles, weights, and no-replacement draws on the bounded primitive

A Fisher-Yates shuffle is uniform when each swap index is drawn uniformly from the correct inclusive range. Picking every output independently is not a shuffle because it can duplicate or omit entries. A no-replacement sample can use part of a shuffle or remove each accepted item from the active population, provided duplicate rows remain distinct entries when that is the stated rule.

For weighted selection, validate finite positive weights and draw over their total using enough numeric precision for the supported limits. The probability belongs to an entry's weight divided by the active total; after a no-replacement winner is removed, the next probabilities change. Tests should cover boundaries, duplicate labels, extreme valid weights, and repeated distribution experiments without mistaking a statistical smoke test for a proof.

Check before continuing

  • Every bounded index comes from rejection sampling.
  • Fisher-Yates uses the shrinking unsorted range.
  • No-replacement removes the exact selected entry.
  • Weights are visible, finite, positive, and included in the active total.

Step 5

Separate the selected result from the animation

The application should select a result from the data model and then animate toward that result. Pointer position, frame timing, and CSS easing are presentation details, not entropy sources. This separation makes reduced-motion output possible and lets tests verify selection independently of rendering speed or device performance.

The visible wheel must still agree with the data model. Segment labels, active counts, weights, pointer alignment, result dialog, elimination, history, and undo should all refer to the same selected entry. A sound random integer does not rescue an interface that announces or removes a different row.

Step 6

State the assurance boundary instead of promising absolute fairness

Web Crypto and unbiased algorithms address how an index is selected from the active inputs. They do not verify that the roster is complete, duplicates are legitimate, weights match the rules, the operator spun only once, or an organization complied with contest law. Those controls belong to preparation, observation, recordkeeping, and governance.

A local browser result is not automatically tamper-evident, independently witnessed, reproducible, or certified. Describe the implemented method, publish practical limits, retain relevant test and release evidence, and recommend stronger independent oversight for regulated or high-value decisions. Precise, reviewable claims are more useful than calling any finite software process perfectly or truly random.

Use the right surface

Tools mentioned in this guide

Common questions

Questions about web crypto randomness

Is crypto.getRandomValues the same as encryption?

No. It is a browser API for obtaining cryptographically strong random values. A wheel uses those values as selection input; it is not encrypting the participant list simply because the API name contains crypto.

Why not use a remainder operation for every random range?

The source integer range may not divide evenly by the requested range, which gives some outcomes an extra source value. Rejection sampling discards the uneven tail before reducing the accepted value.

Does Web Crypto make every raffle fair?

It can support unbiased selection from the active entries. Fairness also depends on correct eligibility, duplicates, weights, replacement rules, operation, verification, and any legal or independent oversight.

Can the spinning animation affect the result?

It should not. A robust design selects with Web Crypto first and animates toward the chosen entry, allowing rendering and reduced-motion behavior to remain presentation-only.

What should happen if Web Crypto fails?

Stop and show an actionable error. Do not silently substitute a predictable source; retry in a supported secure browser or use the predeclared fallback process.