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 move it plays is searched 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
418kB of engine and 5.5MB of weights.
Ready to go.
Both files come down once, when the page loads. After that a move costs zero network requests — kill your wifi mid-game and nothing changes.
What you're playing against.
- Architecture
- A 128-channel residual tower — a stem and five ResBlocks — with seven heads off one shared trunk: policy, value, a 129-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. It starts at 32. Same weights at every setting.
- Bytes
dots.wasmis 418,103 bytes.model.uchen-paramsis 5,520,836 bytes. That is the whole opponent — open the network panel and count.- 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 in the parameter file's header, right after the Uchen010 magic — fetch the file and read the first bytes. The two sizes are 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.