Skip to content

Repository files navigation

Quiet Chess

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.

Run locally

Any static file server works:

python3 -m http.server 8123
# then open http://localhost:8123/

Features

  • 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 show 1-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 to openings.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.

How it works

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 movetimebestmove → 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().

Updating vendored libraries

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.

Credits and licences

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.

About

Play Stockfish in the browser — lichess-style board, WASM engine, endgame trainer. Fully static.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages