Visualise PGN chess games as interactive trees; convert PGN to JSON, EDN, GraphViz DOT and HTML
Project description
chesstree
A command-line tool for converting chess games between PGN, JSON, EDN, GraphViz DOT, and interactive HTML formats. It accepts PGN or chesstree JSON as input (auto-detected from the file extension) and can output JSON, EDN, PGN, DOT, dothtml, or d3html via the -f/--format flag. JSON output includes move number, SAN and UCI notation, FEN positions, comments, NAGs, and variations. DOT output models the game tree as a left-to-right digraph suitable for rendering with GraphViz tools. The dothtml format wraps the DOT graph in a self-contained browser viewer powered by d3-graphviz, with pan, zoom, and board images included. The d3html format produces a purpose-built interactive D3.js tree viewer with collapsible nodes, variation highlighting, optional hover board images, and a dark-themed layout — no GraphViz dependency required.
As a taster how the d3html format looks and works see here.
▶ See d3html output — interactive D3.js tree viewer for full details.
Output format
The tool produces a JSON (or EDN) object with four top-level keys: schema_version, headers, moves, and result. Each move entry carries SAN/UCI notation, FEN positions before and after, and optional annotations (comments, NAGs, clock, eval, arrows). Variations appear inline as { "variation": [ ... ], "branch_fen": "<FEN>" } entries.
For the full normative specification — required vs optional fields, exact types, variation placement rules, NAG encoding, comment normalization, fidelity guarantees, and versioning contract — see docs/schema.md.
Installation
Requires Python 3.9 or later.
From PyPI
pip install chesstree
Or with pipx for an isolated environment (recommended for CLI tools):
pipx install chesstree
The chesstree command is added to your PATH automatically.
From source (for development)
git clone https://github.com/benedekfazekas/chesstree
cd chesstree
python3 -m venv .venv
source .venv/bin/activate # on Windows: .venv\Scripts\activate
pip install -e ".[dev]"
See the Development environment section below for full setup details.
Usage
usage: chesstree [-h] [--version] -i INPUT [-o OUTPUT] [-f {json,edn,pgn,dot,dothtml,d3html}]
[--input-format {pgn,json}] [-b] [--images MODE [MODE ...]]
[--template FILE] [-a] [--no-move-highlight] [-c]
options:
-h, --help show this help message and exit
--version show program's version number and exit
-i, --input INPUT Input file — PGN or chesstree JSON (use '-' for stdin)
-o, --output OUTPUT Output file (default: stdout)
-f, --format {json,edn,pgn,dot,dothtml,d3html} Output format: json (default), edn, pgn, dot, dothtml, or d3html
--input-format {pgn,json} Override auto-detected input format
-b, --forblack Board images from Black's perspective (dot/dothtml/d3html)
--images MODE [MODE ...] Image generation mode for dot/dothtml/d3html output (default: variations)
Choices: none, all, variations, commented.
'variations' and 'commented' may be combined.
SVG files are written alongside the output file;
stdout skips writing SVGs.
Has no effect on json/edn output.
--template FILE Custom HTML template for dothtml or d3html output.
Must contain the required placeholders for the chosen format.
Only used with -f dothtml or -f d3html.
-a, --hover-for-all-moves Embed per-move hover board images (d3html only).
Mouseover a move to see the board position in a popup.
--no-move-highlight Disable last-move square highlighting on board images.
By default the from/to squares of the last move are
coloured on every board image (dot/dothtml/d3html).
-c, --concise Compact output, no pretty-printing (json/edn output only)
The input format is auto-detected from the file extension (.pgn → PGN, .json → chesstree JSON). Use --input-format to override this when reading from stdin or a file with an unusual extension.
Supported conversions:
| Input | -f / --format |
Output |
|---|---|---|
| PGN | json (default) |
chesstree JSON |
| PGN | edn |
chesstree EDN |
| PGN | dot |
GraphViz DOT |
| PGN | dothtml |
Self-contained d3-graphviz HTML viewer |
| PGN | d3html |
Self-contained D3.js interactive tree viewer |
| JSON | pgn |
PGN |
| JSON | dot |
GraphViz DOT |
| JSON | dothtml |
Self-contained d3-graphviz HTML viewer |
| JSON | d3html |
Self-contained D3.js interactive tree viewer |
Examples
Convert a PGN file to JSON:
chesstree -i game.pgn -o game.json
Convert a PGN file to EDN:
chesstree -i game.pgn -f edn -o game.edn
Print compact JSON to stdout:
cat game.pgn | chesstree -i - -c
Generate board images from Black's perspective (dot/dothtml output):
chesstree -i game.pgn -f dot -o game.dot -b
Board image modes
The --images flag controls which segment nodes carry an SVG image row in DOT/dothtml output. It has no effect on JSON/EDN output (which never contains embedded images — use the fen_after field to generate board visuals with any chess library). The following modes are available:
| Mode | Description |
|---|---|
variations |
(default) Images at the last move of each line segment — see below |
all |
Every move gets an image |
commented |
Only moves that carry a comment get an image |
none |
No images at all (smallest output) |
variations and commented can be combined: --images variations commented.
The variations mode in detail
A chess game tree is naturally divided into segments — runs of moves along a single line until the game ends or the tree branches. The variations mode places one image at the last move of each segment:
- End of a line (no further moves) — the final move of the main line or of any variation always gets an image.
- Branch point — when a position has multiple continuations (e.g. a main move and one or more alternative variations), the first continuation (the main-line choice at that fork) is the last move of the current segment and gets an image. The alternatives each start their own segment and follow the same rule recursively.
This means images appear at the moments of decision — exactly where a reader is most likely to want to visualise the board — while keeping the output compact compared to all.
Generate images only at variation endpoints (default, dot/dothtml only):
chesstree -i game.pgn -f dot -o game.dot --images variations
Generate images for every move:
chesstree -i game.pgn -f dot -o game.dot --images all
Generate images only at commented moves:
chesstree -i game.pgn -f dot -o game.dot --images commented
Generate images at both variation endpoints and commented moves:
chesstree -i game.pgn -f dot -o game.dot --images variations commented
Omit all board images:
chesstree -i game.pgn -f dot -o game.dot --images none
Last-move square highlighting
By default, board images colour the from and to squares of the last move, making it easy to see which piece moved. This applies to all image-bearing formats: dot, dothtml, and d3html.
Use --no-move-highlight to produce plain boards without any square colouring:
chesstree -i game.pgn -f dothtml -o game.html --no-move-highlight
chesstree -i game.pgn -f d3html -o game.html --no-move-highlight
chesstree -i game.pgn -f dot -o game.dot --no-move-highlight
Convert a chesstree JSON file back to PGN:
chesstree -i game.json -f pgn -o game_restored.pgn
Export a PGN game to a GraphViz DOT file for visualisation:
chesstree -i game.pgn -f dot -o game.dot
Render the DOT file to SVG using GraphViz:
dot -Tsvg game.dot -o game.svg
Export from chesstree JSON to DOT:
chesstree -i game.json -f dot -o game.dot
dothtml output — d3-graphviz browser viewer
The dothtml format produces a self-contained HTML file that renders the game tree interactively in a browser using d3-graphviz. Board images are written as SVG files alongside the HTML file, which the browser loads by relative path.
# Generate HTML viewer + SVG images in ./output/
chesstree -i game.pgn -f dothtml -o output/game.html
# Open in browser
open output/game.html
The viewer includes a layout-engine selector (Dot, Circo, Fdp, …) and supports pan and zoom.
When writing to stdout, image references are included in the HTML but no SVG files are written:
chesstree -i game.pgn -f dothtml -o -
Custom HTML template (dothtml)
You can supply your own template with --template:
chesstree -i game.pgn -f dothtml --template my_template.html -o game.html
The template is plain HTML with three required placeholders:
| Placeholder | Replaced with |
|---|---|
{{CHESSTREE_TITLE}} |
Game title string (e.g. "White vs Black at 2024.01.01") |
{{CHESSTREE_IMAGES}} |
One .addImage("./name.svg", "144px", "144px") call per image |
{{CHESSTREE_DOT}} |
The raw DOT string — place inside a JS backtick template literal |
All three must be present or generation fails with an error listing the missing ones.
d3html output — interactive D3.js tree viewer
The d3html format produces a self-contained HTML file with a purpose-built interactive tree viewer powered by D3.js v7. Unlike dothtml it does not require GraphViz: the layout and rendering are done entirely in the browser.
# Generate HTML viewer + SVG board images in ./output/
chesstree -i game.pgn -f d3html -o output/game.html
# Open in browser
open output/game.html
Features of the viewer:
- Tree view (default) — a full interactive tree layout:
- Dark theme with colour-coded nodes: main-line segments (blue) are visually distinct from variation segments (purple).
- Node headers show the line type and move range, e.g. "Main line: 1–12" or "Variation: 8–10".
- Collapsible nodes — click any node to hide its variation children while keeping the main-line continuation visible. A badge in the header shows how many nodes are hidden. Ctrl+click collapses all children including the main-line continuation.
- Pan and zoom via scroll and drag.
- Drag-to-reposition individual nodes.
- Hover board images (optional, see
-abelow). - R key / ↺ Reset button restores the automatic layout.
- Deck view — a sequential card-by-card navigator (toggle with the 📇 Deck button):
- Step through game info and main-line segments one card at a time with ← → arrow keys or on-screen buttons.
- When a card has variations, clickable buttons appear below showing each variation's first move and optional comment (mirroring the tree's edge labels).
- Click a variation button to enter it; use ← → to step between sibling variations at the same branch point. Sub-variations are accessible the same way.
- A breadcrumb trail shows your position in the tree and lets you jump back to any ancestor level. Press Escape to go up one level.
- Cross-view navigation — double-click any node in the tree to open it directly in the deck view (with a zoom animation); double-click a deck card to jump back to the tree view centered on that node.
- Light/dark theme toggle applies to both views.
Board images in d3html
The --images flag works the same as for dot/dothtml:
# Default: one image per segment endpoint
chesstree -i game.pgn -f d3html -o game.html
# Images at both variation endpoints and commented moves
chesstree -i game.pgn -f d3html -o game.html --images variations commented
# No images (smallest output)
chesstree -i game.pgn -f d3html -o game.html --images none
SVG files are written alongside the HTML file. When writing to stdout, image references are included but no SVG files are written.
Hover board images (-a)
The -a / --hover-for-all-moves flag embeds a miniature board image for every individual move. Hovering over any move token in the tree shows the board position at that move in a popup:
chesstree -i game.pgn -f d3html -o game.html -a
This significantly increases file size (one small SVG per move) so it is off by default.
Custom HTML template (d3html)
chesstree -i game.pgn -f d3html --template my_d3_template.html -o game.html
The d3html template has four required placeholders (different from the dothtml placeholders):
| Placeholder | Replaced with |
|---|---|
{{CHESSTREE_TITLE}} |
Game title string |
{{CHESSTREE_TREE_DATA}} |
JSON tree data object (embed inside JSON.parse(\...`)`) |
{{CHESSTREE_IMAGES}} |
JS statements that populate the boardImages dict, one per SVG file |
{{CHESSTREE_HOVER_DATA}} |
JS statements that populate the hoverImages dict (empty when -a not used) |
All four must be present or generation fails.
DOT output and board images
The --images flag applies to DOT output as well as JSON/EDN. When writing to a file,
chesstree generates SVG board images and saves them in the same directory as the .dot file.
GraphViz then loads the images by path when rendering the DOT:
# Write game.dot and all SVG images into ./output/
chesstree -i game.pgn -f dot --images variations commented -o output/game.dot
dot -Tsvg output/game.dot -o output/game.svg
When writing to stdout (or with -o -), image references are included in the DOT syntax
but no SVG files are written — useful for piping the DOT string while skipping heavy image generation:
chesstree -i game.pgn -f dot -o - | dot -Tsvg -o game.svg # no SVGs written
To omit images entirely from DOT output:
chesstree -i game.pgn -f dot --images none -o game.dot
Round-trip a game through JSON:
chesstree -i game.pgn -o game.json
chesstree -i game.json -f pgn -o game_restored.pgn
Versioning
chesstree uses two independent version numbers:
| Version | Scheme | Where | Bumped when |
|---|---|---|---|
| Tool version | CalVer YYYY.N |
pyproject.toml, --version output |
Any release |
| Schema version | SemVer | schema_version field in JSON/EDN output |
Schema changes only |
Running chesstree --version shows both:
chesstree 2026.1 (schema 1.2.0)
The two versions are independent: a tool release that fixes a bug or adds a new output format does not change the schema version. See docs/schema.md for the full schema compatibility contract.
Credits
chesstree is built heavily on top of python-chess
by Niklas Fiekas. python-chess provides PGN parsing, board state management, FEN generation, NAG
handling, and SVG board rendering — the core of what makes chesstree work.
Development environment
Setup
git clone https://github.com/benedekfazekas/chesstree
cd chesstree
python3 -m venv .venv
source .venv/bin/activate # on Windows: .venv\Scripts\activate
pip install -e ".[dev]"
The -e flag installs the package in editable mode: changes to the source files under chesstree/ take effect immediately without reinstalling.
Running tests
pytest
With coverage:
pytest --cov=chesstree --cov-report=term-missing
Using the CLI during development
Because the package is installed in editable mode, you can run chesstree directly from the terminal (with the virtual environment active) and your latest source changes are picked up immediately:
chesstree -i path/to/game.pgn -o /tmp/out.json
Alternatively, you can invoke the module directly without installing:
python -m chesstree.cli -i path/to/game.pgn
AI assisted development
The project is developed in a supervised AI development manner where I try to keep a close eye on design choices, read and review the generated code, test the output manually etc.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file chesstree-2026.1.tar.gz.
File metadata
- Download URL: chesstree-2026.1.tar.gz
- Upload date:
- Size: 69.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
af81185b64f2289fee2722490428b1a04cad0dd8ec1a16f7f9eec1185b68cf84
|
|
| MD5 |
1f0342ddc23682e357adee9cbbe624a2
|
|
| BLAKE2b-256 |
cbc4037b4f33e054a96fe2a6b6a11db6dcc42e1b9360de4f2444f5482e4debff
|
Provenance
The following attestation bundles were made for chesstree-2026.1.tar.gz:
Publisher:
publish.yml on benedekfazekas/chesstree
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
chesstree-2026.1.tar.gz -
Subject digest:
af81185b64f2289fee2722490428b1a04cad0dd8ec1a16f7f9eec1185b68cf84 - Sigstore transparency entry: 1437874381
- Sigstore integration time:
-
Permalink:
benedekfazekas/chesstree@373baa682d084d6f4af79b21bd8b148fb0197e2e -
Branch / Tag:
refs/tags/v2026.1 - Owner: https://github.com/benedekfazekas
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@373baa682d084d6f4af79b21bd8b148fb0197e2e -
Trigger Event:
push
-
Statement type:
File details
Details for the file chesstree-2026.1-py3-none-any.whl.
File metadata
- Download URL: chesstree-2026.1-py3-none-any.whl
- Upload date:
- Size: 74.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1b17f2e7e2b46b1bd29cc4f68c0ecbd02dcc9aac532383b0b638ea02b6f896da
|
|
| MD5 |
e71ca63437da40460a86ff838ab276f5
|
|
| BLAKE2b-256 |
5926117d9e10a0f125f0c941442d13fb1bd4dfc3ffc69f9cb60b710803411091
|
Provenance
The following attestation bundles were made for chesstree-2026.1-py3-none-any.whl:
Publisher:
publish.yml on benedekfazekas/chesstree
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
chesstree-2026.1-py3-none-any.whl -
Subject digest:
1b17f2e7e2b46b1bd29cc4f68c0ecbd02dcc9aac532383b0b638ea02b6f896da - Sigstore transparency entry: 1437874393
- Sigstore integration time:
-
Permalink:
benedekfazekas/chesstree@373baa682d084d6f4af79b21bd8b148fb0197e2e -
Branch / Tag:
refs/tags/v2026.1 - Owner: https://github.com/benedekfazekas
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@373baa682d084d6f4af79b21bd8b148fb0197e2e -
Trigger Event:
push
-
Statement type: