← All projects

Case study

Smart Chess

An on-device chess application that runs a local Stockfish 19 engine via WebAssembly, manages a strict rules-enforced game state, and handles PGN parsing and monotonic clock timing entirely in the browser.

TypeScriptStockfishWebAssemblyIndexedDBReactVitestPlaywrightCSS

What it is

Smart Chess is a browser-first application designed to act as a digital companion for physical chessboard games. It runs entirely on-device with no backend, no accounts, and no video uploads. It provides a highly accurate rules engine, a monotonic chess clock, PGN import/export, IndexedDB storage, and local Stockfish 19 engine analysis running inside a Web Worker.

Engine integration and architecture

The application integrates Stockfish 19 compiled to WebAssembly. It uses a custom engine adapter to handle the lifecycle of the Web Worker without importing React or game-state dependencies directly, ensuring a clean separation of concerns. The adapter manages startup handshakes, download progress via MessagePort, search cancellation, and stale-result rejection (ensuring that a cancelled search's output does not overwrite a newer search). It offers a ~1.8 MB 'Quick' lite engine and an optional ~94 MB 'Deep analysis' engine with full NNUE, loaded on demand.

State, clock, and storage reliability

The chess core uses chess.js as the sole rules authority, verified against published perft counts. Moves are processed through a single command path (`CommitMove`) that rejects illegal, malformed, or ambiguous input. The chess clock uses monotonic timestamps rather than a decrementing setInterval counter, ensuring accuracy even if the browser tab is throttled. Data is persisted locally using IndexedDB with schema versioning and explicit write-failure reporting.

Testing and verification

The codebase is thoroughly verified with 302 tests across unit and integration projects, alongside 9 end-to-end browser tests using Playwright. A dedicated script (`npm run verify:engine`) asserts that the pinned Stockfish build exists, verifying its size and SHA-256 digest against upstream releases to prevent unexpected asset drift.

What I learned

  • Separating the engine adapter from React and game-state imports makes it easier to test the Web Worker lifecycle and UCI protocol handling in isolation using recorded transcripts.
  • Relying on monotonic timestamps for the chess clock prevents timing drift and UI throttling issues common with traditional decrementing setInterval timers.

What it doesn't do yet

Listed deliberately — a project's limits are as useful to a reviewer as its features.

  • Camera-based move detection is not implemented; the OpenCV vision pipeline is defined as a contract but not yet built. Moves must be entered by tapping the board.
  • Opening books, move classification (e.g., blunders, brilliant moves), accuracy scores, and post-game review reports are not implemented.
  • Two-device synchronization is not implemented; the transport adapter is specified but not built.
  • Multi-threaded Stockfish builds are not supported because they require COOP/COEP headers for SharedArrayBuffer, which have not been benchmarked on target devices yet.