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.
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.
Requires Python 3.9 or later.
pip install chesstreeOr with pipx for an isolated environment (recommended for CLI tools):
pipx install chesstreeThe chesstree command is added to your PATH automatically.
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: 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 |
Convert a PGN file to JSON:
chesstree -i game.pgn -o game.jsonConvert a PGN file to EDN:
chesstree -i game.pgn -f edn -o game.ednPrint compact JSON to stdout:
cat game.pgn | chesstree -i - -cGenerate board images from Black's perspective (dot/dothtml output):
chesstree -i game.pgn -f dot -o game.dot -bThe --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.
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 variationsGenerate images for every move:
chesstree -i game.pgn -f dot -o game.dot --images allGenerate images only at commented moves:
chesstree -i game.pgn -f dot -o game.dot --images commentedGenerate images at both variation endpoints and commented moves:
chesstree -i game.pgn -f dot -o game.dot --images variations commentedOmit all board images:
chesstree -i game.pgn -f dot -o game.dot --images noneBy 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-highlightConvert a chesstree JSON file back to PGN:
chesstree -i game.json -f pgn -o game_restored.pgnExport a PGN game to a GraphViz DOT file for visualisation:
chesstree -i game.pgn -f dot -o game.dotRender the DOT file to SVG using GraphViz:
dot -Tsvg game.dot -o game.svgExport from chesstree JSON to DOT:
chesstree -i game.json -f dot -o game.dotThe 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.htmlThe 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 -You can supply your own template with --template:
chesstree -i game.pgn -f dothtml --template my_template.html -o game.htmlThe 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.
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.htmlFeatures 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.
- Collapse/Expand All toggle — a single smart button in the toolbar collapses or expands the entire tree at once. The button label and icon reflect the current state:
⊟ Collapse All(all expanded),⊞ Expand All(all collapsed), or◑ Collapse All(mixed — some nodes manually adjusted). Clicking from a mixed state always collapses all. - Pan and zoom via scroll and drag.
- Drag-to-reposition individual nodes.
- Orientation toggle — switch between left-to-right (default) and top-to-bottom tree layout with the ↔ LR / ↕ TB buttons.
- 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.
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 noneSVG files are written alongside the HTML file. When writing to stdout, image references are included but no SVG files are written.
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 -aThis significantly increases file size (one small SVG per move) so it is off by default.
chesstree -i game.pgn -f d3html --template my_d3_template.html -o game.htmlThe 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.
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.svgWhen 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 writtenTo omit images entirely from DOT output:
chesstree -i game.pgn -f dot --images none -o game.dotRound-trip a game through JSON:
chesstree -i game.pgn -o game.json
chesstree -i game.json -f pgn -o game_restored.pgnchesstree 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.
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.
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.
pytestWith coverage:
pytest --cov=chesstree --cov-report=term-missingBecause 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.jsonAlternatively, you can invoke the module directly without installing:
python -m chesstree.cli -i path/to/game.pgnThe 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.