Render your first tile
The shortest possible render
Section titled “The shortest possible render”hillshade.json keeps all of its data remote, so this needs only the CLI:
ezu tile \ --style https://raw.githubusercontent.com/reearth/ezu/main/crates/ezu/examples/styles/hillshade.json \ --tile 11/1813/807 --out fuji.png--tile takes z/x/y in the usual XYZ scheme. Output format follows the
extension (.png or .webp, lossless).
A style from scratch
Section titled “A style from scratch”A style is a JSON document with a nodes map and an output reference. Nothing
else is required. Start with a solid colour:
{ "name": "mine", "nodes": { "bg": { "op": "solid", "color": "#fbf6e6" } }, "output": "@bg"}ezu tile --style mine.json --tile 13/7276/3225 --out mine.png
A cream square. Now add data. Declare an MVT source, select a layer, fill it, and composite the fill over the background:
{ "name": "mine", "pad": 8, "sources": { "basemap": { "type": "mvt", "url": "https://papers.reearth.land/protomaps/tilejson.json" } }, "nodes": { "bg": { "op": "solid", "color": "#fbf6e6" }, "water_f": { "op": "features", "layer": "water" }, "water": { "op": "fill-solid", "features": "@water_f", "fill": "#5876a0" }, "out": { "op": "blend", "base": "@bg", "over": "@water" } }, "output": "@out"}
That URL is a live TileJSON endpoint, so the snippet renders as written — the data is © OpenStreetMap contributors, © Protomaps. Swap it for your own MVT or PMTiles source when you have one.
Three things to notice:
@namewires nodes together.fill-solidreads its geometry from thefeaturesnode;blendreads two rasters. That is the whole graph syntax.- The order in
nodesmeans nothing. Evaluation order comes from the references, not the file. Theblendchain is what puts water on top of the background. outputmust be aRaster.featuresproducesFeatures, which cannot be the output — the graph type-checks this at build time, before any pixel is drawn.
Make it painterly
Section titled “Make it painterly”Swap the flat fill for a scatter-dab fill and the map stops looking styled:
"water": { "op": "fill-dabs", "features": "@water_f", "color": "#5876a0", "opacity": 0.22, "radius-px": 7, "spacing-px": 3}
fill-dabs places dabs on a world-anchored grid, so the wash continues across
tile borders instead of restarting at each edge. Blur the result and it reads as
wet paint:
"soft": { "op": "blur", "input": "@water", "sigma": 3 },"out": { "op": "blend", "base": "@bg", "over": "@soft" }
Blur needs pixels from beyond the tile edge to stay seamless, which is what pad
is for — and you do not have to size it. The graph knows how far each filter
reaches and the renderer pads the canvas to match, so the pad: 8 above could be
left out entirely. ezu check prints what it settled on:
ezu check mine.json --no-fetch# INFO pad: 8 declared, 17 needed — rendering with 17Write pad when you paint wider than your geometry — a thick stroke, a large
dab radius — since a width can be an expression and so cannot be known in
advance. See padding and neighbours.
For real brush strokes, load a brush through sources and use line or
stroke:
"sources": { "pencil": { "type": "brush", "src": "file:brushes/pencil.myb" }},"nodes": { "roads": { "op": "line", "features": "@roads_f", "brush": "@pencil", "color": "#333", "radius-px": 1.2 }}Check before you render
Section titled “Check before you render”ezu check mine.json # parse + build graph + resolve assetsezu check mine.json --no-fetch # offline: parse + graph onlyezu check exits non-zero on a bad reference, a type mismatch, a cycle, or a
missing asset — the same validation the renderer runs, without rendering. Wire it
into CI (validate in CI).
To see the shape of what you built:
ezu graph mine.json # Mermaid `graph LR` of the node dependencies- The live editor — edit and watch the tile update.
- Node catalog — all 82 ops with their fields.
- Bounding boxes and pyramids — render more than one tile.
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.