Skip to content
AFM Studio
Personal ProjectGame TheoryData Visualization

Mind Reader

A browser game where you press left or right and a machine tries to guess your next press before you make it — reconstructing two real 1950s Bell Labs machines from their original papers and staging their historical rematch, while measuring exactly how you fail to be random

All projects4 min read

Role

Solo Developer

Timeframe

Sep 2026

On this page

The problem

Matching pennies has a provably optimal strategy: flip a fair coin. Do that and you tie at 50% against any opponent, forever. The counter-strategy is published, simple, and known to the player — and they still can't execute it. The gap between "the correct move is known" and "you cannot actually make it" is the entire product.

This is a portfolio and explanatory artifact rather than a commercial one, built around that gap. It's also a historical reconstruction: two real relay-based prediction machines from Bell Labs — Hagelbarger's SEER (1956) and Shannon's simpler MRM (1953) — have published designs but neither had been built as an interactive object anyone could actually play against. The build brief explicitly ruled out the easy version of this idea, a single n-gram predictor of the kind that already exists in several public implementations, as "not a portfolio piece" — the historical reconstructions, the historical machine-vs-machine rematch, a five-model ensemble, and a personal predictability portrait all had to exist in the first version.

The approach

The prediction is locked before the input is read, and the type system enforces it

This is the app's entire credibility. A sealing function produces a frozen prediction object; resolving that seal against the player's actual move takes the seal as an argument rather than reading it from internal state, so there's no code path that could compute a prediction after seeing the press. The interface mirrors this structurally — the input handler for the player's move isn't even attached until a seal exists. Then it goes one step further: a separate verification routine re-seals the player's entire session from a fresh machine fed only the historical presses, and the app displays that audit on screen, so the integrity claim is checked in front of the player rather than merely asserted.

The machine sees press history and nothing else — enforced by the function signature itself

Every predictor's input type is a plain list of past moves; there's nowhere to pass timing, tap position, or coordinates even if someone wanted to. The build brief treats reaction time as a genuinely strong signal whose silent use would betray the whole premise, so the constraint lives in the type rather than in a comment asking nicely.

The honesty mechanism is a confidence floor

When the ensemble of five predictors isn't confident, the machine draws its move from a seeded random generator instead of playing its actual guess — a technique taken directly from the real 1950s machines. This is what caps the app at 50% against a genuinely random opponent, turning what would otherwise be a deterministic and therefore reverse-engineerable machine into an honest mixed strategy. A fairness gate plays 100,000 rounds against a seeded coin in CI and asserts convergence to 50% before any deploy — and the build rule is explicit: if that test fails, don't tune it until it passes, because a machine that beats a fair coin has a bug that is almost certainly leaking the future into its prediction, and the same bug would be silently inflating its score against real people too.

A historical-fidelity gate, not just a fairness gate

A second test runs the Shannon and Hagelbarger reconstructions against each other for thousands of rounds and asserts the historically simpler machine finishes ahead — reproducing the documented 1950s result. Both machines were built from the original 1953 memorandum and 1956 paper directly, not from a modern summary of them, and every implementation detail those primary sources leave genuinely unresolved is written down rather than guessed at confidently.

Outcome

Live: five predictors run simultaneously against every player, each individually visible with live-updating weights — switch your strategy mid-session and you can watch a different model visibly take over the ensemble's vote in real time. A predictability portrait measures six things about how you specifically fail to be random: win rate with proper confidence intervals, switch rate against the fair 50%, your run-length distribution against what a truly fair coin would produce, conditional entropy, serial correlation, and which specific press patterns you overproduce. The two historical machines play a staged rematch against each other, reproducing the documented result that Shannon's simpler design beat Hagelbarger's more complex one.

Sole author, 64 commits over 8 days, roughly 12,000 lines of code. Two runtime dependencies — React and its DOM renderer — with every predictor, every one of six measurement charts, and both historical reconstructions written from scratch. Input-to-reveal latency stays under 80 milliseconds by sealing the next round's prediction immediately after the current one reveals, so the machine is never visibly computing while a player waits. The machine reports what it measures and never taunts — not one line of copy gloats or comments on a player's performance, because a machine that says "gotcha" is a toy, and a machine that silently reports 68% is unsettling in the right way.

Outcomes

Predictors running live against you, including two 1950s machines rebuilt from primary sources
5
Rounds played against a coin in CI — the machine must converge to 50% or the build fails
100,000
Input-to-reveal latency, so the machine is never visibly thinking
<80ms
Runtime dependencies — react and react-dom, nothing else
2

Screenshots

Beranda
The Protocol
The Ensemble
The Machine Settings
The Portrait
The Strategy Lab

Have a project like this?

If you need a system built with the same care — clear scope, solid execution — let's talk.

Start a project