Fair Wheels Use crypto.getRandomValues(), Not Math.random()
A look at unbiased selection on a spinning decision wheel — rejection sampling, seedless fairness, and a free tool you can try today.

Every "spin the wheel" widget you've seen has two halves, and only one of them matters. The animation — easing curves, tick sounds, confetti — is theater. The selection is math. If the math is wrong, the theater is a lie wearing confetti.
Most wheel implementations pick like this:
segments[Math.floor(Math.random() * segments.length)]
For a yes/no decision wheel this is fine. Math.random() is deterministic and theoretically predictable, but nobody is attacking your lunch decision. The problem starts when the wheel picks giveaway winners, assigns on-call rotations, or does anything where someone can lose something. Then "almost uniform" stops being good enough, and modulo bias — the slight unevenness of Math.floor(Math.random() * n) for certain n — becomes a real, if small, unfairness.
Rejection sampling in 15 lines
The Web Crypto API exposes OS-level entropy via crypto.getRandomValues(). Turning random 32-bit integers into a uniform index in [0, n) without modulo bias is a textbook rejection sample:
function fairIndex(n) {
const max = 0xffffffff;
const limit = max - (max % n); // reject the biased tail
const buf = new Uint32Array(1);
let x;
do {
crypto.getRandomValues(buf);
x = buf[0];
} while (x >= limit);
return x % n;
}
Values at or above limit would map unevenly onto the segments, so we throw them away and draw again. The expected retry count is ~1.0 — the loop is practically free, and the distribution is exactly uniform.
Architecture: selection before animation
Clean wheel code separates the two halves completely:
- Select the winning index with
fairIndex()— pure function, unit-testable. - Derive the final rotation angle from the index (a few full turns for drama, then land mid-slice).
- Animate to that angle with CSS transitions.
Because selection is decoupled, you can verify fairness offline: run 100,000 draws, histogram the results, and confirm uniformity before any pixel moves. If you're building any picker — wheels, lotteries, shuffles — this test is cheap and catches real bugs (off-by-one slices, rounding in angle math, you name it).
Seedless, serverless, stateless
The whole pipeline runs client-side. There's no seed to protect, no server to trust, and — relevant if your wheel handles entrant names — nothing uploaded anywhere. For a small giveaway tool, "we never see your list" is the simplest privacy policy there is.
Disclosure
I built a free wheel implementing exactly this at toolkitloop.com/yes-no-wheel — yes/no mode plus custom segments, no signup. It's one of 30 client-side tools I maintain at ToolkitLoop. The patterns above are general; adapt them freely.
If you've ever audited randomness in a picker, shuffler, or A/B splitter — what did you find? The subtle bugs are always more interesting than the obvious ones.

