Analyse your games with no account, no upload, and no record.
(A "quiet move" improves your position without fanfare. So does this app.)
A lichess-style web chess game against Stockfish 18, running entirely in the browser — no backend. The engine is the official Stockfish WASM build (lite, single-threaded) executing in a Web Worker, so the whole thing is static files and can be hosted on GitHub Pages. Games you paste or play never leave your machine: the engine runs locally, shared games travel as plain PGN in the URL, and nothing is stored anywhere.
Any static file server works:
python3 -m http.server 8123
# then open http://localhost:8123/- Drag-and-drop board (chessground — lichess's own board UI), legal-move hints, last-move and check highlights, premoves
- Play as White, Black, or random side
- Engine strength slider: 1320–3190 Elo (
UCI_LimitStrength/UCI_Elo) - Move list, undo, flip board, promotion picker
- Checkmate / stalemate / draw detection (chess.js)
- Sounds from lichess's standard set (move, capture, game end) with a persistent mute toggle
- "Get a hint" — a full-strength MultiPV search shows the best move as a green arrow and the runner-up as a second arrow coloured by how much win probability it gives away (blue <5%, yellow <15%, red beyond), each labelled with its eval; the status line spells out the comparison (works even when the opponent is strength-limited)
- Eval bar — score lines from engine searches, mapped to win probability
(lichess's sigmoid); fresh games and undos get a quick full-strength eval,
mates show
#N, finished games show1-0/0-1/½ - Post-game analysis graph — when a game ends, every position is re-evaluated at full strength and drawn as a win-probability chart (lichess-style). Moves that lose ≥10/20/30% win probability are marked as inaccuracies / mistakes / blunders; click the graph, click a move in the move list, or use ←/→ to step through the game on the board.
- Full review: per-player accuracy scores (lichess's formula),
?!/?/??glyphs on the marked moves in the move list, and a green best-move arrow on every reviewed position showing what the engine would have played. - Shareable games — finished games write themselves into the URL as standard
PGN (
?pgn=…), and a small box at the bottom of the panel accepts any pasted PGN. Opening a link (or pasting) replays the game and runs the analysis graph, even for unfinished games; games from custom positions round-trip via PGN's[FEN]header, and you can keep playing against the engine from where a loaded game left off. - Position links — "Copy position link" puts the current position (or the one
you're reviewing) in the URL as
?fen=…and copies the link. Bookmark it like an opening-trainer card: opening the link drops you straight into the position against the engine, playing the side to move, and "New game" retries it. No storage anywhere — the URL is the save file. - Opening book (
openings.html) — a standalone page listing all 3,733 named ECO openings (data: lichess-org/chess-openings, CC0, converted once toopenings.json), with a live filter. Every line links to the app as a?pgn=URI: the opening appears on the board, ready to step through or play out against the engine. - Endgame trainer: 270 positions from melvincarvalho/endgames, each with a goal (convert the win / hold the draw). The engine defends at full strength; the app detects success or failure and "New game" retries the position.
| Piece | Role |
|---|---|
vendor/chessground.min.js |
board rendering and interaction |
vendor/chess.js |
rules, legality, game-over detection |
vendor/stockfish-18-lite-single.js + .wasm |
the engine, spoken to over UCI text protocol |
app.js |
glue: user move → chess.js → position fen / go movetime → bestmove → board |
The single-threaded engine build is used deliberately: multi-threaded WASM
needs SharedArrayBuffer, which requires cross-origin-isolation headers that
GitHub Pages can't send. Single-threaded is plenty strong for play.
A debug handle is exposed in the browser console: game.chess, game.engine,
game.move('e2', 'e4'), game.newGame().
Vendored files come from npm (chessground, chess.js, stockfish):
npm install
cp node_modules/chessground/dist/chessground.min.js vendor/
cp node_modules/chessground/assets/chessground.*.css vendor/
cp node_modules/chess.js/dist/esm/chess.js vendor/
cp node_modules/stockfish/bin/stockfish-18-lite-single.{js,wasm} vendor/positions.json is generated by tools/extract-positions.js from a clone of
the endgames repo (node tools/extract-positions.js /path/to/endgames/docs;
needs native stockfish at /usr/games/stockfish): every fen="…" in its docs
is extracted (labelled by nearest heading and doc directory), validated
with chess.js, then classified by native Stockfish (~250 ms/position) —
decisive evals become "win" goals for the better side, near-zero evals become
"draw" goals defended from the side to move, ambiguous evals (50–200 cp) are
dropped.
Sound files in sounds/ are the "standard" sound set from
lichess-org/lila
(AGPL-3.0). In that set the check sound is silence and the victory/defeat/draw
sounds all alias GenericNotify, so only Move, Capture, and
GenericNotify are vendored.
This app is GPL-3.0. It stands on excellent open-source work:
| Component | Author / project | Licence |
|---|---|---|
| Stockfish 18 (official WASM build via stockfish npm) | The Stockfish developers | GPL-3.0 |
| chessground board UI | lichess.org | GPL-3.0 |
| chess.js rules engine | Jeff Hlywa | BSD-2-Clause |
| cburnett piece set (via chessground assets) | Colin M.L. Burnett | CC-BY-SA 3.0 / GFDL |
| Board sounds (standard set) | lichess-org/lila | AGPL-3.0 |
| Endgame positions | melvincarvalho/endgames | see repo |
The win-probability sigmoid, judgement thresholds (inaccuracy / mistake / blunder), and accuracy formula follow lichess's published approach.