Beat the model running on this page.

Play a convolutional network running in this tab — most territory when the board fills wins.

Five residual blocks, 128 channels, seven output heads: 418 kB of WebAssembly and 5.5 MB of weights, downloaded once when this page loaded. Every turn it takes costs dozens of runs through that network, all of them on your CPU — nothing about this game goes back to a server.

Skip the game

Play the model

Interactive demo: a neural network trained in C++, compiled to WebAssembly, playing the territory game Dots on a 24 by 24 board entirely in this browser tab. The game announces the opponent's moves and captures as you play. Placing a dot currently requires a mouse or touch. Everything the model is, and what it cannot do, is written out below.

Built by Eugene Ostroukhov. How it works · About & contact

No GPU. No server. Shouldn't work.

A 2.76-million-parameter network is playing you from one CPU thread, with no graphics card, inside this tab. And it does not run that network once per turn — it runs it 33 times before it plays a single dot, searching a tree of possible futures and evaluating the board at every one it visits.

Every number below is something you can check yourself — a byte count in the network panel, a timing in the widget's own footer.

Play a move. The footer above times the whole turn on this device — all 33 forward passes, the tree search that decides which positions to spend them on, and one worker round-trip. Divide what it prints by 33 and you are back at the number above. None of it leaves the tab.

How it is built: the model is a constexpr variable · how the forward pass got 6.45× faster · the SIMD kernels underneath

What you're playing against.

Architecture
A 128-channel residual tower — a stem and five residual blocks, the middle one global-pooling — with seven heads off one shared trunk: policy, value, a 257-bucket score distribution, per-cell ownership, capture-completion, opponent policy, and Q-init.
Training
Gumbel-MCTS self-play, run as a Bazel build step and cached like any other build action.
Search
The difficulty menu is the MCTS budget and nothing else — 8, 16, 32, 64, 96 or 128 simulations per move, and each simulation is one more full forward pass through the network. It starts at 32, so a normal turn is 33 evaluations; the top setting is 129. Same weights at every setting — the knob buys search, not a bigger model.
Bytes
dots.wasm is 418,103 bytes. model.uchen-params is 5,520,836 bytes. That is the whole opponent — read the Size column in your network panel, not Transferred, which depends on compression.
Runtime
C++20 inference compiled to WebAssembly by the same toolchain that builds native. Once the page has loaded, a move costs zero network requests: kill your wifi mid-game and nothing changes. Reload and you will need the connection back — there is no service worker.
Game data
Your moves and the board state never leave the tab, because there is no endpoint to send them to.

Provenance

The checkpoint string is plain text at offset 64, in the file's 128-byte metadata block. Ahead of it sits a 64-byte header: the Uchen010 magic, then the payload length, a record count, the metadata size, and padding. Fetch the first 192 bytes and read it. There is no dtype field — the payload is 5,520,644 bytes for 2,760,322 parameters, which is what makes it fp16. The two sizes are the Size column in your network panel. The architecture name is not in either file; it is compiled in, and the game's own footer prints it.

checkpoint: m1_fork8_prod/iter2/s2 2026-07-27 fp16
dots.wasm: 418,103 bytes
model.uchen-params: 5,520,836 bytes

Not a superhuman engine.

32 simulations per move, by default. Engines built to be superhuman search thousands or millions. The menu goes to 128, and that is the entire difficulty knob: more search, more time, identical weights.

There is no opening book and no endgame solver behind it. What you are playing is one checkpoint of a training run — a snapshot, not a ceiling.

5.5 MB of weights, five residual blocks. This is sized to be compiled into an application, not to top a leaderboard. Turn the difficulty up if it is too easy.

One list, and it's quiet.

The last post here went up in November 2024, so I'm not going to promise a cadence I haven't kept. What I will send: a new demo when there's one to play, and the day the framework is something you can actually build. Nothing else.

Double opt-in — I'll send a confirmation email first. Unsubscribe in one click.