Skip to content
Internals
Draft. This essay is a stub or a work in progress — read it as a sketch, not settled documentation.

Architecture Overview

This is the map. It sketches how a GoFish chart goes from a declarative description to an SVG, and points at the essays that cover each part in depth.

A chart is a tree

GoFish is declarative: you never tell it to draw — you describe what the chart is, and the engine works out the rest. That description is an abstract syntax tree of GoFishNodes. Two kinds of node make up the tree:

  • Marks — the things you can see: rect, ellipse, line, area, text.
  • Graphical operators — composition: stackX, spread, layer, connect, coord, and so on. An operator arranges its children; it has no appearance of its own.

The modern v3 fluent APIchart(data).flow(...).mark(...) — is sugar. It desugars into exactly this tree, built from the mark and operator factories.

Three passes

Rendering the tree is not one traversal but three, each answering a different question. A node implements only the passes it participates in.

1 · Domain inference. Before anything can be sized, the engine works out the data ranges in play — the domains. GoFish distinguishes a node's underlying space (is this dimension a position, a size, ordinal, undefined?) and infers position and size domains separately. This pass leans on the monotonic algebra to track, symbolically, how each subtree depends on the data — and to prune subtrees that don't depend on it at all.

2 · Layout. With domains known, each node computes its size. Layout dispatches on the underlying-space kind: a SIZE dimension resolves through the monotonic machinery, a POSITION dimension through position scales. Bounding boxes (the bbox model) are the common currency.

3 · Placement & render. Final absolute positions are assigned, and each node emits SVG. Rendering is reactive — it runs through SolidJS — so a chart can update without a full rebuild. The coord operator is the notable special case: it flattens its subtree into a flat, absolutely-positioned list before applying its coordinate transform.

The full, code-level walkthrough of all three passes is Layout & Render Passes.

Cross-cutting machinery

A few systems thread through every pass rather than belonging to one:

  • Contexts. scopeContext, scaleContext, keyContext carry variable scoping, color/axis scales, and named-element tracking down the tree.
  • Coordinate transforms. linear, polar, bipolar, wavy, clock — pluggable mappings from one plane to another, applied during render.
  • Names & scoping. Marks can be named and referenced across charts via select(); scoping is deliberately hygienic.

Where to go next