| title | MoveLens Chess Review |
|---|---|
| emoji | ♟️ |
| colorFrom | green |
| colorTo | blue |
| sdk | docker |
| app_port | 7860 |
| pinned | false |
| short_description | Free chess game review with a playable analysis board |
Free chess game review. Stockfish 19 labels every move from Brilliant to Blunder, shows the better move, and lets you play on the board to test your own ideas. Import games from Chess.com or Lichess in one click.
Live demo · How it works · Deploy guide
- Reviews a whole game. Stockfish checks every position and each move gets one of 11 labels (Brilliant, Great, Best, Excellent, Good, Book, Forced, Inaccuracy, Mistake, Miss, Blunder) with a plain-English coach comment, the better move as an arrow, an evaluation bar and graph, and an accuracy score per player.
- Lets you play on the board. Drag or click any piece at any point of the game and get the engine's verdict on your move. Retry a mistake and try to find the better move. Switch on the top three engine lines for any position and click a line to play it.
- Imports games by pasting a PGN, uploading a file, or typing a Chess.com / Lichess username. Type your username and the board opens from your side.
- Works offline and remembers everything. Reviews, the side lines you explored, every engine answer, and even the app itself are kept in your browser (Service Worker + IndexedDB). A position you have seen is answered instantly with zero server calls.
- Looks good. Responsive from phone to wide monitor, 6 scenes, 12 boards, 6 piece sets, move sounds, keyboard shortcuts, right-click arrows. The layout never jumps while you step through a game.
Review: coach, best move, graph, move list |
Retry: find the better move, get an instant verdict |
Engine tab: Stockfish's three best lines for any position |
Themes: scenes, boards and piece sets |
| Layer | Technology |
|---|---|
| Backend | Python 3.12, FastAPI, Uvicorn, Pydantic, thread pools (concurrent.futures) |
| Chess engine | Stockfish 19 over the UCI protocol (own Python client), plus a chess rules engine written from scratch (legal moves, SAN/FEN/PGN, check/mate/draws), verified with perft counts |
| Frontend | Vanilla JavaScript (ES modules, no framework, no build step), HTML5, CSS3 (custom properties, container queries), inline SVG, Web Audio API |
| Offline & storage | Service Worker, Cache Storage, IndexedDB, localStorage, Web App Manifest (installable PWA) |
| Integrations | Chess.com public API, Lichess API |
| Machine learning (optional) | scikit-learn gradient-boosted trees (rating estimator), joblib |
| DevOps | Docker (runs as a non-root user), Hugging Face Spaces, Render blueprint, Git/GitHub |
| Testing | pytest, FastAPI TestClient, Node.js test runner |
Browser (PWA) Server (FastAPI, one Docker container)
+-------------------------------+ HTTPS/JSON +--------------------------------------------+
| board.js drag & drop, SVG | ------------> | POST /api/reviews --> job queue --+ |
| review.js state machine | | GET /api/reviews/{id} progress | |
| api.js cached answers <--+-- IndexedDB | thread pool | |
| sw.js offline shell | | POST /api/explore + (1 Stockfish per |
| | <------------ | POST /api/analyse +--> live engine worker)|
+-------------------------------+ | POST /api/legal + (own process) | |
| GET /api/import/{site} --> Chess.com / Lichess
+--------------------------------------------+
- Win percentage, not centipawns. Each Stockfish score is converted to a 0 to 100 winning chance with a logistic curve (
50 + 50·(2/(1+e^(−0.00368·cp)) − 1)). A pawn matters in an equal position and hardly at all when you are already a queen up. - A move's quality is the winning chance it gave away compared with the engine's best move. Extra rules add Brilliant (sound sacrifices), Great (only move), Miss, Book (3,800 named openings) and Forced.
- Reviews run as background jobs (a game takes seconds to minutes), polled once a second. Your own moves use a separate engine process, so they never wait behind a running review.
- The server adapts to its host. It reads the container's CPU limit and, on a free 2-vCPU host, caps the depth at Deep, shortens the queue and tells waiting users how many reviews are ahead of them.
| Label | Rule |
|---|---|
| Best | Stockfish's top move |
| Excellent / Good | lost less than 2% / 5% winning chance |
| Inaccuracy / Mistake / Blunder | lost less than 10% / 20% / 20% or more |
| Brilliant | best move that leaves 2+ pawns of material to be taken, when you were not already winning and are not worse afterwards |
| Great | best move, and every alternative is at least 10% worse |
| Miss | the opponent just erred and your move lost 10% or more, giving the advantage back |
The thresholds are constants in app/classify.py. No site publishes its exact rules, so labels can differ from other sites. Accuracy uses the published Lichess formula.
Docker (nothing else to install; the image downloads Stockfish 19):
docker build -t movelens .
docker run -p 7860:7860 movelens # open http://localhost:7860Python (3.10+ and a Stockfish binary):
pip install -r requirements-dev.txt
# Windows PowerShell: $env:STOCKFISH_PATH="C:\path\to\stockfish.exe"
uvicorn app.main:app --reload # open http://127.0.0.1:8000Click Try a famous game for an instant demo (it needs no engine). Run the tests with pytest (they use a small stand-in engine) and node --test tests/sw.test.mjs.
| Depth | Search | Measured on a 47-move (94-ply) game |
|---|---|---|
| Fast, Standard | 12 / 16 ply | faster than Deep |
| Deep (default) | 20 ply | 115 s on the free Hugging Face CPU (2 vCPUs) |
| Maximum | 24 ply | 190 s on 4 threads (not offered on 2 vCPUs) |
The same Dockerfile runs anywhere. Step-by-step guides: Hugging Face Spaces (recommended; works on the free CPU tier with no configuration) and a Render blueprint (render.yaml).
| Variable | Default | Meaning |
|---|---|---|
STOCKFISH_PATH |
found on PATH |
Engine location (/opt/stockfish in Docker) |
STOCKFISH_FALLBACK |
none | Second engine to try if the first cannot start |
ENGINE_WORKERS |
1 | Reviews analysed in parallel (one Stockfish each) |
ENGINE_THREADS |
detected | CPU threads per engine (detects container CPU limits) |
ENGINE_HASH_MB |
128 | Memory per engine |
MAX_QUALITY |
deep on 2 CPUs, else max |
Highest depth offered |
MAX_QUEUED |
6 on 2 CPUs, else 20 | Reviews allowed to wait |
Interactive docs at /docs (Swagger UI).
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/reviews |
{"pgn", "quality"} → starts a review, returns an id |
| GET | /api/reviews/{id} |
progress, queue position, then the annotated game |
| POST | /api/legal |
every legal move from a FEN, with SAN and resulting FEN |
| POST | /api/explore |
judge a move you played, with the same labels as a review |
| POST | /api/analyse |
the engine's best lines for a position |
| GET | /api/import/{chesscom|lichess}?user= |
a player's 20 latest games |
| GET | /api/health |
engine, depth limit, CPUs, opening book |
app/ FastAPI server, chess rules engine, PGN parser, UCI client, classifier, review pipeline,
live-board logic, Chess.com/Lichess importer, host detection
static/ index.html, css/, js/ (ES modules), sw.js (service worker), pieces/ (SVG sets), bg/ (scenes)
data/ 3,800 named openings (Lichess, public domain)
tests/ pytest suite (82 tests), stand-in UCI engine, service-worker tests (Node)
ml/ optional rating-estimator dataset builder and trainer
tools/ scripts that draw the scene backgrounds and app icons
docs/ HOW_IT_WORKS.md (guided tour), DEPLOY.md
Dockerfile Python image + Stockfish 19, non-root
- From-scratch chess rules (move generation, castling, en passant, promotion, repetition) checked against published perft node counts. No chess library.
- Rigid review layout. The side panel is exactly as tall as the board column and every block has a fixed size, so no coach comment, tab or opened panel can move the board or the buttons.
- Offline-first. The service worker serves the saved app when the server is offline or slow (a free host that is waking up), and never lets a host's "starting" page replace the cached app.
- Hugging Face rejects binary files in git, so images are committed as base64 text and decoded by the server (
app/assets.py); screenshots in this README are SVG wrappers for the same reason. - Honest fallbacks. If the newest Stockfish cannot run on the host, the server starts the older Debian build instead; if IndexedDB is blocked, the app falls back to memory.
Server-side saved reviews (SQLite/Postgres), Stockfish in the browser via WebAssembly (analysis would cost nothing to host), a "Practice my mistakes" mode that chains Retry puzzles from all saved games, per-opening statistics.
Code: MIT. Analysis by Stockfish (GPLv3, run as a separate program). Opening names from the public-domain Lichess opening list. Piece sets by Colin M. L. Burnett, Armando Hernandez Marroquin, Alexis Luengas and Maurizio Monge under their own licences, listed in static/pieces/CREDITS.md. Scene backgrounds are drawn by tools/make_backgrounds.js. MoveLens is an independent project and is not affiliated with Chess.com or Lichess.