Geometry
Features -> Features transforms. They rewrite geometry before anything is painted, so downstream paint ops stay simple.
20 ops: bbox, boundary, buffer, centroid, contour, convex-hull, dash, densify, dot-density, feature-boolean, hatch, medial-axis, resample, simplify, smooth, transform, triangulate, voronoi, voronoi-fracture, wave.
Ops with no demo image below produce no visible result on their own — they feed other nodes rather than paint.
Axis-aligned bounding box of every input vertex (pooled across polygons, lines, and points), emitted as a single rectangular polygon.

| Field | Type | Required | Description |
|---|---|---|---|
features |
@node |
yes | Reference to another node (@name). |
boundary
Section titled “boundary”Convert each polygon to its boundary rings (exterior + holes) as polylines. Existing polylines and points pass through.

| Field | Type | Required | Description |
|---|---|---|---|
features |
@node |
yes | Reference to another node (@name). |
buffer
Section titled “buffer”Minkowski sum / offset with a disk. Polygons accept negative distance for erosion; lines and points are inflated by |distance|.

| Field | Type | Required | Description |
|---|---|---|---|
distance |
number | $param | @node |
yes | Offset distance in tile pixels. Positive inflates, negative erodes (polygons only). |
features |
@node |
yes | Reference to another node (@name). |
join |
miter | round | bevel |
no | — |
centroid
Section titled “centroid”Replace input geometry with one point per polygon / polyline centroid.

| Field | Type | Required | Description |
|---|---|---|---|
features |
@node |
yes | Reference to another node (@name). |
include-lines |
boolean | no | — |
include-polygons |
boolean | no | — |
contour
Section titled “contour”Isolines from a ScalarField (marching squares with linear sub-cell interpolation), chained into polylines. One feature group per level with properties {"level": <number>}, so data-driven paint can style by level. Levels are base + k·interval, or the explicit levels array (which overrides both), clamped by min/max. Chain dem → contour → stroke for elevation contours, or density → contour → stroke for point-density isolines.
| Field | Type | Required | Description |
|---|---|---|---|
base |
number | $param | @node |
no | Offset the interval grid: levels sit at base + k·interval. |
field |
@node |
yes | Reference to another node (@name). |
interval |
number | $param | @node |
no | Spacing between levels, in field units. Required unless levels is given. |
levels |
array of number | string | no | Explicit levels to extract; overrides interval/base. A level may be a $param; the list is sorted and de-duplicated per render. |
max |
number | no | Emit no level above this value. |
min |
number | no | Emit no level below this value. |
convex-hull
Section titled “convex-hull”Convex hull of every input vertex as a single polygon.

| Field | Type | Required | Description |
|---|---|---|---|
features |
@node |
yes | Reference to another node (@name). |
Cut polylines into dash/gap segments. Lengths are in canvas pixels (consistent with *-px brush parameters). Polygons and points pass through unchanged.

| Field | Type | Required | Description |
|---|---|---|---|
dash-px |
number (0…∞) | $param | @node |
yes | — |
features |
@node |
yes | Reference to another node (@name). |
gap-px |
number (0…∞) | $param | @node |
yes | — |
phase-px |
number | $param | @node |
no | Initial offset into the dash/gap pattern, in pixels. May be negative. |
densify
Section titled “densify”Insert intermediate vertices so no segment of any polyline or polygon ring exceeds target-px. Original vertices are preserved.
| Field | Type | Required | Description |
|---|---|---|---|
features |
@node |
yes | Reference to another node (@name). |
target-px |
number (0…∞) | yes | — |
dot-density
Section titled “dot-density”Scatter points inside polygons at a per-feature areal density (dot density map).

density-expr instead of stating one constant. style| Field | Type | Required | Description |
|---|---|---|---|
density |
number (0…∞) | $param | @node |
no | Constant density in units per square kilometre, used where density-expr is absent. |
density-expr |
— | no | Per-feature MapLibre expression giving density in units per square kilometre, e.g. [“/”, [“get”, “POP”], [“get”, “AREA_KM2”]]. |
dot-value |
number | $param | @node |
no | Units one dot stands for. 100 means one dot per 100 people. |
features |
@node |
yes | Reference to another node (@name). |
jitter |
number (0…1) | $param | @node |
no | How far a dot may stray from its cell centre, as a fraction of the cell. 0 leaves a visible grid. |
seed |
integer (0…∞) | no | Lattice salt; change it to reshuffle the dots without changing their density. |
spacing-px |
number | $param | @node |
no | Lattice cell size in screen pixels. One dot per cell at most, so this caps how dense the scatter can get. |
feature-boolean
Section titled “feature-boolean”Polygon boolean operation between two Features inputs. Subject = a, clip = b. Lines and points on either input are dropped — booleans are defined on polygons only.
| Field | Type | Required | Description |
|---|---|---|---|
a |
@node |
yes | Reference to another node (@name). |
b |
@node |
yes | Reference to another node (@name). |
Parallel-line hatching of input polygons.

| Field | Type | Required | Description |
|---|---|---|---|
angle-deg |
number | $param | @node |
no | Hatch direction (degrees, CCW from +X). |
features |
@node |
yes | Reference to another node (@name). |
phase |
number | $param | @node |
no | Per-line offset in spacing units (0..1 cycles through one period). |
spacing |
number (0…∞) | $param | @node |
yes | Perpendicular spacing between consecutive lines, in tile pixels. |
medial-axis
Section titled “medial-axis”Approximate medial axis of each input polygon, emitted as polylines. Smaller densify-px → more accurate but slower; min-branch-px prunes short side branches after stitching.

| Field | Type | Required | Description |
|---|---|---|---|
densify-px |
number (0.5…∞) | $param | @node |
no | — |
features |
@node |
yes | Reference to another node (@name). |
min-branch-px |
number (0…∞) | $param | @node |
no | — |
resample
Section titled “resample”Resample each polyline / polygon ring to evenly-spaced vertices at spacing-px along arc length. Original vertices are not preserved.
| Field | Type | Required | Description |
|---|---|---|---|
features |
@node |
yes | Reference to another node (@name). |
spacing-px |
number (0…∞) | yes | — |
simplify
Section titled “simplify”Douglas-Peucker simplify polylines and polygon rings; points pass through.

| Field | Type | Required | Description |
|---|---|---|---|
epsilon |
number (0…∞) | $param | @node |
yes | Max perpendicular distance a vertex may be from the simplified line, in tile pixels. |
features |
@node |
yes | Reference to another node (@name). |
smooth
Section titled “smooth”Chaikin corner-smoothing over each input polyline and polygon ring. Each iteration roughly doubles the vertex count; 2–3 iterations are usually enough.

| Field | Type | Required | Description |
|---|---|---|---|
features |
@node |
yes | Reference to another node (@name). |
iterations |
integer (0…8) | no | — |
transform
Section titled “transform”Translate / rotate / scale every input vertex. Rotation happens around pivot (defaults to the origin) before the final translation. The scalar fields take a $param or an @node port and follow it per render; the [x, y] arrays are literal, and seed the matching scalars. Uniform scale multiplies the per-axis factors.

| Field | Type | Required | Description |
|---|---|---|---|
features |
@node |
yes | Reference to another node (@name). |
pivot |
array of number | no | Literal [x, y] rotation centre in feature space. |
rotation-deg |
number | $param | @node |
no | — |
scale |
number | $param | @node |
no | — |
scale-x |
number | $param | @node |
no | — |
scale-xy |
array of number | no | Literal per-axis scale. scale-x / scale-y override it; uniform scale multiplies it. |
scale-y |
number | $param | @node |
no | — |
translate |
array of number | no | Literal [x, y] shift. translate-x / translate-y override it. |
translate-x |
number | $param | @node |
no | — |
translate-y |
number | $param | @node |
no | — |
triangulate
Section titled “triangulate”Delaunay triangulation of the input’s point set. Each triangle becomes a closed polygon. Polygons and lines on input are ignored.

centroid first. style| Field | Type | Required | Description |
|---|---|---|---|
features |
@node |
yes | Reference to another node (@name). |
voronoi
Section titled “voronoi”Voronoi diagram of the input’s point set. Output is a Features value carrying the Voronoi edges as polylines (each polyline is a single 2-point edge). Polygons and lines in the input are ignored — use centroid to derive points from them.

| Field | Type | Required | Description |
|---|---|---|---|
features |
@node |
yes | Reference to another node (@name). |
voronoi-fracture
Section titled “voronoi-fracture”Fracture each polygon in features into Voronoi sub-cells using the points in seeds as Voronoi sites. Cells are clipped against the source polygon. Lines/points on features and lines/polygons on seeds are ignored.

| Field | Type | Required | Description |
|---|---|---|---|
features |
@node |
yes | Reference to another node (@name). |
seeds |
@node |
yes | Reference to another node (@name). |
Displace polylines laterally with a sine wave. Lengths in canvas pixels. Sharp corners may kink (no tangent smoothing). Polygons/points pass through.

| Field | Type | Required | Description |
|---|---|---|---|
amplitude-px |
number | $param | @node |
yes | Peak lateral deviation in pixels. May be negative to flip phase. |
features |
@node |
yes | Reference to another node (@name). |
noise-amp-px |
number | $param | @node |
no | Peak 1D-value-noise jitter added on top of the sine, in pixels. 0 = pure sine (default). |
noise-scale-px |
number (0.5…∞) | $param | @node |
no | Cell length of the noise jitter along arc length, in pixels. Defaults to wavelength-px. |
phase-px |
number | $param | @node |
no | Offset into the wave at the polyline start, in pixels. |
samples-per-wavelength |
number (2…256) | no | Subdivisions per wavelength. Higher = smoother but more vertices. Default 16. |
seed |
integer (0…∞) | no | Optional explicit u32 seed for the world-anchored 2D value noise. Default: a fixed constant so adjacent tiles agree across the seam. |
wavelength-px |
number (0.5…∞) | $param | @node |
yes | — |
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.