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.
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.wasmis 418,103 bytes.model.uchen-paramsis 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 bytesNot 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.
Check your inbox
If that address is new here, a confirmation link is on its way. Click it to finish subscribing.
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.