Live · running in this tab

Watch it think.

The opponent is already thinking. Every method it calls to choose its move is on the right as it happens — a real trace, from the real library, running in this tab. You are O.

Its opening is the widest search in the game — nine candidates, half a million positions. Your reply is usually the narrowest: one tactic fires and the search never runs.

narrativetrace 0 calls
Also in your console — open DevTools and type nt.last()

What you just watched

Set the level to off and the game plays identically while the panel stays empty — with capture disabled the proxy calls the real method and returns. Click a square that is already taken and a failure narrates itself.

It is the real library

The tracer is NarrativeTrace™ for TypeScript itself, built from that repository and served from this domain — no CDN, no analytics, no network call of any kind. The opponent is ordinary code in demo-engine.js: a Referee that both sides play through, a Computer that decides, and the Tactics, MoveRanker, Minimax and TieBreak it decides with. Not one line of it knows it is being watched.

The shape is a decision

The opening search visits some 550,000 positions. You are seeing about a dozen lines, because the recursion lives inside one object and only calls that cross an object boundary are traced. Each Minimax.score line reports how many positions that one call really visited — the work that is not shown is counted rather than hidden.

The prose comes from the method

The italic line under each call is that method's narration template, resolved against its own arguments. In Java it is an annotation; here it is the same library function a decorator desugars to, because browsers ship no decorators and this site has no build step.