Skip to content

nodes and output

"nodes": {
"bg": { "op": "solid", "color": "#fbf6e6" },
"out": { "op": "blur", "input": "@bg", "sigma": 2 }
},
"output": "@out"

Each entry is id: { "op": "<op-name>", …fields }. The id is yours; the op name selects the implementation from the registry (all 82 are in the node catalog); everything else is that op’s fields.

Ids may contain letters, digits, _ and -, and must not start with a digit. Function expansion produces ids of the form <call>/<node>, so avoid / in ids you write yourself.

The order of entries is meaningless. Evaluation order comes from the references. Group and name nodes for a human reader.

A field whose value is "@other" is a connection. What kind of connection it is depends on the field:

"features": "@water_f" // a Features port
"input": "@bg" // a Raster (or Sprite) port
"brush": "@pencil" // a Brush port
"sigma": "@computed" // a Scalar port

The op declares each port’s accepted kinds, and build_graph checks them — see ports and types. A numeric field usually accepts a literal, $param, or @node interchangeably.

Fan-out is free and encouraged: several consumers may reference one node, and it is evaluated once because the cache is content-addressed.

A layer. Data always enters through a features node, which is where the filter and zoom gates live, and is shared by every consumer of that layer:

"roads_f": { "op": "features", "source": "basemap", "layer": "roads",
"filter-expr": ["==", ["get", "kind"], "highway"],
"min-zoom": 8 },
"roads": { "op": "stroke", "features": "@roads_f", "color": "#333", "width-px": 1.5 }

Stacking. Compositing is painter’s algorithm as a graph: stack draws its layers bottom to top, each over the ones below. ezu translate emits exactly this from a MapLibre layer list:

"out": { "op": "stack", "layers": ["@bg", "@landuse", "@water", "@roads"] }

A long list is split into a chain of narrower stack nodes, each taking the one below as its first layer, to the same pixels. blend composites a single pair, with a choice of blend mode and composite.

Four layers composited by one `stack` node — the shape `ezu translate` emits from an ordered MapLibre layer list.
Four layers composited by one stack node — the shape ezu translate emits from an ordered MapLibre layer list.

Masking. blend with composite: "source-atop" clips the result to the base’s alpha — Photoshop’s clipping mask, and how a fill-pattern gets confined to its polygon.

Toggling a branch. switch passes one of its inputs through: the first of its cases whose condition holds, as MapLibre’s case does, or one of its branches picked by name, as match does. A single case is an on/off toggle, which is how --keep-hidden preserves a hidden MapLibre layer without drawing it:

"labels": { "op": "switch",
"cases": [ { "when": "$labels", "then": "@with_labels" } ],
"default": "@plain" },
"seasonal": { "op": "switch",
"cases": [ { "when": "$winter", "then": "@snow" },
{ "when": "@deep", "then": "@detail" } ],
"default": "@summer" },
"themed": { "op": "switch", "select": "$theme",
"branches": { "warm": "@warm", "cool": "@cool" }, "default": "@plain" }

An older two-way form, a and b picked by select, is deprecated. It still works, and the build warns with the cases or branches that does the same.

Only the branch taken is rendered. When the choice rests on literals or params the others are skipped outright, with everything only they need; a @node condition is known only once evaluated, so the branches from it on are rendered alongside it.

"output": "@out"

output must resolve to a node whose kind is Raster. Notably:

  • A Sprite cannot be the output (it is not canvas-sized) — put it through a placement op first.
  • Features, Brush, Scalar, Labels and ScalarField cannot be the output either. color-ramp is the usual way to turn a ScalarField into a Raster.

Nodes not reachable from output are still validated, and still evaluated on every render, so a branch left unconnected costs as much as one in use. To turn a branch off without deleting it, put it behind a switch: a render skips only the nodes that nothing reads but a branch a switch did not pick.

Terminal window
ezu graph style.json # Mermaid `graph LR` of the dependencies
ezu --verbose tile … # per-node op, cache hit/miss, output shape, duration

The Mermaid diagram is the fastest way into a translated recipe you did not write.

Map renders on this site are made fromOpenStreetMap data viaProtomaps (© OpenStreetMap contributors), elevation from Re:Earth Terrain,Mapterhorn andEGM2008 (NGA), and aerial imagery from GSI Japan(© 国土地理院). The painterly styles use CC0 brushes byDavid Revoy.