Same FEN, different game
If you key a chess overlay on FEN alone, two different games can look identical. Rematches are where this shows up. The starting position is the same string, the player knows it's a new game, and an old arrow can come back if you never stored anything else.
Lichess does issue a new game id. In the recorder that's an explicit rematch boundary: a new siteGameId / session identity doesn't get glued onto the previous unfinished game just because the profile looks the same. The overlay identity is narrower still.
I work on ChessNavio. It runs Stockfish locally and draws one suggested move next to the board. I spent more time on "is this even a game" and "is this result still about that board" than on the engine itself.
querySelector will find you a board that is not a game
Open Chess.com Play Computer as a guest. The pieces can already sit on their starting squares before anyone presses Play. A legal start FEN doesn't tell you much here — that string doesn't change when the game begins. What changes is the chrome beside the board: Resign, hint, undo, the live move list. Those are what we wait for after Play.
World Chess is the other direction: the page can contain more than one board-like node. We don't pick a winner between two legal FENs. The qualifier wants exactly one primary game board. If that's missing, or there are several, the UI stays quiet.
Per-site checklists
Each site has a short checklist: are we on a route that can host a game, is there exactly one visible board of the expected kind, has the game actually started, do we know the user's side, and is there a local or platform game id when the site exposes one.
If any of that is missing, nothing renders. I'd rather the panel sit there doing nothing than draw an arrow on a lobby or a broadcast.
The DOM does not settle in one mutation
After a move, the board node and the chrome around it can update on different frames. The piece may already have moved while the surrounding controls are still catching up, or the board root gets thrown away and rebuilt. We wait until those signals agree. If they don't, we treat it as not ready yet.
Display-time identity
Once the engine is asynchronous, a result is only allowed to paint if it still matches the board we're looking at. The type we compare at presentation time isn't a FEN and isn't a session snapshot:
type PresentationIdentity = {
fen: string;
boardEpoch: number;
configRevision: number;
jobId: string;
};
boardEpoch ticks when the position we care about changes. jobId ties the paint to the job that produced it. The user's side and platform game id live on the session snapshot. Side-to-move is already in the FEN.
UCI stop asks Stockfish to stop. It still emits bestmove later, so we can't assume the old search is gone. The result gets displayed only if fen, epoch, job id and config revision all still match.
The engine does not live in the service worker
MV3 service workers get stopped when they go idle, which means you can't count on keeping a WASM engine in memory there. Stockfish normally runs in an offscreen document, behind one scheduler. If that transport fails, there's an iframe fallback.
Priority, from the job reasons in the scheduler: confirmed live position, interactive training, speculative reply, recorded-move verification, post-game enrichment. A confirmed live position preempts speculative and post-game jobs.
What stays on the machine
Live position, engine lines and the caption next to the move are computed on the device. The server is for the account, and for sync if the user actually turned that on. Finished games don't go to the account until that setting is on.
Strength
The engine is Stockfish 18 as WebAssembly. Shallower levels mostly search less.
On the softer levels Maia proposes candidates; Stockfish scores them and we skip ones past the allowed eval loss. Max is plain Stockfish. I'm not mapping that to a rating.
Captions
The sentence next to the move is built locally from the position, the recommended move, the continuation and a detected motif. If we don't have a more specific motif, the English line is "improves the position." We don't invent kingside pressure the search never showed.
Fair play
Platforms prohibit engine help in an ongoing game against a human. The same pipeline against a computer, on a training board, or after the game is analysis. There's no stealth mode and no anti-cheat bypass. Getting a selector to work is not the same as Lichess or Chess.com saying that's allowed.
A FEN you can paste without installing anything
You can paste a PGN or FEN in the browser without an account. What stays on the device versus the account.
What I still don't know
I still don't know whether the short note next to the arrow helps people understand the move, or whether they look at the arrow and skip the sentence. Engine strength isn't the open question. The wording is.
I used an assistant to edit this. The session gates and identity types are from the code I ship.
Top comments (0)