cd /news/machine-learning/show-hn-chesslm-tiny-chess-model-tra… · home topics machine-learning article
[ARTICLE · art-135165] src=github.com ↗ pub= topic=machine-learning verified=true sentiment=· neutral

Show HN: Chesslm – Tiny chess model trained on Stockfish

Developer skorotkiewicz released chesslm, an open-source chess engine that pairs a 100,353-parameter NumPy neural network with a fixed material evaluator and alpha-beta search, using Stockfish only to supply training labels and benchmark comparisons. The project ships a Tkinter desktop game and a localhost-only web server, runs on Python 3.10 or newer, and requires no Stockfish process at play time. The author states the included model.npz checkpoint's training history and playing strength have not been verified and claims no Elo rating.

read7 min views3 publishedSep 20, 2026
Show HN: Chesslm – Tiny chess model trained on Stockfish
Image: Michielbdejong (auto-discovered)

A tiny neural chess evaluator with alpha-beta search and a desktop chess game.

Quick start · Desktop game · Training · Benchmarking · Development

chesslm combines a small NumPy network with a fixed material evaluator to choose chess moves. Play against the included model.npz in a Tkinter window, or request a move from the command line. Playing uses your CPU and requires no Stockfish process. Stockfish supplies training labels and benchmark comparisons.

The network has 100,353 parameters and 401,412 bytes of float32 weights, about 392 KiB. Despite the name, it is not a language model. The included checkpoint's training history and playing strength have not been verified; no Elo is claimed.

Use Python 3.10 or newer. Run these commands in a POSIX shell:

git clone https://github.com/skorotkiewicz/chesslm.git
cd chesslm
python -m venv .venv
. .venv/bin/activate
python -m pip install -r requirements.txt
python chess_game.py

The desktop game needs Tkinter and a display. Some Linux distributions provide Tkinter separately in a package named python3-tk or tk. On Windows, activate the environment with .venv\Scripts\Activate.ps1 in PowerShell.

For a terminal-only first move, run this from the project directory:

OPENBLAS_NUM_THREADS=1 python chesslm.py play --model model.npz

The OPENBLAS_NUM_THREADS=1 prefix limits OpenBLAS threading for the small network. In PowerShell, set $env:OPENBLAS_NUM_THREADS = "1" before running the Python command. The desktop game sets this default automatically.

python chess_game.py
python chess_game.py --color black
python chess_game.py --model model.npz --depth 3 --max-nodes 20000

Click a piece, then its destination. Dots and rings mark legal moves, and buttons let you choose a promotion piece. Use arrow keys and Enter or Space to move with the keyboard; Tab reaches the buttons. Playing as Black flips the board.

Search runs in a background thread, so the window can redraw and close while the model thinks. New game becomes available after the search finishes. The default checkpoint is model.npz beside the script.

If you use uv, you can also launch the game without setting up .venv:

uv run --python /usr/bin/python chess_game.py
uv run --python /usr/bin/python chess_game.py --color black

These examples use a Linux system Python with Tkinter installed. Choose a Python with Tkinter on your platform. uv installs the script's declared NumPy and python-chess dependencies in an isolated environment.

python chess_web.py
uv run --python /usr/bin/python chess_web.py

Open http://127.0.0.1:8000 in your browser. Choose White or Black, then click a piece and its destination. Legal moves are highlighted; promotions offer all four pieces. Arrow keys and Enter work on the board. Each tab has its own game; refreshing the page starts over.

The Python server loads model.npz beside the script and performs inference on CPU. The browser does not download the model. No Stockfish, training, external assets, or JavaScript packages are needed. Tkinter is not required.

Use --model /path/to/model.npz, --port 8080, --depth 3, or --max-nodes 20000 to change the defaults. The server preserves move history for repetition draws and validates every move. Games are limited to 1000 submitted plies. One search runs at a time; another tab can retry if the model is busy.

This is a localhost-only server for personal play, not a public hosting setup. Stop it with Ctrl+C. Keep chess_web.html beside chess_web.py.

The included Pages workflow builds and deploys the browser game when you push to main. In the repository, open Settings > Pages and select GitHub Actions as the source. Push these files, or run Deploy chess game to GitHub Pages from the Actions tab. For this repository, the expected address is https://skorotkiewicz.github.io/chesslm/.

Pages cannot run a Python server. This build instead loads Pyodide and NumPy in a Web Worker, then runs the same evaluator and search with the bundled model.npz. The checkpoint is copied unchanged; CI does not train or generate data. Relative asset paths support project Pages URLs such as /chesslm/.

The first visit downloads the Python runtime and NumPy from jsDelivr and can take a minute on a slow connection. Browser inference uses depth 2 and a 2,000-node limit per move, below the local server's default budget. The UI stays responsive while the worker searches. Refreshing discards the game.

To preview the Pages build locally, use an environment with requirements.txt:

python build_pages.py
python -m http.server 8001 --bind 127.0.0.1 --directory _site

Open http://127.0.0.1:8001. Opening index.html as a file:// URL will not work. The build bundles python-chess with its GPL license in chess.zip.

OPENBLAS_NUM_THREADS=1 python chesslm.py play \
  --model model.npz --depth 3 --max-nodes 20000

The command prints JSON with move, a UCI move string such as e2e4, and nodes, the visited node count. When the game has ended, move is null. Add --fen '...' with a valid FEN to choose a move in another position.

This command chooses one move per invocation; it does not implement a UCI engine server. CLI model paths are relative to your working directory.

Component Implementation
Input 782 features: 12 piece planes, side to move, castling rights, legal en passant file, and halfmove clock
Network One 128-unit tanh hidden layer, learning a correction to a fixed material evaluator
Target White's evaluation, transformed with tanh(centipawns / 600)
Training Mean squared error with Adam; save the weights with the lowest validation loss
Search Iterative deepening, alpha-beta pruning, capture ordering, and four quiescence plies
Runtime NumPy and python-chess on CPU

The weight size excludes the NPZ header, Python, NumPy, and process memory.

A node limit can leave only a shallow completed search iteration. If none finishes, search returns a legal fallback move. Long tactics remain a limitation. The game and search recognize automatic draws but do not implement optional draw claims. A FEN does not include prior repetition history.

Training is optional. The included checkpoint is enough to play.

Data generation and benchmarking require a Stockfish executable. Stockfish is not tracked in this repository. The default path is stockfish/stockfish-linux-x86-64-universal, relative to chesslm.py, for x86-64 Linux. Supply --engine /path/to/stockfish to use another location or a binary for your platform.

Run these commands on your training machine. Output files must not already exist; model-trained.npz leaves the included checkpoint available.

python chesslm.py generate \
  --engine /path/to/stockfish \
  --positions 100000 --nodes 20000 --threads 2 \
  --output positions-train.jsonl

OPENBLAS_NUM_THREADS=2 python chesslm.py train \
  --data positions-train.jsonl --epochs 30 --output model-trained.npz

Stockfish labels self-play positions with White's centipawn evaluation using a three-line search. Opening choices vary among those lines, with occasional variation later. Mate labels use ±10,000 centipawns. The node budget bounds Stockfish's search work, not elapsed time.

Validation holds out entire games and removes positions shared with those games from training. A dataset needs at least two games and some distinct positions. Training saves the initial weights if no epoch improves validation loss.

The dataset is loaded into memory. Input arrays need roughly 313 MB per 100,000 positions, plus JSON rows and temporary arrays. Larger datasets need more RAM.

Copy model-trained.npz to your playing machine and select it explicitly:

python chess_game.py --model model-trained.npz

Generate a separate corpus with a different seed. Do not train on this file. Use fresh output filenames if you repeat the workflow.

python chesslm.py generate \
  --engine /path/to/stockfish \
  --seed 9001 --positions 5000 --nodes 20000 \
  --output positions-benchmark.jsonl

OPENBLAS_NUM_THREADS=1 python chesslm.py benchmark \
  --engine /path/to/stockfish \
  --model model-trained.npz --data positions-benchmark.jsonl \
  --positions 100 --nodes 50000

The JSON report contains the position count, best-move agreement, and mean centipawn loss against Stockfish. These are noisy, node-limited estimates. Different seeds can still produce repeated openings, and match testing is needed to establish an Elo rating. A tiny distilled model should not be expected to match Stockfish.

From an environment containing requirements.txt, run:

OPENBLAS_NUM_THREADS=1 python -m unittest -v
python chesslm.py --help
python chess_game.py --help
python chess_web.py --help

Tests cover encoding, checkpoint size and , gradient math, invalid positions, validation separation, node limits, promotions, check evasion, forced mates, a free queen capture, and desktop interactions. GUI tests skip when no display is available.

The tests do not run Stockfish, generate data, or optimize model weights. Search tests use hand-set zero weights; the derivative check uses random weights without optimization. Passing them does not measure the supplied checkpoint's strength. Generation and training still need end-to-end verification on the training machine.

File Purpose
chesslm.py Model, search, data generation, training, and benchmark CLI
chess_game.py Tkinter desktop game
chess_web.py Local HTTP server and validated game API
chess_web.html Browser chess board
test_chess_web.py HTTP, rule, request-validation, and static-build checks
chess_position.py Shared move validation and board responses
chess_worker.js Browser-side model inference with Pyodide
build_pages.py Static site build, including the unchanged model
model.npz Included checkpoint for play
requirements.txt Runtime dependencies
test_chesslm.py Model and search checks
test_chess_game.py Desktop game checks

MIT.

── more in #machine-learning 4 stories · sorted by recency
── more on @chesslm 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/show-hn-chesslm-tiny…] indexed:0 read:7min 2026-09-20 ·