A web app for digitizing chess games from handwritten notation sheets and board images. Scan, review, analyze with Stockfish, and export to Lichess — all in one place.
- Notation Analyzer — Upload photos of handwritten or printed score sheets. AI extracts moves and metadata automatically, then you review each move one by one.
- Board Scanner — Upload a photo of a chessboard (or a page with multiple diagrams) and get the FEN position instantly.
- Stockfish Analysis — Local engine evaluation with configurable depth. See the best move and position assessment after every move.
- Lichess Export — Send your transcribed game or scanned position directly to a Lichess study.
- Multi-provider AI — Supports Google Gemini, OpenAI, OpenAI Codex via ChatGPT/Codex auth stores, Anthropic Claude, and OpenAI-compatible custom endpoints. Switch providers with a single environment variable.
git clone <repo-url>
cd ChessNotation
npm installCopy the template and fill in your API key:
cp .env.example .env.localEdit .env.local:
AI_PROVIDER=gemini
AI_API_KEY="your-api-key-here"See AI Provider Setup below for details on getting a key.
npm run devOpen http://localhost:3000.
Chess Toolbox needs an AI vision API to parse images. Pick any one of the supported providers.
| Provider | Default Model | Get an API Key |
|---|---|---|
| Gemini | gemini-2.5-flash |
Google AI Studio — free tier available |
| OpenAI | gpt-4o |
OpenAI Platform |
| Claude | claude-sonnet-4-20250514 |
Anthropic Console |
| OpenAI Codex | gpt-5.4 |
Uses an existing OpenClaw or Codex auth store; AI_API_KEY is not required |
| Custom (OpenAI-compatible) | gpt-4o |
Use your provider's API key, or OpenAI OAuth token when AI_AUTH_METHOD=openai-oauth |
Set these in .env.local:
AI_PROVIDER=gemini # gemini | openai | claude | custom | openai-codex
AI_API_KEY="your-key" # Required for gemini/claude, and for openai/custom when AI_AUTH_METHOD=api-key
# Not required for openai-codex or openai/custom when AI_AUTH_METHOD=openai-oauth
AI_AUTH_METHOD=api-key # OpenAI/custom only: api-key | openai-oauth
AI_OAUTH_TOKEN= # Optional static bearer-token override for openai-codex, or openai/custom with AI_AUTH_METHOD=openai-oauth
# Use for one-off/debug sessions; when set it overrides auth-store discovery
AI_MODEL= # (Optional) Override the default model
AI_CLAW_AUTH_PROFILE_PATH= # Optional exact OpenClaw auth-profile override; disables discovery
AI_CODEX_AUTH_FILE= # Optional exact Codex auth.json override
# OPENCLAW_STATE_DIR= # Optional OpenClaw state dir hint; leave unset to use defaults
# CODEX_HOME= # Optional Codex home dir hint; leave unset to use defaults
AI_BASE_URL= # Required when AI_PROVIDER=customExamples:
# OpenAI
AI_PROVIDER=openai
AI_API_KEY="your-openai-api-key"# OpenAI Codex
AI_PROVIDER=openai-codex# OpenAI Codex on a VPS with OpenClaw auth discovery
AI_PROVIDER=openai-codex
OPENCLAW_STATE_DIR="/var/lib/openclaw"# OpenAI Codex with an exact OpenClaw auth-profile override
AI_PROVIDER=openai-codex
AI_CLAW_AUTH_PROFILE_PATH="/var/lib/openclaw/agent/auth-profiles.json"# Local Codex CLI fallback
AI_PROVIDER=openai-codex
# Uses $HOME/.codex/auth.json by default# Explicit auth-store override
AI_PROVIDER=openai-codex
AI_CODEX_AUTH_FILE="/mnt/secrets/codex-auth.json"# OpenAI-compatible custom endpoint
AI_PROVIDER=custom
AI_API_KEY="your-provider-key"
AI_BASE_URL="https://your-proxy.example.com/v1"# OpenAI-compatible custom endpoint with OpenAI OAuth
AI_PROVIDER=custom
AI_AUTH_METHOD=openai-oauth
AI_OAUTH_TOKEN="your-openai-oauth-token"
AI_BASE_URL="https://your-proxy.example.com/v1"Note:
AI_OAUTH_TOKENis a static bearer-token override foropenai-codex, and foropenai/customwhenAI_AUTH_METHOD=openai-oauth. It is optional and mainly meant for one-off or debug sessions. When set, it takes priority over normal auth discovery.
Note:
AI_API_KEYis not required foropenai-codex; that provider reads an existing OpenClaw or Codex auth source unless you override it withAI_OAUTH_TOKEN,AI_CLAW_AUTH_PROFILE_PATH, orAI_CODEX_AUTH_FILE.
Tip: Gemini's free tier is a good starting point. For best accuracy on messy handwriting, try
gemini-2.5-flashorgpt-4o.
To export games directly to your Lichess studies:
- Go to lichess.org/account/oauth/token
- Create a new personal access token
- Enable the Study read and Study write scopes
- Copy the token and add it to
.env.local:
LICHESS_TOKEN="lip_xxxxxxxxxxxxxxxxxxxxx"You can also paste the token into the Lichess Integration section of the in-app Settings modal — but .env.local is recommended so it persists across browsers.
- Open the Notation Analyzer tab
- Click Upload Photo and select an image of a chess score sheet
- The AI will parse all moves and show them in a review panel
- For each move, you can:
- Accept — play the move on the board and advance
- Skip — discard this move and go to the next
- Prev — go back and re-review the previous move
- If the notation spans multiple pages, click Upload Next Page after finishing the first
- Edit game metadata (players, event, date) in the header area
- Export the finished PGN to Lichess or copy/download it
AI parsing is not perfect, especially with messy handwriting. Here's how to handle errors:
If a move is wrong:
- When reviewing, if the suggested move doesn't look right, click Skip to discard it.
- Then play the correct move manually by dragging the piece on the board.
- Continue accepting the remaining parsed moves as normal.
If a move was missed:
- The AI may skip a move entirely (e.g., misread two moves as one).
- Skip the incorrectly merged move, then manually play both individual moves on the board by dragging pieces.
- Resume accepting parsed moves from where they line up again.
If the move order is wrong:
- Click Prev to revert back to before the incorrect move.
- Manually play the moves in the correct order.
- Then continue accepting from the next correct parsed move.
If notation is ambiguous:
- For example: "Nd2" when both knights can go to d2, or a piece letter that looks like a pawn move.
- Skip the ambiguous move and make the correct one on the board. The board enforces legal moves only, so you'll know immediately if a move is valid.
Tip: Enable Stockfish analysis in Settings. The engine evaluation helps you spot incorrect transcriptions — if the eval suddenly swings wildly after a move, the transcription might be wrong.
Single board:
- Open the Board Scanner tab
- Click Scan Single Board
- Select an image containing one chessboard
- The position loads automatically — copy the FEN or export to Lichess
Multiple boards (e.g., a textbook page):
- Click Scan Multiple Boards
- The AI first detects all board locations (Phase 1), then parses each position in parallel (Phase 2)
- Click any detected board to load its position
- Use the piece toolbar to make corrections if needed
The Board Scanner tab includes a full board editor:
- Select a piece type from the toolbar and click squares to place it
- Use the trash tool to remove pieces
- Switch to Move Only mode to drag pieces normally
- Start Position resets to the standard opening setup
- Clear Board empties everything
src/
├── app/
│ ├── api/
│ │ ├── parse-image/ # Notation sheet → moves + metadata
│ │ ├── parse-board/ # Board image → FEN
│ │ ├── parse-boards/ # Multi-board detection
│ │ ├── analyze/ # Stockfish analysis endpoint
│ │ └── lichess/ # Lichess API proxy routes
│ └── page.tsx # Main app shell + settings modal
├── components/
│ ├── AnalysisBoard.tsx # Notation analyzer tab
│ ├── BoardScanner.tsx # Board scanner tab
│ └── LichessExportModal.tsx
├── hooks/
│ ├── useChessGame.ts # Chess game state management
│ ├── useImageParser.ts # Image upload + AI parsing
│ └── useSettings.ts # LocalStorage settings
├── lib/
│ └── ai-provider.ts # Multi-provider AI abstraction
├── prompts/ # AI prompt templates + JSON schemas
└── utils/ # FEN manipulation, image compression
- Framework: Next.js 16, React 19, TypeScript
- Styling: Tailwind CSS
- Chess: chess.js, react-chessboard
- AI Vision: Google Gemini / OpenAI / Anthropic Claude
- Engine: Stockfish (runs locally in-browser)
- Export: Lichess API
MIT