How Random Is "Random"?
The Mathematics Behind WheelNamer

WheelNamer · Published 2025

Press a button. A wheel spins. A coin flips. A decision is made in an instant. But what is actually happening under the hood — and how can we prove it is genuinely fair?

Want to just run the test and see the results for yourself?

For the math teachers, the boffins, the scientists, and insomniacs looking for a good read to fall asleep to, the full mathematics, step-by-step proofs, and all the good stuff awaits below.

Skip straight to the mathematics ↓
Live randomness proof simulator
Spins run
Outcomes
Expected each
Max deviation
from fair %
Chart 1 — the raw random value behind every spin
Every dot is the raw number Math.random() produced — a value between 0 and 1. The coloured bands show which outcome each region maps to. A fair source fills every band evenly.
Chart 2 — running percentage per outcome (watch the convergence)
Each line tracks one outcome's running percentage. Early on the lines are noisy — small samples always look uneven. As spins grow, every line converges on the dashed fair line.
Chart 3 — final outcome counts vs expected (y-axis from zero)
The y-axis starts at zero so you see true proportions. At 100,000 spins the bars are visually indistinguishable — which is exactly what a fair source predicts. The dashed line marks the expected count; deviation labels show the precise difference.
The mathematics behind the results

Why can we simulate 100,000 spins in seconds?

This question catches people off guard, so it is worth answering before anything else.

When you spin a wheel on WheelNamer, two things happen that look connected but are mathematically separate:

What happens on every spinStep 1 — the result is calculated. This takes roughly 0.00001 seconds.
Step 2 — the animation plays. This takes 8–12 seconds.

The animation and the decision are separate things. The moment you click spin, the computer generates a random number and maps it to an outcome — this is the act of randomness itself. The wheel then spins for the full duration, giving that decision a satisfying visual form. Nothing is rigged or predetermined; the randomness is genuine and happens before the wheel stops.

A simulation skips the animation entirely. Instead of spin → wait → record → repeat, it does generate number → record → repeat, essentially instantly. A modern computer can call Math.random() roughly 100 million times per second. So 100,000 simulated spins takes about one millisecond.

Key pointThe simulator uses the identical decision logic as a real spin. The random number that determines the outcome is exactly the same whether or not an animation plays afterwards.

The engine: one number decides everything

Every random outcome on WheelNamer — Yes/No, Heads/Tails, or any wheel — begins with a single function call:

Math.random()

This returns a decimal number between 0 (included) and 1 (not included). Every value in that range is equally likely. That single number is then mapped to an outcome.


Yes / No — step by step

The question: how do we guarantee each outcome is exactly 50%?

let r = Math.random()   // r is somewhere between 0 and 1

if (r < 0.5) {
    result = "Yes"
} else {
    result = "No"
}

Picture the range 0 to 1 as a ruler, one metre long. The condition r < 0.5 covers the left half (0 to 0.5). The condition r ≥ 0.5 covers the right half (0.5 to 1). Both halves are exactly the same length.

0 ──────────────── 0.5 ──────────────── 1
│        Yes        │         No        │
│       50.00%      │       50.00%      │
P(Yes) = 0.5 ÷ 1.0 = 50%
P(No)  = 0.5 ÷ 1.0 = 50%
Total  = 100%  ✓

No magic. Just geometry on a number line.


Heads / Tails — identical logic

Heads/Tails uses the exact same binary split. The coin animation — the spin, the arc, the landing — plays out in full while the outcome has already been determined by the random number generated the moment you clicked.


Three equal options — a three-way split

let r = Math.random()

if      (r < 0.3333) { result = "A" }
else if (r < 0.6667) { result = "B" }
else                 { result = "C" }
0 ─────── 0.333 ─────── 0.667 ─────── 1
│    A    │     B      │     C      │
│  33.33% │   33.33%   │   33.33%   │
P(A) = 0.3333 ÷ 1.0  =  33.33%
P(B) = 0.3333 ÷ 1.0  =  33.33%
P(C) = 0.3333 ÷ 1.0  =  33.33%
Total = 100%  ✓

The wheel — any number of segments

let r = Math.random()
let result_index = Math.floor(r * numberOfSegments)

Multiplying by n stretches the ruler from 1 to n units, and Math.floor chops it into n equal integer buckets. Each bucket is length 1 out of n total, so P = 1/n exactly. For any number of equal segments, every entry always has the same probability.

Weighted segments

If segments have different sizes — say one option has twice the arc of another — the probability is simply that segment's weight divided by the total of all weights. The mathematics is identical in every case: geometry on a number line, with boundaries set to match the intended probabilities exactly.


What generates the number?

Math.random() uses an algorithm called xorshift128+, which Chrome, Firefox, Edge, and Safari all implement. It works in three steps:

  1. Seed — a starting number drawn from unpredictable system sources: CPU timing down to nanoseconds, the OS entropy pool.
  2. Transform — a series of fast bit-manipulation operations converts the seed into an output number.
  3. Chain — that output becomes the seed for the next call.

The sequence will not repeat until 2¹²⁸ calls have been made — at 100 million calls per second, cycling through the full sequence would take roughly 10²² years.

Is it 'truly' random?Technically it is pseudo-random: deterministic if you know the seed. But since the seed comes from unpredictable hardware noise, the output is indistinguishable from true randomness in practice. xorshift128+ passes all 106 tests in the TestU01 BigCrush battery — the industry standard for detecting any statistical pattern whatsoever.

The Law of Large Numbers

The more trials you run, the closer your observed results get to the true probability. For a fair coin (P = 0.5), the expected deviation after n trials is σ = 0.5 / √n:

n =     100:  σ = ±5.0%
n =   1,000:  σ = ±1.6%
n =  10,000:  σ = ±0.5%
n = 100,000:  σ = ±0.16%

This is why Chart 3 in the simulator looks like two identical bars at 100,000 spins. They are not identical — one might be 50,083 and the other 49,917 — but at that scale, 0.16% is visually indistinguishable from zero. That is the proof of fairness.


The chi-squared test — formal proof of fairness

The chi-squared (χ²) test asks: "Are these results consistent with what a truly fair source would produce?" Any result below the critical value means there is no statistically significant evidence of bias — the observed deviation is consistent with natural random variation.

Important nuanceA fair source will fail this test roughly 5% of the time — that is baked into the 95% confidence threshold. A generator that always passed would itself be suspicious, because perfect results are actually less likely than slightly-off results.

Why each spin is independent

The random number generator has no usable memory of past outputs. Landing on "Yes" ten times in a row does not make "No" more likely. The probability resets to exactly 50% for every single spin. This is the memoryless property, and it is mathematically guaranteed by the structure of xorshift128+. There are no hot streaks. No due outcomes. No patterns to detect or exploit.


Summary

FeatureMethodCalculationTrue probability
Yes / Nor < 0.50.5 ÷ 1.050% each
Heads / Tailsr < 0.50.5 ÷ 1.050% each
Three equal optionsfloor(r × 3)0.333 ÷ 1.033.33% each
Wheel (n equal segments)floor(r × n)1 ÷ n1/n each
Wheel (weighted segments)proportional walkweight ÷ totalweight / total
The key takeawayThe randomness is real, it happens the instant you click, and the animation gives it a visual form. The mathematics is provable — and with enough spins, you can measure it yourself using the simulator at the top of this page.

WheelNamer uses browser-native Math.random() (xorshift128+) seeded by OS entropy on every page load. No server-side influence. No patterns. No memory between spins.