The Verification Venue · pointed at the coin your browser flips
The Seam in the Browser's Coin
Flip any coin enough times and you stop asking whether it is fair and start asking what it is made of. The recurrence below, stated in full so you can check every line of it, has a seam: its lowest output bit is an exactly linear function of the 128-bit state, which means a few hundred low bits determine all the rest, forever. This page recovers that seam live, predicts a thousand future bits before generating them, and then runs a test past ten billion draws to show why passing a small randomness battery proves so little.
The recurrence, in full. Two 64-bit state words s0, s1; one step is:
Why the low bit is linear: a left shift moves nothing into bit 0, XOR and addition agree in bit 0, and nothing in the step ever carries into bit 0 from below (with a, b the pre-step words). Walk bit 0 through the step: after x ^= x << 23 it is b0; after x ^= x >> 17 it has gained b17; the next two lines add a0 and a26; and the final output = x + b adds b0 once more, cancelling it. The output's low bit is b17 XOR a0 XOR a26, three named state bits, at every step, with no carry ever involved. That makes the low-bit stream a linear recurring sequence over GF(2) of order at most 128.
Bits generated (live, in this tab)
0
plain JavaScript, the stated recurrence, nothing hidden
They look random
they are not
512 of them suffice to predict all the rest; the check panel does it
Any seed. The linearity is a property of the recurrence, so it holds for every one of them.
New bits per frame. Pause below if you want to stare.
What the engine does next is bigger than a browser tab has ever been asked to do on this ground. A C engine compiled to WebAssembly, driven by a pool of Web Workers across every core you have, generates up to ten billion outputs of the stated recurrence, jumping each chunk to its starting index by matrix powers over GF(2), and accumulates a birthday-style collision statistic on windows of the low-bit stream and, separately, of the top-bit stream, at rungs of 10⁴ through 10¹⁰ draws. You launch it; nothing runs until you press the button.
waiting for the checks below to pass before anything heavy runs
| rung | draws | low balls | low repeats | p (low) | high repeats | p (high) |
|---|
An interrupted run is not a result. The totals above are PARTIAL: the run stopped before finishing, and nothing on this page treats them as the answer.
running Berlekamp-Massey over 512 low bits…
…
first 64 predicted low bits
…
first 64 generated low bits (same positions)
…
…
The check · every number recomputed in front of you
Both implementations (the compiled engine and a slow, obvious JavaScript reference written from the contract) run a battery of small instances right here, before any headline number is shown. The sabotage control deliberately breaks the reference and requires the comparison to go red. The byte hash is of the engine this page actually fetched. The reproducible rebuild from the committed C is an offline check: your browser cannot compile C, so it runs in node research/the-seam-in-the-browsers-coin/verify-the-seam-in-the-browsers-coin.mjs.
| battery row | engine fingerprint | reference fingerprint | agree |
|---|
hashing engine.wasm…
computing the state-update matrix's characteristic polynomial two ways…
…
not yet run
What none of this rules out: two chunkings agreeing rules out boundary errors and a wrong jump-ahead and almost nothing else, and what they agree on is the stream and the ball values, not the collision counts, which are scoped to a chunk and so belong to the partition rather than to the rung; the divisibility and prediction checks rule out a wrong recurrence but not a mis-stated one, since all three implementations share the statement on this page (the divisibility claim pairs the characteristic polynomial with the REVERSED form of Berlekamp-Massey's connection polynomial, which is its monic minimal polynomial; the connection polynomial itself is the predictor form and divides nothing here); the prefix identity shown here is a fold (a hash) of the first 4096 outputs taken with the same p1 on both sides, with the byte-for-byte, output-for-output stitch of the chunked stream done by the offline verifier over those same 4096 outputs and no further; and every p-value here is a normal approximation, not an exact tail.
Operations performed (engine's own counter)
·
counted inner-loop steps, never an estimate
Wall time / workers
·
·
main-thread comparison not yet measured
What's exactly true, what's a model, and what this page refuses to claim
Exactly true. The recurrence as stated; the bit-0 algebra (the output's low bit is b17 XOR a0 XOR a26 of the pre-step words); the recovered polynomial's degree and taps; the divisibility of the recovered linear model's minimal polynomial (the reversal of Berlekamp-Massey's connection polynomial) into the state-update matrix's characteristic polynomial (which is computed two independent ways and verified by Cayley-Hamilton on every basis vector); the prediction counts; the ladder totals; the jump-ahead identities.
Idealised. The p-values are a normal approximation to the occupancy null. The collision statistic counts repeats within chunks, with the null computed for exactly that scope. Window width is capped at 40 bits and balls at 2²⁴ per rung; where the collapse lands is printed from the algebra before the run, and if it lands beyond 10¹⁰ the page says so rather than pretending.
Refused claims. This page does not claim which generator your browser ships; engines have changed it before and may again. The Math.random probe below measures your tab's own ordinary random and reports what it finds, nothing more. This is not a security flaw in anything: exact linearity of the low bits is a known and intended property of a non-cryptographic generator, and the cryptographic source exists for the other job. The famous stories about broken generators refer to older, different defects; this page does not merge them into this one.