Raster
Image-processing ops over Raster (and, where noted, Sprite): generators, composition, colour, warp, morphology, dither, terrain.
40 ops: blend, blur, brightness-contrast, channel-shuffle, circle, color-ramp, color-to-alpha, density, dilate, displace, dither, edge-detect, erode, field-math, flow-field, flow-smear, gradient-conic, gradient-diamond, gradient-linear, gradient-radial, hillshade, hsl, invert, levels, map-range, mix, mosaic, noise, place, posterize, quantize, saturate, sharpen, slope, solid, stack, threshold, tiling, vibrance, warp.
Composite over onto base with a W3C blend mode. clip: true clips result to base alpha (Photoshop-style clipping mask, i.e. source-atop): the result takes the base’s alpha, so clipping onto a 0.12-alpha wash caps the whole overlay at 0.12 — when you mean “restrict this to an area”, give an opaque shape as mask instead. composite: "destination-out" makes over erase base (brush-eraser effect when over is a brush-shaped raster). Optional mask raster’s alpha modulates source coverage.


| Field | Type | Required | Description |
|---|---|---|---|
base |
@node |
yes | Reference to another node (@name). |
clip |
boolean | $param | @node |
no | — |
composite |
over | source-over | destination-out | dest-out | erase |
no | — |
mask |
@node |
no | Reference to another node (@name). |
mode |
normal | multiply | screen | overlay | darken | lighten | color-dodge | color-burn | hard-light | soft-light | difference | exclusion | hue | saturation | color | luminosity |
no | — |
opacity |
number (0…1) | $param | @node |
no | — |
over |
@node |
yes | Reference to another node (@name). |
Gaussian blur on a raster, sprite or scalar field; the output is the same kind as the input. Rasters are blurred in fixed point and fields in a fixed order of float operations, so both render the same bytes on every host. Blurring an elevation field (dem) before hillshade, slope or flow-field generalises the terrain: small gullies drop out and the major ridges stay, so the shading follows the ridge crests. A field keeps its nodata value and geographic scale; nodata samples are left out of the average, and a pixel with nothing but nodata in reach stays nodata. Grows upstream pad by 3σ, so sigma needs an upper bound the build can see: a literal, a $param with max, or sigma-max alongside an @node port (the port’s value is then clamped to it).


| Field | Type | Required | Description |
|---|---|---|---|
input |
@node |
yes | Reference to another node (@name). |
sigma |
number (0…∞) | $param | @node |
yes | — |
sigma-max |
number (0…∞) | no | Upper bound on sigma for padding, required when sigma is an @node port. Values above it are clamped. |
brightness-contrast
Section titled “brightness-contrast”Linear brightness shift and contrast slope around mid-gray. Both in [-1, 1], 0 = no change.


| Field | Type | Required | Description |
|---|---|---|---|
brightness |
number (-1…1) | $param | @node |
no | — |
contrast |
number (-1…1) | $param | @node |
no | — |
input |
@node |
yes | Reference to another node (@name). |
channel-shuffle
Section titled “channel-shuffle”Rearrange RGBA channels. Each output (r/g/b/a) names which input channel feeds it, or a constant (0/1). Identity by default. Operates in non-premultiplied sRGB.


| Field | Type | Required | Description |
|---|---|---|---|
a |
r | g | b | a | 0 | 1 |
no | — |
b |
r | g | b | a | 0 | 1 |
no | — |
g |
r | g | b | a | 0 | 1 |
no | — |
input |
@node |
yes | Reference to another node (@name). |
r |
r | g | b | a | 0 | 1 |
no | — |
circle
Section titled “circle”Centred disk source. kind: raster (default) renders at canvas size with radius = radius-frac × tile-size. kind: sprite renders at width-px × height-px with radius = radius-frac × min(width, height).

circle layers. style| Field | Type | Required | Description |
|---|---|---|---|
color |
color | $param | @node |
yes | sRGB hex color, $param reference, or @node scalar port. |
hardness |
number (0…1) | $param | @node |
no | — |
height-px |
integer (1…∞) | no | — |
kind |
raster | sprite |
no | — |
radius-frac |
number (0…1) | $param | @node |
yes | — |
width-px |
integer (1…∞) | no | — |
color-ramp
Section titled “color-ramp”Map a ScalarField (or a Raster, by its luminance — a gradient map) to colour through a stop table, interpolating between stops in space. Samples outside [stops[0].value, stops[-1].value] clamp to the end colours. A field’s missing samples (NaN or its nodata value) are fully transparent. Canonical use case is hypsometric tinting over a DEM (stops[i].value = elevation in metres). Give ramp-expr instead of stops to drive the ramp from a MapLibre color expression over heatmap-density.
| Field | Type | Required | Description |
|---|---|---|---|
field |
@node |
yes | Reference to another node (@name). |
opacity |
number (0…1) | $param | @node |
no | Uniform output-alpha multiplier (layer opacity). Default 1.0. Feed an expr node via @node for a zoom curve. |
ramp-expr |
— | no | A MapLibre color expression over heatmap-density (e.g. a heatmap-color value). When present it overrides stops: the expression is baked into a 256-entry LUT per eval (zoom in context), inputs clamp to [0, 1], and lookups interpolate linearly between entries — mirroring MapLibre’s 256-px ramp texture. stops is only required when this is absent. |
space |
rgb | hsl | hsv | hcl | lab |
no | Colour space the stops interpolate in. rgb (default) is a straight sRGB lerp; hsl/hsv/hcl interpolate hue on the shortest path; hcl/lab are perceptual (ported from the MapLibre style spec). |
stops |
array of object | no | Either half of a stop may be a $param, so a ramp can be recoloured or rescaled at render time. The table is sorted by value on every eval, so a param may reorder it. |
color-to-alpha
Section titled “color-to-alpha”Make pixels close to color transparent. Distance ≤ threshold → alpha 0, distance ≥ threshold + softness → alpha unchanged, linear in between. Distance is Chebyshev (max per-channel) in non-premultiplied sRGB.


| Field | Type | Required | Description |
|---|---|---|---|
color |
color | $param | @node |
yes | sRGB hex color, $param reference, or @node scalar port. |
input |
@node |
yes | Reference to another node (@name). |
softness |
number (0…1) | $param | @node |
no | — |
threshold |
number (0…1) | $param | @node |
no | — |
density
Section titled “density”Kernel-density estimate of the input’s point features as a ScalarField (the MapLibre heatmap kernel: support = radius px, falloff exp(-0.5·3²·(d/radius)²)/√(2π)). Points only — lines and polygons contribute nothing, so a line or polygon layer comes out an all-zero field with no error; put centroid in front of it. Buffer points outside the tile still contribute, so tile borders match. Output is raw accumulated density, unclamped and scaling with intensity, so probe its range before placing ramp stops. Map it with color-ramp or extract isolines with contour. Grows upstream pad by radius.
| Field | Type | Required | Description |
|---|---|---|---|
features |
@node |
yes | Reference to another node (@name). |
intensity |
number (0…∞) | $param | @node |
no | Global multiplier on every point’s contribution. |
intensity-expr |
— | no | A MapLibre number expression giving the intensity multiplier, evaluated per feature group; overrides the constant intensity. |
radius |
number (0…∞) | $param | @node |
no | Kernel radius in px. Pad is computed at build time, so this needs a static bound: a literal, or a $param with max. |
radius-expr |
— | no | A MapLibre number expression (px), evaluated per feature group; overrides the constant radius. Evaluated values are clamped to radius’s static bound so the pad stays sound. |
weight-expr |
— | no | A MapLibre number expression giving a point’s weight, evaluated per feature group. A group without it (or whose expression doesn’t resolve to a number) weighs 1. |
dilate
Section titled “dilate”Per-channel morphological max over a square kernel, on a raster, sprite or scalar field; the output is the same kind as the input. Grows bright / opaque regions; pair with erode to clean up speckle noise (open / close). Being a max, it also flattens detail finer than the kernel — a few px of radius-px is enough to erase grain, so add grain after morphology, never before. On an elevation field (dem) it raises each sample to the highest in reach, widening peaks and ridges and narrowing valleys; dilate then erode (a closing) fills pits and gullies narrower than the kernel while leaving broad slopes alone. A field keeps its nodata value and geographic scale; nodata samples are left out, and a pixel with nothing but nodata in reach stays nodata. Separable box implementation; grows upstream pad by radius-px.

| Field | Type | Required | Description |
|---|---|---|---|
input |
@node |
yes | Reference to another node (@name). |
radius-px |
integer (0…256) | $param | @node |
yes | — |
radius-px-max |
number (0…∞) | no | Upper bound on radius-px for padding, required when radius-px is an @node port. Values above it are clamped. |
displace
Section titled “displace”Photoshop-style displacement map. Output pixel reads input at a position offset by the displacement raster’s R/G channels (0.5 means no offset). input may be a raster, sprite or scalar field, and the output is the same kind. Displacing an elevation field (dem) bends it before contour or hillshade, the same way the map bends the imagery drawn over it, and the contour lines stay level lines of one surface, so they never cross. A field keeps its nodata value and geographic scale; nodata samples are left out of the interpolation, a read with only nodata around it stays nodata, and the field always clamps at its edge. Grows upstream pad by amp-px so warped samples stay seamless across tile borders, provided the displacement source is itself seamless (e.g. noise with anchor: world).


| Field | Type | Required | Description |
|---|---|---|---|
amp-px |
number (0…∞) | $param | @node |
yes | — |
amp-x-px |
number (0…∞) | $param | @node |
no | — |
amp-x-px-max |
number (0…∞) | no | Upper bound on amp-x-px for padding, required when amp-x-px is an @node port. Values above it are clamped. |
amp-y-px |
number (0…∞) | $param | @node |
no | — |
amp-y-px-max |
number (0…∞) | no | Upper bound on amp-y-px for padding, required when amp-y-px is an @node port. Values above it are clamped. |
boundary |
clamp | transparent | mirror |
no | — |
displacement |
@node |
yes | Reference to another node (@name). |
input |
@node |
yes | Reference to another node (@name). |
dither
Section titled “dither”Reduce to a fixed palette with dithering so gradients survive a small palette. method: "floyd-steinberg" (default) diffuses error to neighbours; method: "ordered" uses a 4×4 Bayer matrix with strength amount (0..1). Nearest-colour distance uses space (lab default / rgb). Alpha preserved.


| Field | Type | Required | Description |
|---|---|---|---|
amount |
number (0…1) | no | Ordered-dither strength (ignored for floyd-steinberg). |
input |
@node |
yes | Reference to another node (@name). |
method |
floyd-steinberg | ordered |
no | — |
palette |
array of string | yes | Each entry is #rrggbb / #rrggbbaa (alpha ignored for matching) or a $param, so a palette can be recoloured at render time. |
space |
lab | rgb |
no | — |
edge-detect
Section titled “edge-detect”Sobel gradient magnitude per channel, scaled by strength and clamped to [0, 255]. Pass-through over Raster / Sprite. Use after a hillshade for stylised outlines, or on a flat fill to derive an outline mask.

| Field | Type | Required | Description |
|---|---|---|---|
input |
@node |
yes | Reference to another node (@name). |
strength |
number (0…∞) | $param | @node |
no | Scale factor applied to the gradient magnitude before clamping. |
Per-channel morphological min over a square kernel, on a raster, sprite or scalar field; the output is the same kind as the input. Shrinks bright / opaque regions; classic mask cleanup after color-to-alpha. Removes anything thinner than the kernel outright, so erode then dilate (an opening) keeps only features wider than radius-px — a way to select by size when the source carries no size attribute. Detail finer than the radius does not survive either op: add grain after morphology, never before. On an elevation field (dem) it lowers each sample to the lowest in reach, narrowing peaks and ridges and widening valleys; an opening shaves off spikes and ridges narrower than the kernel. A field keeps its nodata value and geographic scale; nodata samples are left out, and a pixel with nothing but nodata in reach stays nodata. Separable box implementation; grows upstream pad by radius-px.

| Field | Type | Required | Description |
|---|---|---|---|
input |
@node |
yes | Reference to another node (@name). |
radius-px |
integer (0…256) | $param | @node |
yes | — |
radius-px-max |
number (0…∞) | no | Upper bound on radius-px for padding, required when radius-px is an @node port. Values above it are clamped. |
field-math
Section titled “field-math”Per-pixel arithmetic over scalar fields, with the same functions as math: unary fns use a; binary a b; clamp(a, b=lo, c=hi), lerp(a, b, c=t). Each operand is a field (@node outputting a ScalarField) or a number (literal, $param, or @node scalar port) applied to every pixel; at least one must be a field, and all fields must be the same size. Add a little noise to a DEM to roughen it, mix two fields by a third with lerp, or scale a field by a $param. A pixel is missing in the output where any field operand is missing (NaN or its nodata value), or where the result is not a finite number (sqrt of a negative, div by zero). The output keeps the nodata value and geographic scale of the first field operand, and writes missing pixels as that nodata, or NaN.
| Field | Type | Required | Description |
|---|---|---|---|
a |
number | $param | @node |
yes | $param reference, or @node that outputs a ScalarField or a Scalar. |
b |
number | $param | @node |
no | $param reference, or @node that outputs a ScalarField or a Scalar. |
c |
number | $param | @node |
no | $param reference, or @node that outputs a ScalarField or a Scalar. |
fn |
abs | neg | floor | ceil | round | sqrt | add | sub | mul | div | mod | min | max | pow | clamp | lerp |
yes | — |
flow-field
Section titled “flow-field”Direction raster from an elevation field: which way the ground falls, rises, or runs level at each pixel. R = 0.5 + 0.5·vx and G = 0.5 + 0.5·vy in pixel axes (+x east, +y south), B = 0, opaque; flat 50% grey means no direction. This is displace’s encoding, so the output feeds displace directly, and it is what flow-smear reads to streak a texture along the terrain. The vector’s length is the slope angle over max-deg (capped at 1), or 1 everywhere the ground slopes with normalize. Missing samples (NaN or the field’s nodata value) get the opaque no-flow grey, so displace and flow-smear leave them in place; a missing neighbour counts as the centre value, so the ground around a hole does not flow into or out of it.
| Field | Type | Required | Description |
|---|---|---|---|
direction |
downhill | uphill | along |
no | downhill follows the fall line, uphill points the other way, and along follows the contour lines with downhill on its right-hand side (counter-clockwise around a summit on a north-up map). |
exaggeration |
number | $param | @node |
no | Multiplier on the gradient before the slope angle is taken, as in hillshade. |
field |
@node |
yes | Reference to another node (@name). |
max-deg |
number | $param | @node |
no | Slope angle (degrees) at which the vector reaches full length. Ignored with normalize. |
normalize |
boolean | $param | @node |
no | If true, every sloping pixel gets a vector of length 1; only perfectly flat ground stays at zero. |
flow-smear
Section titled “flow-smear”Smears input along the streamlines of a direction field (line integral convolution): each pixel becomes the average of input sampled along the curve through it, re-reading the field at every step so strokes bend with it. The field is flow-field’s encoding — R/G = 0.5 + 0.5·v in pixel axes, flat grey meaning no direction — and a pixel’s walk is length-px times the field’s length there, so flat ground stays sharp. For hachures or contour strokes, smear a world-anchored noise along dem → flow-field, then threshold or color-ramp the result. Seamless across tile borders when input is world-anchored; grows the upstream pad by length-px + 1.
| Field | Type | Required | Description |
|---|---|---|---|
field |
@node |
yes | Reference to another node (@name). |
input |
@node |
yes | Reference to another node (@name). |
length-px |
number (0…∞) | $param | @node |
no | Walk length in px each way, at full field length. |
length-px-max |
number (0…∞) | no | Upper bound on length-px for padding, required when length-px is an @node port. Values above it are clamped. |
sides |
both | forward |
no | both smears each way along the field, so a speck becomes a stroke centred on it; forward smears only the way the field points, so a speck trails a comet tail downstream. |
taper |
flat | smooth |
no | flat weights every sample alike; smooth fades them towards the ends of the walk (a Hann window), softening the stroke ends. |
gradient-conic
Section titled “gradient-conic”Conic (sweep) gradient around center. Gradient parameter is the clockwise angle from start-angle (degrees) normalized to [0, 1). 0° points along +x. kind: sprite switches to sprite-local [0, 1] coords.

| Field | Type | Required | Description |
|---|---|---|---|
anchor |
tile | world |
no | — |
center |
array of number | no | — |
height-px |
integer (1…∞) | no | — |
kind |
raster | sprite |
no | — |
space |
rgb | hsl | hsv | hcl | lab |
no | Colour space the stops interpolate in; hue-based spaces take the shortest path. |
start-angle |
number | $param | @node |
no | — |
stops |
array of array of — | yes | [[t, color], ...]. Either half of a pair may be a $param; the table is sorted by t on every eval, so stops need not be declared in order. |
width-px |
integer (1…∞) | no | — |
gradient-diamond
Section titled “gradient-diamond”Diamond gradient from center. Gradient parameter is Manhattan (|dx| + |dy|) distance / radius. kind: sprite switches to sprite-local [0, 1] coords.

| Field | Type | Required | Description |
|---|---|---|---|
anchor |
tile | world |
no | — |
center |
array of number | no | — |
height-px |
integer (1…∞) | no | — |
kind |
raster | sprite |
no | — |
radius |
number (0…∞) | $param | @node |
no | — |
space |
rgb | hsl | hsv | hcl | lab |
no | Colour space the stops interpolate in; hue-based spaces take the shortest path. |
stops |
array of array of — | yes | [[t, color], ...]. Either half of a pair may be a $param; the table is sorted by t on every eval, so stops need not be declared in order. |
width-px |
integer (1…∞) | no | — |
gradient-linear
Section titled “gradient-linear”Linear gradient from start to end (fractional coords). kind: raster (default) samples in tile/world coords; kind: sprite samples in [0, 1] sprite coords at width-px × height-px.

| Field | Type | Required | Description |
|---|---|---|---|
anchor |
tile | world |
no | — |
end |
array of number | no | — |
height-px |
integer (1…∞) | no | — |
kind |
raster | sprite |
no | — |
space |
rgb | hsl | hsv | hcl | lab |
no | Colour space the stops interpolate in; hue-based spaces take the shortest path. |
start |
array of number | no | — |
stops |
array of array of — | yes | [[t, color], ...]. Either half of a pair may be a $param; the table is sorted by t on every eval, so stops need not be declared in order. |
width-px |
integer (1…∞) | no | — |
gradient-radial
Section titled “gradient-radial”Radial gradient from center outward. Gradient parameter is Euclidean distance / radius. aspect > 1 stretches the ellipse along X, < 1 along Y. kind: sprite switches to sprite-local [0, 1] coords.

| Field | Type | Required | Description |
|---|---|---|---|
anchor |
tile | world |
no | — |
aspect |
number (0…∞) | $param | @node |
no | — |
center |
array of number | no | — |
height-px |
integer (1…∞) | no | — |
kind |
raster | sprite |
no | — |
radius |
number (0…∞) | $param | @node |
no | — |
space |
rgb | hsl | hsv | hcl | lab |
no | Colour space the stops interpolate in; hue-based spaces take the shortest path. |
stops |
array of array of — | yes | [[t, color], ...]. Either half of a pair may be a $param; the table is sorted by t on every eval, so stops need not be declared in order. |
width-px |
integer (1…∞) | no | — |
hillshade
Section titled “hillshade”Analytical hillshade (Horn 1981) from a ScalarField. mode: shade outputs grayscale; mode: relief outputs transparent black scaled by 1-shade for multiply-blend over a base map. Missing samples (NaN or the field’s nodata value) are fully transparent in both modes; a missing neighbour counts as the centre value, so the edge of a hole shades as level ground instead of a cliff.
| Field | Type | Required | Description |
|---|---|---|---|
altitude-deg |
number | $param | @node |
no | Light elevation above the horizon (degrees). |
azimuth-deg |
number | $param | @node |
no | Light direction (0 = north, clockwise). |
exaggeration |
number | $param | @node |
no | Extra vertical exaggeration on top of z-factor. |
field |
@node |
yes | Reference to another node (@name). |
highlight-color |
color | $param | @node |
no | sRGB hex color, $param reference, or @node scalar port. |
mode |
shade | relief |
no | — |
multidirectional |
boolean | $param | @node |
no | ESRI-style 4-direction weighted hillshade — softer, more legible at small zooms. |
shadow-color |
color | $param | @node |
no | sRGB hex color, $param reference, or @node scalar port. |
z-factor |
number | $param | @node |
no | Multiplier on elevation before gradient — use ~1/cos(lat) for high latitudes. |
HSL adjustment: rotate hue by hue-shift degrees, shift saturation/lightness in [-1, 1] (0 = no change, +1 = toward max, -1 = toward 0).


| Field | Type | Required | Description |
|---|---|---|---|
hue-shift |
number | $param | @node |
no | — |
input |
@node |
yes | Reference to another node (@name). |
lightness |
number (-1…1) | $param | @node |
no | — |
saturation |
number (-1…1) | $param | @node |
no | — |
invert
Section titled “invert”Negate RGB channels (1 - c). Alpha is preserved.


| Field | Type | Required | Description |
|---|---|---|---|
input |
@node |
yes | Reference to another node (@name). |
levels
Section titled “levels”Photoshop-style levels: remap input range [in-black, in-white] through gamma onto output range [out-black, out-white]. All values in [0, 1] (non-premultiplied sRGB). gamma > 1 lightens midtones; gamma < 1 darkens them.


| Field | Type | Required | Description |
|---|---|---|---|
gamma |
number (0.01…∞) | $param | @node |
no | — |
in-black |
number (0…1) | $param | @node |
no | — |
in-white |
number (0…1) | $param | @node |
no | — |
input |
@node |
yes | Reference to another node (@name). |
out-black |
number (0…1) | $param | @node |
no | — |
out-white |
number (0…1) | $param | @node |
no | — |
map-range
Section titled “map-range”Linearly remap scalar field values from [in-min, in-max] to [out-min, out-max]. With clamp: true, results outside the output range are pinned to the range bounds. Missing samples (NaN or the field’s nodata value) stay missing: the output keeps the field’s nodata value and geographic scale, and writes missing pixels as that nodata, or NaN. Useful for normalising elevation or distance fields before a color-ramp.
| Field | Type | Required | Description |
|---|---|---|---|
clamp |
boolean | $param | @node |
no | — |
field |
@node |
yes | Reference to another node (@name). |
in-max |
number | $param | @node |
no | — |
in-min |
number | $param | @node |
no | — |
out-max |
number | $param | @node |
no | — |
out-min |
number | $param | @node |
no | — |
Interpolate two rasters per-pixel by scalar t (0..1) in colour space. A straight colour tween (contrast with blend, which composites with alpha). t accepts a literal, $param, or @node scalar for param-driven theming.


| Field | Type | Required | Description |
|---|---|---|---|
a |
@node |
yes | Reference to another node (@name). |
b |
@node |
yes | Reference to another node (@name). |
space |
rgb | hsl | hsv | hcl | lab |
no | Colour space the interpolation runs in; hue-based spaces take the shortest path. |
t |
number (0…1) | $param | @node |
no | — |
mosaic
Section titled “mosaic”Quantize a raster into uniform square blocks. mode selects how each block is summarized: average (default) takes the mean of covered pixels (smooth, classic mosaic); nearest takes the block’s centre pixel (crisp, no inter-colour blending). World-anchored by default so adjacent map tiles share the same block grid.


| Field | Type | Required | Description |
|---|---|---|---|
anchor |
world | tile |
no | world (default) makes the block grid seamless across map tiles by growing the upstream pad. tile restarts the grid at every map tile’s top-left and requires no extra padding. |
block |
integer (1…∞) | $param | @node |
yes | Block edge length in canvas pixels. With anchor: world the block size sets the canvas padding, which is fixed before anything renders — so this needs an upper bound the build can see: a literal, a $param with max, or block-max beside an @node port. |
block-max |
number (0…∞) | no | Upper bound on block for padding, required when block is an @node port. Values above it are clamped. |
input |
@node |
yes | Reference to another node (@name). |
mode |
average | nearest |
no | average (default) blends covered pixels into a mean colour. nearest samples the block’s centre pixel verbatim — produces hard block edges without inter-colour averaging. |
Procedural noise source. With kind: raster (default) the noise is mapped to RGBA via low-color/high-color/opacity. With kind: scalar it emits a ScalarField of raw fBm values — compose with map-range before feeding hillshade/color-ramp. Every type lands in [-1, 1]. white/value/perlin/simplex sit around 0. worley and cell both divide the plane into cells around scattered sites: worley is 1 − 2·distance to the nearest site, +1 on the site and falling towards the cell’s edge, and cell is the nearest site’s own random value, flat across its cell and spread evenly over [-1, 1] — one tone per cell, for stones, panes or patches. anchor=world (default) keeps the field seamless across tile borders.

| Field | Type | Required | Description |
|---|---|---|---|
anchor |
tile | world |
no | — |
gain |
number | $param | @node |
no | — |
high-color |
color | $param | @node |
no | sRGB hex color, $param reference, or @node scalar port. |
kind |
raster | scalar |
no | — |
lacunarity |
number | $param | @node |
no | — |
low-color |
color | $param | @node |
no | sRGB hex color, $param reference, or @node scalar port. |
octaves |
integer (1…12) | no | — |
opacity |
number (0…1) | $param | @node |
no | — |
scale-px |
number | array of number | yes | Noise wavelength in pixels. A single number for isotropic noise; an [x, y] array for anisotropic noise (larger value = longer wavelength along that axis, i.e. stretched pattern). |
seed |
integer (0…∞) | no | Field seed. Omitted, anchor: world uses a fixed seed so every tile samples the same field, and anchor: tile uses the host’s per-tile seed so each tile gets its own. Set it to pin either. |
type |
white | value | perlin | simplex | worley | cell |
no | — |
warp-amp |
number | $param | @node |
no | — |
warp-freq |
number | $param | @node |
no | — |
Draw the source raster onto the canvas exactly once. fit:none (default) uses position/anchor/scale; cover/contain/stretch derive the destination rect from the canvas (and treat position-px as an offset from canvas center). Companion to tiling (repeats) and stamp (per-point).
| Field | Type | Required | Description |
|---|---|---|---|
anchor |
top-left | top-center | top-right | center-left | center | center-right | bottom-left | bottom-center | bottom-right |
no | Only used when fit=none. |
fit |
none | cover | contain | stretch |
no | — |
input |
@node |
yes | Reference to another node (@name). |
opacity |
number (0…1) | $param | @node |
no | — |
position-px |
array of number | no | fit=none: canvas pixel where the image’s anchor point lands. Other fits: offset from canvas center. |
rotation-deg |
number | $param | @node |
no | Rotation (degrees clockwise) around the placement anchor / canvas center. |
scale |
number (0…∞) | $param | @node |
no | Uniform scale for fit=none. Ignored for other fits. |
posterize
Section titled “posterize”Quantise each RGB channel into steps evenly-spaced levels (in non-premultiplied sRGB). Alpha preserved. Pair with levels to control which tonal band gets the strongest banding.


| Field | Type | Required | Description |
|---|---|---|---|
input |
@node |
yes | Reference to another node (@name). |
steps |
integer (2…256) | no | — |
quantize
Section titled “quantize”Snap each pixel to the nearest colour in palette, measuring distance in space (lab = perceptual ΔE, default; or rgb). Alpha is preserved. Use for limited-palette / poster / pixel-art looks; see dither for the error-diffused variant.


| Field | Type | Required | Description |
|---|---|---|---|
input |
@node |
yes | Reference to another node (@name). |
palette |
array of string | yes | Each entry is #rrggbb / #rrggbbaa (alpha ignored for matching) or a $param, so a palette can be recoloured at render time. |
space |
lab | rgb |
no | Colour space the nearest-colour distance is measured in. |
saturate
Section titled “saturate”Scale CIELAB chroma uniformly by amount (1 = identity, 0 = greyscale, >1 = more saturated). Hue and lightness are preserved.


| Field | Type | Required | Description |
|---|---|---|---|
amount |
number (0…∞) | $param | @node |
no | — |
input |
@node |
yes | Reference to another node (@name). |
sharpen
Section titled “sharpen”4-neighbour Laplacian sharpen on a raster, sprite or scalar field; the output is the same kind as the input. Amplifies each pixel relative to its orthogonal neighbours by amount. Around 0.5–1.0 is a typical unsharp-mask look; negative values give a soft halo. Sharpening an elevation field (dem) before hillshade or slope steepens the flanks of ridges and valleys, so the shading draws crisper crests; a field is not clamped, so values can overshoot past the field’s range next to a sharp step. A field keeps its nodata value and geographic scale; a nodata neighbour counts as the centre value, so a hole does not distort its rim, and a nodata sample stays nodata. Grows upstream pad by 1.


| Field | Type | Required | Description |
|---|---|---|---|
amount |
number | $param | @node |
no | Sharpening strength. 0 = pass-through, ~1 = strong. |
input |
@node |
yes | Reference to another node (@name). |
Slope angle as grayscale, normalised to 0..1 against max-deg. Missing samples (NaN or the field’s nodata value) are fully transparent; a missing neighbour counts as the centre value, so the edge of a hole reads as level ground instead of a sheer drop.
| Field | Type | Required | Description |
|---|---|---|---|
field |
@node |
yes | Reference to another node (@name). |
invert |
boolean | $param | @node |
no | If true, flat = white and steep = black. |
max-deg |
number | $param | @node |
no | Slope angle (degrees) that maps to white. |
Solid-colour source. kind: raster (default) fills the full canvas; kind: sprite emits a Sprite at width-px × height-px for use as a synthetic placement/tiling source.

| Field | Type | Required | Description |
|---|---|---|---|
color |
color | $param | @node |
yes | sRGB hex color, $param reference, or @node scalar port. |
height-px |
integer (1…∞) | no | — |
kind |
raster | sprite |
no | — |
width-px |
integer (1…∞) | no | — |
Composite an ordered list of raster layers bottom-to-top with plain source-over (equivalent to a chain of blend nodes with mode: normal, composite: over, opacity: 1), folded into one pass over a single accumulator. Fully-transparent layers are skipped. For per-layer opacity, blend modes, clipping, masks, or the eraser composite, use blend.


| Field | Type | Required | Description |
|---|---|---|---|
layers |
array of @node |
yes | Raster (or sprite) layers, bottom first. The output mirrors the bottom layer’s kind. |
threshold
Section titled “threshold”Binarise a scalar field: emit low for samples ≤ value, high otherwise. With softness > 0 the transition is a linear ramp of width softness centred on value. Missing samples (NaN or the field’s nodata value) stay missing: the output keeps the field’s nodata value and geographic scale, and writes missing pixels as that nodata, or NaN.
| Field | Type | Required | Description |
|---|---|---|---|
field |
@node |
yes | Reference to another node (@name). |
high |
number | $param | @node |
no | — |
low |
number | $param | @node |
no | — |
softness |
number (0…∞) | $param | @node |
no | — |
value |
number | $param | @node |
no | — |
tiling
Section titled “tiling”Repeat a raster across the canvas with bilinear wrap-around sampling. Primary use case is paper / canvas texture composition. Does not stretch-to-fit — use a dedicated cover/contain node if that’s what you want.
| Field | Type | Required | Description |
|---|---|---|---|
anchor |
world | tile |
no | world (default) makes the pattern seamless across map tiles; tile restarts the pattern at every map tile’s top-left. |
input |
@node |
yes | Reference to another node (@name). |
offset-px |
array of number | no | Pattern offset in canvas pixels [x, y]. |
opacity |
number (0…1) | $param | @node |
no | — |
rotation-deg |
number | $param | @node |
no | Rotation of the pattern in degrees (clockwise). |
scale-px |
number (0.5…∞) | no | Canvas pixels per one source repetition. Defaults to the source image’s native width. |
vibrance
Section titled “vibrance”Adaptively boost CIELAB chroma — low-chroma pixels rise more than already-vivid ones (amount = strength, 0 = identity). Hue and lightness preserved.


| Field | Type | Required | Description |
|---|---|---|---|
amount |
number (0…∞) | $param | @node |
no | — |
input |
@node |
yes | Reference to another node (@name). |
Domain warp via an internal noise field, on a raster, sprite or scalar field; the output is the same kind as the input. With anchor: world (default) the warp is seamless across tile borders; the upstream pad grows by amp-px to keep samples inside the available raster. Warping an elevation field (dem) before contour makes the contour lines wobble together without ever crossing, since they stay level lines of one surface; warping a noise field with kind: scalar gives a swirled field to threshold or ramp. A field keeps its nodata value and geographic scale; nodata samples are left out of the interpolation, and a read with only nodata around it stays nodata. A field always clamps at its edge; boundary applies to images.


| Field | Type | Required | Description |
|---|---|---|---|
amp-px |
number (0…∞) | $param | @node |
yes | — |
amp-x-px |
number (0…∞) | $param | @node |
no | — |
amp-x-px-max |
number (0…∞) | no | Upper bound on amp-x-px for padding, required when amp-x-px is an @node port. Values above it are clamped. |
amp-y-px |
number (0…∞) | $param | @node |
no | — |
amp-y-px-max |
number (0…∞) | no | Upper bound on amp-y-px for padding, required when amp-y-px is an @node port. Values above it are clamped. |
anchor |
tile | world |
no | — |
boundary |
clamp | transparent | mirror |
no | — |
gain |
number | $param | @node |
no | — |
input |
@node |
yes | Reference to another node (@name). |
lacunarity |
number | $param | @node |
no | — |
octaves |
integer (1…12) | no | — |
scale-px |
number (0…∞) | $param | @node |
yes | — |
seed |
integer (0…∞) | no | Field seed. Omitted, anchor: world uses a fixed seed so every tile samples the same field, and anchor: tile uses the host’s per-tile seed so each tile gets its own. Set it to pin either. |
type |
white | value | perlin | simplex | worley | cell |
no | — |
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.