How YAML frontmatter becomes an SVG board diagram. One function call, no translation layers.
The pipeline has four stages:
Frontmatter YAML
↓ parseFrontmatter()
Parsed meta object
↓ cascadeResolve({ surface, family, variant })
Resolved cascade (topology + surface + render + setup + pieces)
↓ renderFromEngine(resolved, { pieceImages })
SVG string
The entry point is renderFromEngine() in packages/render/src/render-engine.js. It takes a resolved cascade object and returns a complete SVG string.
packages/schema/src/cascade-resolver.js deep-merges three layers:
rulebook.md engine blockThe result is a flat object with topology, surface, render, setup, pieces, players, components, and meta fields. Variant values override family values; family values override surface defaults.
packages/schema/src/produce-layout.js takes the resolved cascade and produces a layout configuration object for the appropriate topology renderer. It handles:
Output: { type, config } where config is everything the topology renderer needs.
Each topology exports a render function that produces an element array:
renderGridLayout(rows, cols, config) → { width, height, elements, cells, labels }
renderHexLayout(config) → { width, height, elements }
renderGraphLayout(config) → { width, height, elements }
renderPitLayout(config) → { width, height, elements }
renderTrackLayout(config) → { width, height, elements }
Elements are plain objects: { tag: 'rect', attrs: { x, y, width, height, fill } }. They're serialised to SVG strings by serialize-layout.js.
The orchestrator. It:
produceLayout() → topology rendererFor grid topologies, pieces are rendered externally (after the board). The engine:
<image> elements at cell coordinates<circle> for stone/man/king types without imagesFor hex/pit/track/graph, pieces are rendered internally by the topology renderer (via _pieceImages and _parsedSetup on the render config).
Games like Alice Chess have multiple boards. When topology.layers is set and setup is an array of FENs, the engine renders each board as a separate layer and composites them side-by-side with labels.
topology:
type: grid
rows: 8
cols: 8
layers: 2
layer_labels: [Board A, Board B]
setup:
- rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR
- 8/8/8/8/8/8/8/8