Skip to content

params

"params": {
"paper": { "type": "color", "default": "#fbf6e6" },
"softness": { "type": "number", "default": 0, "min": 0, "max": 4,
"description": "Blur over the finished tile, in px." },
"labels": { "type": "bool", "default": true }
}

Reference a param with $name anywhere a scalar field lives:

"bg": { "op": "solid", "color": "$paper" },
"out": { "op": "blur", "input": "@composite", "sigma": "$softness" }
Field Applies to Meaning
type all color, number, or bool — required
default all used when the caller supplies nothing — required
min / max number validated on every override; max also serves as the static bound for padding-determining fields
description all surfaced in the generated schema and the editor’s params panel

Colours are #rrggbb or #rrggbbaa. Bools are true / false.

Terminal window
# CLI — repeatable, validated against the declarations
ezu tile --style watercolor.json --tile 13/7276/3225 \
--param 'paper=#ffe0f0' --param softness=2 --out tile.png
# Tile server — query-string overrides on the tile endpoint
curl 'http://127.0.0.1:8080/tiles/13/7276/3225.png?paper=%23ffe0f0&softness=2'
let mut params = ezu::graph::ParamValues::new();
params.set("softness".into(), parse_param_value(&doc.params, "softness", "2")?);

Every path validates: a number outside min/max, a malformed colour, or a non-boolean fails before rendering. An unset param falls back to its default.

Params resolve at render time, so nothing rebuilds when a value changes, and the cache keys on the values each node actually reads — flipping one param re-evaluates only its subtree. That is what makes the editor’s params panel responsive on a style that takes hundreds of milliseconds cold.

Document::params_schema() derives a JSON Schema of the declarations — types, defaults, ranges, descriptions:

Terminal window
curl http://127.0.0.1:8080/style/params

The same document is available wherever the style is loaded: the tile server serves it at /style/params, and in the browser it is the paramsSchema getter on the wasm Renderer.

Drive your own sliders and colour pickers off that rather than parsing the style. The live editor’s panel is generated from exactly this.

Canvas padding is fixed at build time, so a field that decides it — blur sigma and friends — must carry an upper bound the build can see. A literal is its own; a $param qualifies only if it declares max:

"softness": { "type": "number", "default": 0, "min": 0, "max": 4 } // ✅ usable as a blur sigma
"softness": { "type": "number", "default": 0 } // ❌ build error

A @node port carries no bound, so the style states one beside it with a <field>-max — see padding and neighbours.

The bound is what the graph budgets padding for, so keep it as tight as the style actually needs — an unnecessarily large max costs render time on every tile.

Some fields are not scalars but tables — a colour ramp’s stops, a palette, a dash pattern. A $param works in each entry too:

"tint": { "op": "color-ramp", "field": "@dem",
"stops": [ { "value": 0, "color": "$lowland" },
{ "value": "$treeline", "color": "$upland" } ] }

Two differences from a scalar field. A table entry takes a literal or a $param but not a @node: a port needs a fixed name and its own slot on the node, and a table of unknown length has neither. And a ramp is sorted by value on every render rather than at build time, since a param can reorder it — so stops need not be declared in order.

Params are inputs, not expressions. To combine them, wire scalars through the graph — a $param argument keeps its runtime-override behaviour all the way through:

"z": { "op": "zoom" },
"zfrac": { "op": "math", "fn": "div", "a": "@z", "b": 16 },
"lu_alpha": { "op": "math", "fn": "mul", "a": "$landuse-alpha", "b": "@zfrac" }

watercolor.json is a complete parametric style.

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.