Chessko · WebAssembly
Running Stockfish in the browser with WebAssembly and Web Workers
Stockfish is written in C++. Chessko's two strongest levels run it inside your browser tab, with no server doing the thinking. This is how that works, and what it costs.
What WebAssembly is doing here
WebAssembly (WASM) is a compact binary format that browsers run at close to native speed. The Stockfish team's C++ is compiled to a .wasm file, and a small JavaScript loader instantiates it. Chessko ships Stockfish 19, lite single-threaded build, from the stockfish.js project: a 1.8 MB .wasm plus a 21 KB loader, with its NNUE network embedded in the binary.
In testing on a desktop Chrome, it searched roughly 700,000 positions per second and reached depth 20 from the starting position in about half a second. That is far past what a JavaScript engine of Chessko's size manages, which is the point.
Why the lite, single-threaded build
Stockfish.js comes in several builds. The full multi-threaded engine is about 94 MB and needs the page to be cross-origin isolated (special COOP and COEP headers so SharedArrayBuffer is allowed). That would constrain every other page on this site. The single-threaded lite build needs none of that, downloads in a moment, and is still stronger than any human player. The trade-off is that it is weaker than the full engine, and it uses one thread.
Talking to it: UCI over postMessage
The engine is a plain classic Web Worker that speaks UCI, the text protocol chess engines have used for decades. The page sends lines with postMessage and receives lines back:
uci → uciok
setoption name UCI_LimitStrength value true
setoption name UCI_Elo value 2000
isready → readyok
position fen <fen> moves e2e4 e7e5
go movetime 700 → info depth 14 score cp 21 … pv …
→ bestmove g1f3
Chessko's wrapper parses the info lines (depth, nodes, score in centipawns or mate distance, principal variation) and resolves a promise on bestmove. Because a search is a promise, “stop the old search when the position changes” is a single stop command and a discarded answer.
Loading it lazily
Nothing is downloaded until it is needed. The jelly levels never touch the WASM file. The first time you select a Stockfish level or press hint, the worker starts and the 1.8 MB file is fetched; selecting the level starts that download while you are still thinking about your first move. In local testing the engine answered readyok in about 190 ms. If the browser has no WebAssembly or Web Worker support, or the file cannot load, Chessko falls back to its own search at full effort and tells you so.
Strength limiting
Stockfish has a built-in limiter. With UCI_LimitStrength on, UCI_Elo makes the engine deliberately play worse, choosing weaker moves in a calibrated way instead of just searching shallowly. Level 6, Gelatitan, is capped at UCI_Elo 2000; level 7, Set in Stone, plays at full strength for 1.5 seconds a move. Note that UCI_Elo is Stockfish's own scale, calibrated against its own test conditions, not a rating from any rating list. The hint button always uses full strength, because a hint should be the best move, not a wobbly one.
Licence
Stockfish is free software under the GNU General Public License v3. The files served here are the unmodified stockfish-19-lite-single build from the stockfish npm package, with its licence text alongside. The corresponding source and build scripts are in the stockfish.js and Stockfish repositories, and the network was trained by Chris Bao. Chessko only talks to the engine over the UCI text protocol.
Want to try it? Pick Set in Stone.
Quick answers
- Can Stockfish run in a web browser?
- Yes. Stockfish can be compiled to WebAssembly and run in a Web Worker. Chessko uses a 1.8 MB single-threaded lite build that needs no special server headers.
- Is the Stockfish in Chessko free software?
- Yes. It is distributed under the GNU General Public License v3, and the licence and source links are published with the files.