Nodes whose primary output is a raster image — sources that generate pixels, and modifiers that transform them.
Generators
3D Cube (Test)
cube-3d-test
M0 render-bridge spike: a lit, spinning cube rendered by three.js on its own WebGL context and composited into the 2D graph. Proof-of-concept for the 3D context.
reads
time
flags
unstable
gotcha
Renders on its own isolated three.js WebGL context (a proof-of-concept render bridge), not the engine's shared 3D scene/camera graph.
gotcha
Cube size, position, and camera are fixed in code; only color, speed, metalness, and roughness are exposed as params.
gotcha
Spin comes from ctx.time * speed applied to both X and Y rotation, so it is deterministic per tick and safe for offline export.
gotcha
If the three.js renderer fails to construct (no WebGL2 or a lost context), the output degrades to a solid magenta debug clear instead of throwing.
Parameters
Name
Type
Default
Opacity
opacity
scalar
0 … 1
1
Color
color
color
"#ff6633"
Spin speed
speed
scalar
-4 … 4
0.6
Metalness
metalness
scalar
0 … 1
0.1
Roughness
roughness
scalar
0 … 1
0.45
Sockets
Direction
Name
Type
outputprimary
out
image
Fluid Simulator
fluid-simulatorpolymorphic
2D Eulerian fluid (smoke / dye / ink in open water). Wire any mask/spline/shape into `deposit` to release dye; an image into `color` for its hues (alpha doubles as coverage when `deposit` is unwired). Takes the SAME force nodes as the Particle Simulator (Vortex/Wind/Gravity/Turbulence… — add slots in the panel) and Circle/Line/Image-Mask colliders as solid obstacles the flow routes around. The optional `field` input steers the fluid with an authored velocity field (Perlin curl / Spline Flow Field); the `velocity` aux emits the LIVE sim field in the same encoding, so Advect Points / Advect Image can ride the simulation. Advection-reflection keeps swirls energetic; `vorticity` adds small-scale swirl back. `buoyancy` makes dense dye rise (smoke). Runs while the timeline plays; restarting the timeline clears the tank.
reads
time
flags
simulation, unstable
gotcha
resolution downsamples the simulation grid (× canvas dims, min 8px per axis); only the final present/aux passes render back at full canvas resolution.
gotcha
Sim advances only while ctx.playing (or offline frame-stepping), or when the wired time input increases if drive_by_scene_time is on; a paused tweak re-presents the last state.
gotcha
Resets the whole tank (velocity, dye, pressure all cleared) on first evaluation or when ctx.time wraps back near 0 (scene loop).
gotcha
field and the velocity aux share the same signed-RG, y-down, canvas-width-relative encoding (VELOCITY_ENCODE_GLSL), so Advect Points/Advect Image can consume either directly.
gotcha
Falls back to a blank transparent output and a neutral velocity aux, with a one-time console.warn, when EXT_color_buffer_float is unavailable.
gotcha
force1..N/collider1..N use the same descriptor kinds as Particle Simulator (Gravity/Drag/Point/Vortex/Wind/Turbulence; Circle/Line/Image-Mask); slot count is fixed by forceCount/colliderCount.
Parameters
Name
Type
Default
Dye rate
inject_rate
scalar
0 … 20
6
Dye color
ink_color
color
"#ffffff"
Dye fade / sec
dissipation
scalar
0 … 1
0.02
Velocity fade / sec
velocity_dissipation
scalar
0 … 2
0.05
Vorticity (swirl)
vorticity
scalar
0 … 30
8
Buoyancy
buoyancy
scalar
-2 … 2
0
Field strength
field_strength
scalar
0 … 4
0.5
Speed
speed
scalar
0.1 … 4
1
Edges
edges
enum
"closed"
closed · open
Pressure iterations
pressure_iterations
scalar
8 … 60
24
Substeps / frame
substeps_per_frame
scalar
1 … 8
2
Resolution
resolution
scalar
0.1 … 1
0.5
Force slots
forceCount
scalar
0 … 6
1
Collider slots
colliderCount
scalar
0 … 4
0
View
view
enum
"composite"
composite · velocity
Drive by time input
drive_by_scene_time
boolean
false
Opacity
opacity
scalar
0 … 1
1
Sockets
Direction
Name
Type
input
deposit
deposit
mask
input
color
color
image
input
Field (guide)
field
image
input
force1
force1
force
outputprimary
out
image
outputaux
velocity
image
Gradient
gradientpolymorphic
Two-color gradient in linear, radial, polar or wave modes.
radial mode's distance-to-center is not aspect-corrected, so it reads as an ellipse on non-square canvases; only the wave ring sub-mode aspect-corrects its radius to stay round.
gotcha
angle_mod (and uv_in) are only exposed via resolveInputs in modes where they apply: linear and the directional (non-ring) wave sub-mode.
gotcha
angle_mod_amount converts the modulator's sampled red channel into a per-pixel radian offset that rotates the gradient axis (linear) or wave direction, added to the base angle.
gotcha
softness reshapes t through two nested pow() curves toward the endpoints; multipoint mode ignores softness entirely and blends colors directly by inverse-distance weighting.
gotcha
phase_animate drives phase from scene time (ctx.tick) via a [phase_start, phase_end]-frame loop window times phase_rate cycles; it loops seamlessly only when phase_rate is a whole number.
gotcha
multipoint supports at most 16 points (MAX_POINTS); any beyond that are silently dropped from the uniform arrays.
gotcha
uv_in, when wired, replaces the per-pixel v_uv sample every mode evaluates against; wiring a scalar broadcasts that single value to both UV components.
Skia-layer Gradient: linear (start/end handles), radial, polar, wave (bands or rings) and multipoint (inverse-distance blend of up to 16 points) as one SkSL function fused into the layer's shader. Same params as Gradient; positions are uv (Y-up 0..1). The angle mod input rotates the linear axis / wave direction by its red channel × angle mod (°).
Skia-layer Image Source: the same file, fit (cover / contain / stretch) and translate / scale / rotate / pivot, drawn as a React Native Skia <Image> and shipped in the export as a bundled asset. Wire the primary drawing into Skia Group / Skia Canvas; the `shader` output feeds the image into Skia shader nodes (fused as an ImageShader child, sampled exactly like Image Source); `preview` is the drawing as an image (an SkSL Shader's iImage input exports it as a bundled image too).
TRS is Image Source's, about the pivot after the fit: rigid in pixels, translateY / pivotY width-relative (Y-down).
gotcha
legacySkew (set on ≤v12 saves) keeps the old per-axis TRS: it shears when W ≠ H and reads translateY / pivotY height-relative.
gotcha
The drawing's edges are anti-aliased by Skia where Image Source cuts hard (a rotated image differs in its 1-px edge band); the `shader` output keeps Image Source's hard edges.
gotcha
opacity fades the drawing (React Native Skia group opacity) and the shader's alpha; the preview image is faded by the evaluator like any image output.
gotcha
No EXR stills and no uv_in field: React Native Skia has no float image format or per-pixel warp for an <Image>.
gotcha
The export ships the bitmap's original file (PNG / JPEG / WebP / GIF) under assets/; a synthetic bitmap ships as its PNG re-encode.
Parameters
Name
Type
Default
Opacity
opacity
scalar
0 … 1
1
Image
file
file
—
Fit
fit
enum
"cover"
cover · contain · stretch
Translate X
translateX
scalar
-1 … 1
0
Translate Y
translateY
scalar
-1 … 1
0
Scale X
scaleX
scalar
0.01 … 10
1
Scale Y
scaleY
scalar
0.01 … 10
1
Rotate (°)
rotate
scalar
-360 … 360
0
Pivot X
pivotX
scalar
0 … 1
0.5
Pivot Y
pivotY
scalar
0 … 1
0.5
Legacy skew
legacySkew
boolean
false
Sockets
Direction
Name
Type
outputprimary
out
skia_draw
outputaux
shader
skia_shader
outputaux
preview
image
Image Generate
image-generate
Generate images via OpenAI's gpt-image-2 with a chat interface in the param panel. Bring-your-own OpenAI key (Toolbox → User Preferences). Three optional reference image inputs feed the model as context. Outputs the currently-selected generation, or transparent when none is selected.
gotcha
compute() never reads the ref_a/ref_b/ref_c sockets; they only feed the generation request from the panel's chat interface, not the render graph.
gotcha
Output is driven entirely by selectedImagePath (a public bucket URL); empty means transparent output and immediately disposes any cached texture.
gotcha
On a URL change it first tries a synchronous panel-stashed ImageBitmap.
gotcha
On a cache miss it falls back to an async image fetch that emits transparent for that frame and fires pipeline-bump on completion.
gotcha
Requires the active user's own OpenAI API key (Toolbox -> User Preferences); generations are billed to and stored under that user's account.
Rotation is rigid in pixels on any canvas; translateY / pivotY are width-relative like shapes (on 16:9 the frame's top edge is pivotY ≈ 0.219).
gotcha
legacySkew (set on ≤v12 saves) keeps the old per-axis TRS: it shears when W ≠ H and reads translateY / pivotY height-relative.
gotcha
translateX/Y, scaleX/Y, rotate and pivotX/Y apply as inverse TRS in output UV before the aspect fit, so cover's crop stays a movable window over the full-res source.
gotcha
fit=contain letterboxes with transparent alpha outside the image bounds; pixels outside the transformed unit square are also transparent.
gotcha
No file loaded emits fully transparent output, not black.
gotcha
exr_unpremultiply defaults on because EXR stores associated (premultiplied) alpha while the engine composites straight alpha.
gotcha
EXR layer/alpha decode is async; the previous texture keeps showing until the new decode lands, with decode progress folded into the node's fingerprint so the cache doesn't stick on stale data.
gotcha
uv_in, when wired, replaces v_uv before TRS/fit is applied, so a UV warp happens in output space ahead of the aspect fit; a scalar input broadcasts to both UV axes.
Parameters
Name
Type
Default
Opacity
opacity
scalar
0 … 1
1
Image
file
file
—
Layer
exr_layer
enum
""
Un-premultiply alpha
exr_unpremultiply
boolean
true
Fit
fit
enum
"cover"
cover · contain · stretch
Translate X
translateX
scalar
-1 … 1
0
Translate Y
translateY
scalar
-1 … 1
0
Scale X
scaleX
scalar
0.01 … 10
1
Scale Y
scaleY
scalar
0.01 … 10
1
Rotate (°)
rotate
scalar
-360 … 360
0
Pivot X
pivotX
scalar
0 … 1
0.5
Pivot Y
pivotY
scalar
0 … 1
0.5
Legacy skew
legacySkew
boolean
false
Sockets
Direction
Name
Type
input
UV
uv_in
uv
outputprimary
out
image
outputaux
element
element
Lyapunov Fractal
lyapunov
Iterate the logistic map with a two-letter A/B sequence of r values and color each pixel by its Lyapunov exponent.
gotcha
uv.x maps to A in [a_min, a_max] and uv.y (Y-up) maps to B in [b_min, b_max]; the parameter rectangle fills the frame with no aspect correction.
gotcha
Sequence letters are A and B only (case-insensitive); other characters are dropped, an empty string falls back to AB, and more than 32 letters are truncated.
gotcha
warmup iterates without accumulating; iterations is the count in λ = mean(ln|r(1-2x)|); warmup+iterations is capped at 1024.
gotcha
color_mode=signed paints λ≈0 with color_stable, negative toward color_div, positive toward color_chaos; color_mode=exponent emits λ remapped through [lambda_lo, lambda_hi] as grayscale.
gotcha
x0=0.5 is the logistic-map critical point; a sequence that drives x to 0 or 1 (the r=4 edge with x0=0.5) is colored with color_div.
gotcha
uv_in replaces v_uv when wired (image R/G, or a scalar broadcast to both axes), so the sample is taken at that parameter-space coordinate.
Parameters
Name
Type
Default
Opacity
opacity
scalar
0 … 1
1
Sequence
preset
enum
"AABAB"
AABAB · AB · BBBBBAAAAA · BBABABA · Custom
Custom
sequence
string
"AABAB"
A min
a_min
scalar
0 … 4
2
A max
a_max
scalar
0 … 4
4
B min
b_min
scalar
0 … 4
2
B max
b_max
scalar
0 … 4
4
Warmup
warmup
scalar
0 … 800
200
Iterations
iterations
scalar
1 … 800
200
x₀
x0
scalar
0.001 … 0.999
0.5
Color
color_mode
enum
"signed"
Signed · Exponent
Stable
color_stable
color
"#f2d14b"
Chaos
color_chaos
color
"#1e3a8a"
Diverged
color_div
color
"#0a0612"
λ− scale
lambda_neg
scalar
0.01 … 4
0.6
λ+ scale
lambda_pos
scalar
0.01 … 4
0.5
λ lo
lambda_lo
scalar
-4 … 2
-1
λ hi
lambda_hi
scalar
-4 … 2
0.5
Sockets
Direction
Name
Type
input
UV
uv_in
uv
outputprimary
out
image
Lyapunov Fractal (Skia)
skia-lyapunov
Skia-layer Lyapunov Fractal: iterates the logistic map with the A/B sequence per pixel and colors by the Lyapunov exponent, as an SkSL function that fuses into the layer's one runtime shader. Same params as Lyapunov Fractal; `UV` re-samples at a wired shader's R/G.
flags
no-mask, gates-outputs
gotcha
uv.x maps to A in [a_min, a_max] and uv.y (Y-up) to B in [b_min, b_max] with no aspect correction, as in Lyapunov Fractal.
gotcha
The sequence is a 32-float uniform array walked by an inner loop index (Skia has no uint or bit shifts); preset/sequence and warmup/iterations bind as derived values.
gotcha
warmup + iterations is capped at 1024 steps exactly as Lyapunov Fractal rescales it; every step costs a log, so this is the heaviest shader in the family.
gotcha
color_mode=exponent paints λ through [lambda_lo, lambda_hi] as grey; diverged orbits are black there and color_div in signed mode.
gotcha
uv_in replaces the canvas uv with the wired shader's R/G at the same pixel; opacity is folded into the shader's alpha.
gotcha
Chaotic regions (λ > 0) amplify fp32 rounding: their speckle differs pixel to pixel from Lyapunov Fractal (and between GPUs); periodic regions match it.
Parameters
Name
Type
Default
Opacity
opacity
scalar
0 … 1
1
Sequence
preset
enum
"AABAB"
AABAB · AB · BBBBBAAAAA · BBABABA · Custom
Custom
sequence
string
"AABAB"
A min
a_min
scalar
0 … 4
2
A max
a_max
scalar
0 … 4
4
B min
b_min
scalar
0 … 4
2
B max
b_max
scalar
0 … 4
4
Warmup
warmup
scalar
0 … 800
200
Iterations
iterations
scalar
1 … 800
200
x₀
x0
scalar
0.001 … 0.999
0.5
Color
color_mode
enum
"signed"
Signed · Exponent
Stable
color_stable
color
"#f2d14b"
Chaos
color_chaos
color
"#1e3a8a"
Diverged
color_div
color
"#0a0612"
λ− scale
lambda_neg
scalar
0.01 … 4
0.6
λ+ scale
lambda_pos
scalar
0.01 … 4
0.5
λ lo
lambda_lo
scalar
-4 … 2
-1
λ hi
lambda_hi
scalar
-4 … 2
0.5
Sockets
Direction
Name
Type
input
UV
uv_in
skia_shader
outputprimary
out
skia_shader
outputaux
preview
image
Noise
noise
Multi-algorithm fBm noise. Three outputs: `image` (rasterized canvas-wide), `value` (the image's noise as one scalar, sampled at the `position` input), and `field` (per-pixel scalar shader expression sampled at `field_position` — wire into SDF Rotate.angle_field / SDF Twist.strength_field for true per-pixel/per-tile modulation; pair with SDF Repeat.cell_id for per-tile variation). The `field` output uses simplex regardless of the `type` param; image and value paths respect type. The `UV` input re-evaluates the noise at coordinates read from the wired value's R/G — wire another Noise (or any image) in for Blender-style domain warping/marbling.
space
in:position=uv01
reads
time
flags
gates-outputs
gotcha
field always samples simplex (SDF compiler only ports it); image and value respect type; field3d keeps perlin/value/simplex (perlin-deriv as perlin) and maps the rest to simplex.
gotcha
animated=true drives evolution from scene time on a closed loop between anim_start/anim_end frames, overriding the manual w slider.
gotcha
For type=flow, animated instead sweeps flow_time through one full 2*pi rotation over that loop; animated forces a recompute every tick.
gotcha
value is the noise at `position` before contrast/colors, ignoring UV (curl: the vector x); off the CPU fast path (perlin/simplex/value, w=0, small coords) it costs a sync 1x1 GPU readback.
gotcha
The y sample is compressed by canvas aspect (width/height) before evaluating so noise cells stay square, so `scale` reads as cells-across-the-width regardless of aspect ratio.
gotcha
field3d is true 3D world-space noise (a different space from image/value/field): one world unit spans `scale` noise units, with w as evolution.
gotcha
Wiring an image into `UV` re-reads the sample position from its R/G channels instead of the canvas UV, for domain warping; a wired scalar broadcasts that one value to both axes.
gotcha
type=phasor: phasor_frequency is cycles per lattice cell, phasor_orientation is degrees (0=+X), phasor_isotropy 0=aligned to that heading and 1=±180° random.
Skia-layer Noise: every Noise algorithm (perlin, simplex, value, the OpenSimplex family, super simplex, perlin-deriv, flow, curl, phasor) as an SkSL function that fuses into the layer's one runtime shader. Same params as Noise, including the looping evolution, flow time and the phasor controls; `UV` re-samples at a wired shader's R/G. The `preview` output renders it through CanvasKit; the shader output feeds other Skia nodes.
flags
no-mask, gates-outputs
gotcha
Each type × animated × UV-wired combination fuses its own SkSL function, so the shader holds only the chosen algorithm; a keyframed type refuses to export.
gotcha
animated=true reads the shader clock (frame = iTime × fps) and loops on the device clock with no per-frame uniforms; it forces w to 0, and type=flow sweeps flow_time one turn instead.
gotcha
uv_in replaces the sample uv with the wired shader's R/G at the same pixel, in fp32 (Noise reads a half-float texture there).
gotcha
offset_x/offset_y are in noise-lattice units added after scale and the y aspect compression, exactly as Noise; seed adds (127.1, 311.7) per unit.
gotcha
w is a smoothstep blend between hashed integer slices, so fractional w costs two fBm evaluations; integer w costs one.
gotcha
type=curl writes the curl vector as RG = v×0.5+0.5 with B = 0 and ignores color_a/color_b; phasor_orientation is degrees (radians in the shader).
gotcha
opacity is folded into the shader's alpha; the preview output is faded by the evaluator instead, so it is not applied twice.
gotcha
Noise's value / field / field3d outputs, position inputs and field_lo/field_hi are not ported; a type outside the enum emits transparent and is reported.
Draw on the main canvas while this node is selected — brush, eraser, blur, fill, and eyedropper tools with editable brushes. Wire an image in to paint over it and erase into it; Flatten bakes it into the layer.
space
param:size=pixels
gotcha
compute() only composites the already-drawn snapshot bitmaps; color, size, brush, and fill_tolerance shape the NEXT stroke in the Paint tool overlay and do nothing to strokes already painted.
gotcha
With `image` wired, output = strokes over the input minus the hidden `knockout` mask; unpainted pixels stay live, and the eraser cuts holes in the input itself.
gotcha
Flatten (tool dock) bakes the current input into the strokes and knocks the live input out entirely, so upstream changes stop showing after it.
gotcha
The eraser only writes the knockout while an image is wired; erasing on an unwired Paint removes strokes only, so connecting an image later shows it everywhere the strokes don't cover.
gotcha
bg_mode=transparent passes straight-alpha strokes through untouched (composites over other layers, mattes cleanly to a mask); bg_mode=color composites them over `background` and is always opaque.
gotcha
Unwired, an empty canvas outputs fully transparent even in bg_mode=color (the background only shows once a stroke exists); wired, the input passes through.
gotcha
size is the brush diameter in canvas pixels at render resolution, so it does not scale automatically with output size.
Parameters
Name
Type
Default
Opacity
opacity
scalar
0 … 1
1
Color
color
color
"#ffffff"
Brush size
size
scalar
1 … 400
12
Fill tolerance
fill_tolerance
scalar
0 … 1
0.05
Background
bg_mode
enum
"transparent"
transparent · color
Bg color
background
color
"#000000"
Sockets
Direction
Name
Type
input
Image
image
image
outputprimary
out
image
Physarum
physarumpolymorphic
GPU slime-mold (Physarum) transport network. Millions of agents sense a decaying trail field ahead-left / ahead / ahead-right, turn toward the strongest, step and deposit; the field diffuses and decays, and self-optimising vein networks emerge. Uses Sage Jenson's 36 Points parameterisation — every classic parameter is `base + scale * sensed^power`, so one algorithm covers 24 wildly different regimes. Pick a `preset`, then ride the four multipliers on top of it: `sense scale` is the most expressive (it slides the whole system between regimes), `turn` and `sensor angle` reshape the branching, and `scale` sets structure size — raise `blur passes` with it, since structure size is really the ratio of agent stride to diffusion width and `scale` alone just makes agents outrun the field. `custom` opens all 15 raw numbers. `agents` is a quality knob, not a look knob (the deposit is density-normalised), and `resolution` is fidelity above ~740px of sim height and a zoom below it. Wire a mask into `inject` to draw structure the agents colonise. Turn on `two points` and wire a mask into `blend` to run two different presets in different parts of the frame with a continuous frontier — a Circle driven by the Cursor node reproduces the original's interactive pen. Plays while the timeline runs; restarting the timeline reseeds. Algorithm: Jones 2010 / mxsage 36 Points; implementation ported from Etienne Jacob's interactive-physarum (CC BY-NC-SA 3.0).
reads
time
flags
simulation, unstable
gotcha
Point B (preset_b and its raw params) only affects the sim where `blend` is wired; unwired, `two_points` has no visible effect and every agent uses Point A.
gotcha
sensor_x offsets the sensing point along the agent's current heading; sensor_y offsets it by a fixed amount in world Y regardless of heading.
gotcha
Sensor/move distances (sd/md base+scale, sensor_x/y) scale with resolution above ~740px sim height to stay frame-relative; below it, low resolution just reads as zoomed in.
gotcha
respawn_rate is a per-step phase increment, not a probability: each agent teleports to a fresh random spot roughly every 1/respawn_rate steps, staggered by its seed phase.
gotcha
Diffuse radius follows the same resolution scaling as the distances (1..8 texels) and floors at 1 texel below the reference sim height, capping how far structure spreads per frame.
gotcha
view=trail shows the raw decayed trail channel; view=count applies the same count-to-intensity curve used by the color view and the count aux output.
Parameters
Name
Type
Default
Preset
preset
enum
"pure multiscale"
pure multiscale · hex hole open · vertebrata · star network · enmeshed singularities · waves upturn · more individuals · sloppy bucky · massive structure · speed modulation · transmission tower · ink on white · vanishing points · scaling nodule emergence · hyp offset · strike · clear spaghetti · point R · point S · point T · point U · point V · point W · point X · custom
Sensor distance base
sd_base
scalar
0 … 1200
0
Sensor distance power
sd_power
scalar
0 … 1400
4
Sensor distance scale
sd_scale
scalar
0 … 800
0.3
Sensor angle base
sa_base
scalar
0 … 240
0.1
Sensor angle power
sa_power
scalar
0 … 2080
51.32
Sensor angle scale
sa_scale
scalar
0 … 800
20
Rotation angle base
ra_base
scalar
0 … 240
0.41
Rotation angle power
ra_power
scalar
0 … 800
4
Rotation angle scale
ra_scale
scalar
0 … 520
0
Move distance base
md_base
scalar
0 … 160
0.1
Move distance power
md_power
scalar
0 … 1320
6
Move distance scale
md_scale
scalar
0 … 800
0.1
Sensor Y offset
sensor_y
scalar
-8 … 320
0
Sensor X offset
sensor_x
scalar
-16 … 640
0
Sense scale
sense_scale
scalar
0 … 2400
22
Two points
two_points
boolean
false
Preset B
preset_b
enum
"vertebrata"
pure multiscale · hex hole open · vertebrata · star network · enmeshed singularities · waves upturn · more individuals · sloppy bucky · massive structure · speed modulation · transmission tower · ink on white · vanishing points · scaling nodule emergence · hyp offset · strike · clear spaghetti · point R · point S · point T · point U · point V · point W · point X · custom
Grid of cells with nested concentric shape copies. Per-cell shape, copy count and base hue are seeded; copy scales animate over time. Pipe Scene Time into `time` to drive motion.
space
in:time=time
reads
time
flags
unstable
gotcha
time falls back to ctx.time when the time input is unwired, so the grid keeps animating during normal playback even without Scene Time wired.
gotcha
If useCircle/useSquare/useTriangle are all off, pickShape falls back to circle rather than an empty cell.
gotcha
copiesMin/copiesMax pick a per-cell copy count via hashed seed, clamped to 1..8; lockBaseShape pins only the outermost ring to scaleMax.
gotcha
cellSize is a 0..1 fraction of the shorter cell axis (aspect-preserving), not the full cell width or height.
gotcha
hueShift advances the ramp per concentric copy: rampPos = fract(baseHue + i*hueShift), so 0.17 on the default 6-stop ramp moves one stop per ring.
Skia-layer Shape Cells: a grid of cells with nested concentric circle / square / triangle copies whose scales oscillate over time, colored from the ramp, as an SkSL function that fuses into the layer's one runtime shader. Same params as Shape Cells; the per-cell randomness uses a float hash, so the layout differs from Shape Cells for the same seed.
reads
time
flags
no-mask, gates-outputs
gotcha
Per-cell shape, copy count, base hue and phase come from a float hash (Skia has no uint), so the layout differs from Shape Cells for the same seed; one shape + one copy + lockBaseShape matches.
gotcha
No time input: the shader reads the clock (iTime, Shape Cells' unwired fallback); speed scales it.
gotcha
Edges antialias over the SDF's one-pixel forward differences instead of fwidth, so the GL node's faint 1-px seams on cell borders are absent.
gotcha
cellSize is a 0..1 fraction of the shorter cell axis (aspect-preserving); copies are clamped to 1..8 and lockBaseShape pins only the outermost ring to scaleMax.
gotcha
interpolation cardinal/monotone/bspline/smooth and space oklch/oklch_long are not ported: the node emits transparent and reports them.
gotcha
The output is opaque (background, alpha 1) times opacity, which is folded into the shader's alpha.
A Skia runtime shader (SkSL) rendered by Skia itself through CanvasKit — the same program React Native Skia runs on device, so the preview is the shipped shader rather than a translation. Write helpers plus `half4 main(float2 fragCoord)`: fragCoord is pixels Y-DOWN, iResolution.xy is the canvas size, iTime is seconds, wired images are iImage1..iImage4 (sample with iImage1.eval(xy)), and the return value is PREMULTIPLIED. Write sRGB math: channel colors, iImage samples and the returned color are gamma-encoded sRGB numbers on every canvas — the node converts the result (and the samples) for the device's color space (Display P3 on wide-gamut iPhones, sRGB on Android and the web), so the shader looks identical everywhere; don't call toLinearSrgb / fromLinearSrgb yourself. Tunables are // comments synced into controls: // ch("k", 0.5, 0, 1) → float k; // color("tint", "#ff8800") → float4 tint (straight alpha, sRGB numbers); // toggle("on", true) → int on (SkSL forbids bool uniforms); // pick("mode", "a", "b") → int mode plus const int mode_a, mode_b; // ramp("ink", "#000000", "#ffffff") → float4 ink(float t) (straight alpha, sRGB) and // curve("fall", 1, 0) → float fall(float x), both 256-entry lookup tables. Skia compiles strict GLSL ES 1.0: no uint or bitwise ops, no while loops, constant loop bounds (break out early), no dFdx/fwidth. ch/toggle/pick/color channels keyframe directly (set_keyframes with the channel name; a wire still wins), and scalar/toggle/color channels are also wireable from keyframed Animated Value / Color nodes — both export as a Reanimated timeline. Exports ship a static Image Source / Image (Skia) wired into iImage1..4 as a bundled image (useImage + ImageShader); animated or other image inputs refuse.
reads
time
gotcha
fragCoord is Y-DOWN pixels (Skia local coords), unlike GLSL Expression's Y-up v_uv; divide by iResolution.xy for 0..1.
gotcha
Return premultiplied color (rgb * a); an unpremultiplied return with alpha < 1 renders too bright, exactly as on device.
gotcha
Colors are sRGB numbers on every canvas (Display P3 iPhones too): your main() becomes tbx_user_main and an appended main converts its result for the device. tbx_ names are reserved.
gotcha
Rendered by CanvasKit in its own WebGL context: the first frames are transparent until the 7 MB WASM loads, then it re-renders.
gotcha
Wired images cross into Skia as 8-bit RGBA through a CPU readback per frame, so HDR input values clip and large inputs cost.
gotcha
ramp() / curve() channels are 256×1 RGBA8 lookup-table children sampled at texel centres: within 0.5/255 of GLSL Expression's float LUT, and the export ships these exact bytes.
gotcha
A static Image Source / Image (Skia) in iImage1..4 is sampled from its file through Skia (the exported ImageShader), not the engine's pixels, once the file has decoded.
gotcha
`half` is 16-bit on iOS Metal but 32-bit in this preview — keep hashes and large coordinates in float.
"// SkSL runtime shader — the same file React Native Skia runs.\n// fragCoord is in pixels, Y-DOWN. iResolution.xy = canvas size,\n// iTime = seconds. Wired images are iImage1..iImage4: iImage1.eval(xy).\n// Write sRGB math and return PREMULTIPLIED sRGB color: colors, iImage\n// samples and the result are sRGB numbers on every canvas, and the node\n// converts for the device (Display P3 on iPhone), so it looks the same\n// everywhere. Tunables are one-line comments that Sync turns into\n// controls: ch → float, color → float4 (straight alpha, sRGB), toggle →\n// int 0/1 (SkSL has no bool uniforms), pick → int + consts.\n// ch(\"amount\", 0.5, 0, 1)\n// color(\"tint\", \"#3d7bff\")\n\nhalf4 main(float2 fragCoord) {\n float2 uv = fragCoord / iResolution.xy;\n float w = 0.5 + 0.5 * sin(uv.x * 12.0 + iTime * 2.0);\n float3 c = mix(tint.rgb, float3(uv, 1.0 - uv.x), amount * w);\n return half4(float4(c, 1.0));\n}"
On error
on_error
enum
"transparent"
transparent · magenta
Sockets
Direction
Name
Type
input
iImage1
image1
image
input
iImage2
image2
image
input
iImage3
image3
image
input
iImage4
image4
image
input
amount
in:ein-dg78jz
scalar
input
tint
in:ein-xnitx0
vec4
outputprimary
out
image
Solid Color
solid-color
Fills the frame with a single color. The color square on the node opens a picker in place; the H/S/L/A row at the bottom nudges it without one.
gotcha
alpha (baked into the fill's u_color.a) and the universal opacity param both scale output alpha independently, so they multiply together.
Parameters
Name
Type
Default
Opacity
opacity
scalar
0 … 1
1
Color
color
color
"#808080"
Alpha
alpha
scalar
0 … 1
1
Sockets
Direction
Name
Type
outputprimary
out
image
Solid Color (Skia)
skia-solid-color
Skia-layer Solid Color: one flat color as an SkSL function that fuses into the layer's runtime shader. Same params as Solid Color (color, alpha, opacity).
flags
no-mask, gates-outputs
gotcha
Output alpha is alpha × opacity, folded into the shader; the color's own alpha digits (#rrggbbaa) are ignored, as in Solid Color.
gotcha
The color is a plain sRGB uniform; the fused program converts its result to the device's color space (Display P3 on iPhone), so it matches on every canvas.
Parameters
Name
Type
Default
Opacity
opacity
scalar
0 … 1
1
Color
color
color
"#808080"
Alpha
alpha
scalar
0 … 1
1
Sockets
Direction
Name
Type
outputprimary
out
skia_shader
outputaux
preview
image
Text
textpolymorphic
Renders text with a built-in transform. Wire a spline into the Path input to lay the text along it (Text on Path). Primary is the rasterized image; aux exposes an SDF (jump-flood) and the glyph outline as a spline.
boxWidth/boxHeight are per-axis uv01 fractions of the working canvas (width/height respectively), not aspect-corrected canvas01; 1×1 is the full canvas.
gotcha
translateX/Y and pivotX/Y are per-axis uv01 offsets on the raster; rotate is rigid in pixels (legacySkew on ≤v12 saves keeps the old rotation, which skews when W ≠ H).
gotcha
font_size, strokeWidth and letter_spacing default to raw pixels; units=% resolves them as a percent of canvas width, so the framing holds across comp resolutions and preview scales.
gotcha
sdf and spline aux build lazily only when consumed; dragging text while only primary/element are wired skips the JFA SDF and marching-squares contour work.
gotcha
A wired path or a maskDriven morph_mask axis forces a full re-rasterize every frame, bypassing the normal per-param signature cache.
gotcha
morph_mask (variable-font axis morph) only modulates the primary raster; element and instances aux always use the unmodulated base style.
gotcha
aux:spline is a simplified polyline (marching squares + ~0.4px RDP tolerance), not smooth beziers, so sharp zoom-ins show facets.
gotcha
Animator anim_<p>_by=word counts words in the cascade, samples fields and pivots scale/rotation/position at the word centre; easing=custom samples the exposable anim_<p>_easing_curve.
Parameters
Name
Type
Default
Opacity
opacity
scalar
0 … 1
1
Text
text
string
"Hello"
Font
font_family
enum
"Inter"
Inter · Roboto · Open Sans · Lato · Montserrat · Poppins · Work Sans · DM Sans · Nunito · Raleway · Oswald · Bebas Neue · Playfair Display · Merriweather · Crimson Text · Cormorant Garamond · Space Mono · JetBrains Mono · Helvetica · Arial · Georgia · Times New Roman · Courier New
Draws text in a Skia layer with a font that ships in the export (Inter, Geist or an uploaded Custom font), laid out like the Text node (centered text box, alignment, vertical alignment, leading, letter spacing, case, wrap in box), as React Native Skia paragraphs (shaped and kerned) or simple <Text> runs. Wire it into Skia Group or Skia Canvas.
Only shipped fonts draw: Inter, Geist (bundled, OFL) or a Custom font upload (its bytes ship). Any other font_family refuses the export; the preview draws Inter.
gotcha
Variable fonts draw their default instance (Inter: opsz 14, wght 400): React Native's native text ignores font axes, so font_weight must be the default and font_variations unset.
gotcha
The Text node draws variable fonts at their default optical size too (no automatic opsz), so Inter matches it at every size, as Geist (static) does.
gotcha
boxWidth/boxHeight are uv01 fractions of the canvas, the box centered and moved by translateX/Y, exactly like the Text node; there is no rotate / scale here (wrap it in a Skia Group).
gotcha
Paragraph shaping puts each line leading × size below the last and anchors the block by vAlign, wrapped too; SkParagraph picks the breaks (greedy, but it keeps runs of spaces).
gotcha
shaping=line draws React Native <Text> runs: no kerning and no letter spacing, so long lines drift a few px from the Text node; paragraph shaping matches its advances.
Parameters
Name
Type
Default
Opacity
opacity
scalar
0 … 1
1
Text
text
string
"Hello"
Font
font_family
enum
"Inter"
Geist · Inter
Custom font
custom_font
font
—
Size
font_size
scalar
4 … 1000
96
Units
units
enum
"px"
px · %
Weight
font_weight
scalar
100 … 900
400
Italic
italic
boolean
false
Color
color
color
"#ffffff"
Align
alignment
enum
"center"
left · center · right
Vertical align
vAlign
enum
"middle"
top · middle · bottom
Case
textCase
enum
"none"
none · upper · lower · title
Leading
leading
scalar
0.5 … 3
1.2
Letter spacing
letter_spacing
scalar
-10 … 40
0
Box width
boxWidth
scalar
0.01 … 2
1
Box height
boxHeight
scalar
0.01 … 2
1
Wrap in box
wrap
boolean
false
Shaping
shaping
enum
"paragraph"
Paragraph (kerned) · Simple runs
Translate X
translateX
scalar
-2 … 2
0
Translate Y
translateY
scalar
-2 … 2
0
Sockets
Direction
Name
Type
outputprimary
out
skia_draw
Texture Coordinate
texture-coordinate
Emits the default per-pixel (u, v) as a UV field. Feed it into UV-aware generators (Image Source, Gradient, Perlin) — with Math in UV mode sitting in between to warp, offset, or animate the coordinates.
gotcha
Only R and G carry u and v; B is always 0 and A is always 1, so anything reading the blue or alpha channel gets a flat constant.
Sockets
Direction
Name
Type
outputprimary
out
uv
Texture Coordinate (Skia)
skia-texture-coordinate
Skia-layer Texture Coordinate: emits each pixel's (u, v) in R and G (B = 0, A = 1) as an SkSL function that fuses into the layer's one runtime shader. Feed it into the UV input of Noise (Skia) or Lyapunov (Skia), with warps in between.
flags
no-mask, gates-outputs
gotcha
Only R and G carry u and v (toolbox v_uv: 0..1, Y-up); B is always 0 and A is always 1.
gotcha
The coordinates are evaluated inside the fused shader in fp32; Texture Coordinate stores them in a half-float texture, so consumers there see uv quantized to ~1/2048.
Sockets
Direction
Name
Type
outputprimary
out
skia_shader
outputaux
preview
image
Video Source
video-sourcepolymorphic
Load a video file and render its current frame. Sync the clock to scene time for deterministic playback (good for exports), or let it play on its own.
TRS inverse-samples before the fit, transparent outside the moved frame; rotation rigid in pixels, translateY / pivotY width-relative like shapes (16:9 frame top = pivotY ≈ 0.219).
gotcha
legacySkew (set on ≤v12 saves) keeps the old per-axis TRS: it shears when W ≠ H and reads translateY / pivotY height-relative.
gotcha
sync_to_scene_time drives currentTime to time*speed+start_offset (looped or clamped to duration); off, the element free-runs and only tracks scene play/pause.
gotcha
source_kind=sequence maps scene time to a frame index via seq_fps and forward-fills gaps between numbered frames; exr_layer/exr_unpremultiply apply only to EXR sequences.
gotcha
The video's own audio track stays muted unless this node's audio aux output is wired into Output's audio socket; volume only matters when audible.
gotcha
fit=contain letterboxes with transparent alpha outside the video bounds, not opaque black.
gotcha
Offline export always pauses and hard-seeks to the exact frame and waits for the decode to settle before capture, regardless of playbackRate soft-sync used live.
gotcha
One <video> element has one currentTime, so this node is not retimeable; a wrapping Time Offset passes scene time through unshifted instead of retiming it.
Parameters
Name
Type
Default
Opacity
opacity
scalar
0 … 1
1
Source
source_kind
enum
"video"
video · sequence
Video
file
video_file
—
Image sequence
sequence
image_sequence
—
Sequence FPS
seq_fps
scalar
1 … 120
24
Layer
exr_layer
enum
""
Un-premultiply alpha
exr_unpremultiply
boolean
true
Fit
fit
enum
"cover"
cover · contain · stretch
Translate X
translateX
scalar
-1 … 1
0
Translate Y
translateY
scalar
-1 … 1
0
Scale X
scaleX
scalar
0.01 … 10
1
Scale Y
scaleY
scalar
0.01 … 10
1
Rotate (°)
rotate
scalar
-360 … 360
0
Pivot X
pivotX
scalar
0 … 1
0.5
Pivot Y
pivotY
scalar
0 … 1
0.5
Legacy skew
legacySkew
boolean
false
Sync to scene time
sync_to_scene_time
boolean
true
Speed
speed
scalar
-4 … 4
1
Start offset (s)
start_offset
scalar
0 … 3600
0
Loop
loop
boolean
true
Volume
volume
scalar
0 … 1
1
Sockets
Direction
Name
Type
input
UV
uv_in
uv
outputprimary
out
image
outputaux
audio
audio
Voronoi
voronoipolymorphic
Worley/Voronoi cells from one of three point sources — an infinite procedural lattice, a CPU-scattered set (image-density placement + Lloyd relaxation, the former Fracture node), or an external Points input. Renders F1 / F2-F1 / F2 / cell-id / mask / position modes with selectable distance metric, and emits the true cell geometry as aux outputs: cell polygons (grouped, with per-cell driver), deduped edges, vertices, centers (area-scaled points), and the Delaunay neighbor graph. Geometry is the euclidean diagram and ignores the lattice warp/UV inputs; the Cell Bow param bulges cell edges outward (bubbles) or inward (pebbles).
reads
time
writes
attr:group, attr:driver
gotcha
offset_x/offset_y (lattice) pan in cell-sized units set by scale, not canvas-normalized distance; the same offset covers less canvas as scale increases.
gotcha
Aux geometry (cells/edges/vertices/centers/neighbors) is always the euclidean diagram; under metric=manhattan/chebyshev/minkowski it no longer overlays the rendered image.
gotcha
edge_bow only bulges the cells' spline edges outward (>0) or inward (<0); the rendered image and other aux outputs are unaffected.
gotcha
The warp input (lattice) distorts the rendered image per pixel but the geometry aux ignores it entirely; use scatter's density input for geometry-matched variable density.
gotcha
animated=true drives w from the scene tick along a seamless forward-only loop over anim_start..anim_end at anim_rate, overriding the manual w slider.
gotcha
cells stamp attr:group (stable per-cell id) and attr:driver (per-cell random, feeds Stroke/Rasterize ramp_by=driver); centers stamp the same group id plus area-relative scale.
gotcha
source=points truncates an incoming points value to the first 1024 points (MAX_POINTS) and logs a one-time console warning past that.
Parameters
Name
Type
Default
Opacity
opacity
scalar
0 … 1
1
Source
source
enum
"lattice"
lattice · scatter · points
Mode
mode
enum
"f2-f1"
f1 · f2-f1 · f2 · cells · mask · position
Distance metric
metric
enum
"euclidean"
euclidean · manhattan · chebyshev · minkowski
Minkowski N
minkowski_n
scalar
0.1 … 16
2
Scale
scale
scalar
0.5 … 80
8
Jitter
jitter
scalar
0 … 1
1
Offset X
offset_x
scalar
-50 … 50
0
Offset Y
offset_y
scalar
-50 … 50
0
Cell count
count
scalar
4 … 1024
80
Placement
placement
enum
"image"
image · uniform · random
Density bias
density_gamma
scalar
0.1 … 8
1.5
Density floor
density_floor
scalar
0 … 1
0.05
Lloyd iterations
relax
scalar
0 … 8
1
Seed
seed
scalar
0 … 1000
0
W (Evolution)
w
scalar
-100 … 100
0
Animated
animated
boolean
false
Start (frame)
anim_start
scalar
0 … 100000
0
End (frame)
anim_end
scalar
1 … 100000
120
Rate
anim_rate
scalar
0 … 10
1
Edge width
edge_width
scalar
0.0001 … 0.2
0.01
Falloff
falloff
scalar
0.1 … 8
1
Contrast
contrast
scalar
0.1 … 5
1
Invert
invert
boolean
false
Warp (dark)
warp_lo
scalar
0.1 … 8
0.5
Warp (bright)
warp_hi
scalar
0.1 … 8
2
Color A (low)
color_a
color
"#000000"
Color B (high)
color_b
color
"#ffffff"
Alpha
alpha
scalar
0 … 1
1
Cell bow (splines)
edge_bow
scalar
-1 … 1
0
Sockets
Direction
Name
Type
input
UV
uv_in
uv
input
Warp
warp
image
outputprimary
out
image
outputaux
cells
spline
outputaux
edges
spline
outputaux
vertices
points
outputaux
centers
points
outputaux
neighbors
spline
Watercolor Ink
watercolor-inkpolymorphic
Cellular-automaton watercolor / sumi ink simulation (Zhang et al. 1999). Wire any mask/spline/shape into `deposit` to lay down ink; water spreads through fibrous paper (nijimi), suspended ink rides and diffuses with it, and ink fixes permanently where the water dries. Wire an image into `color` and the deposited ink keeps its hues (white = clear water; with `deposit` unwired the image's alpha doubles as coverage). Low concentration = thin ink that wicks far along fibers; high = dense marks. `water map` scales the delivered water per cell (wired alone = pre-wet the sheet, wet-on-wet); `ink map` scales pigment concentration per cell (needs water to ride — dry areas deliver nothing). Sample rate 0 = continuous contact; above 0, the input (e.g. a video frame) is stamped as a full wash every 1/rate seconds and left to bloom/dry between stamps. Plays while the timeline runs; restarting the timeline clears the sheet.
reads
time
flags
simulation, unstable
gotcha
resolution runs the sim grid at a fraction of canvas size (0.1–1); substeps and evap/rewet rates auto-scale by simScale² so bloom and drying stay roughly resolution-independent.
gotcha
Requires EXT_color_buffer_float; without it the simulation is disabled and the node just outputs a flat, blank paper-color image.
gotcha
Sim state (wet ink, dried ink, paper) persists per node in float32 textures across frames; a timeline restart (scene time wrapping back near zero) clears the sheet to dry and blank.
gotcha
drive_by_scene_time replaces the default timeline-playing gate with a wired `time` scalar input; the sim advances a substep only when that value increases.
gotcha
Changing fiber_count or paper_seed regenerates the paper texture and resets the sheet to dry, even mid-playback.
gotcha
lifetime=0 makes dried ink permanent; above 0, marks older than lifetime decay to about 1% of their value over `dissolve` seconds.
Parameters
Name
Type
Default
Deposit water
deposit_water
scalar
0 … 200
60
Ink concentration
dip_concentration
scalar
0.02 … 1
0.5
Sample rate (Hz)
sample_rate
scalar
0 … 30
0
Flow rate
alpha
scalar
0.05 … 1
0.8
Diffusion
beta
scalar
0 … 0.25
0.12
Evaporation
evap_rate
scalar
0 … 0.05
0.004
Re-wetting
rewet
scalar
0 … 0.05
0
Fade / sec
fade
scalar
0 … 1
0
Dried lifetime (s)
lifetime
scalar
0 … 60
0
Dissolve (s)
dissolve
scalar
0.05 … 10
1
Substeps / frame
substeps_per_frame
scalar
1 … 16
6
Resolution
resolution
scalar
0.1 … 1
0.5
Paper coarseness
fiber_count
scalar
0 … 200000
50000
Paper seed
paper_seed
scalar
0 … 1000000
0
Wet ink strength
wet_strength
scalar
0 … 2
0.55
Dried ink strength
dried_strength
scalar
0 … 2
0.9
Ink color
ink_color
color
"#000000"
Paper color
paper_color
color
"#f7f7f6"
Paper background
paper_bg
boolean
true
View
view
enum
"composite"
composite · water · ink · dried · paper
Drive by time input
drive_by_scene_time
boolean
false
Opacity
opacity
scalar
0 … 1
1
Sockets
Direction
Name
Type
input
deposit
deposit
mask
input
color
color
image
input
water map
water_map
mask
input
ink map
ink_map
mask
outputprimary
out
image
Webcam Source
webcam-source
Live webcam feed via getUserMedia. First eval triggers the browser permission prompt. Mirror toggle defaults on to match how video-chat tools render the feed.
space
param:offsetX,param:offsetY=uv01
flags
unstable
gotcha
offsetX/offsetY pan in per-axis UV fractions before the fit, not aspect-corrected; zoom scales about the canvas center.
gotcha
mirror flips the sample X axis after offset/zoom is applied, so a positive offsetX pans the opposite screen direction while mirror (the default) is on.
gotcha
Switching facing tears down the current getUserMedia stream and re-requests camera permission for the new facing; audio is never requested.
gotcha
Live capture only, so this node is not retimeable; a wrapping Time Offset passes the current frame through unshifted instead of retiming it.
Parameters
Name
Type
Default
Opacity
opacity
scalar
0 … 1
1
Facing
facing
enum
"user"
user · environment
Fit
fit
enum
"cover"
cover · contain · stretch
Offset X
offsetX
scalar
-1 … 1
0
Offset Y
offsetY
scalar
-1 … 1
0
Zoom
zoom
scalar
0.01 … 10
1
Mirror
mirror
boolean
true
Sockets
Direction
Name
Type
input
UV
uv_in
uv
outputprimary
out
image
Modifiers
Adaptive Pixelate
adaptive-pixelate
Pixelate with a non-constant grid: block size is driven by a luminance map (the Size Map input, or the image's own luminance). Quadtree mode splits blocks 4-ways where the map calls for detail; lattice mode crowds full-span rows/columns; uniform mode is a plain grid. Emits one point per block (center, scale, level) for building systems downstream.
size/block_min/block_max are absolute pixels at render resolution and do not scale with output size.
gotcha
Brighter size_map (or source luminance) drives smaller blocks (more detail) by default; invert flips that mapping, gamma reshapes the response curve.
gotcha
lattice_axes=columns/rows locks the other axis to a single full-span block instead of a grid; only 'both' crowds rows and columns independently.
gotcha
sample=average filters through a private mip chain at each cell's geometric-mean LOD, avoiding aliasing on large blocks; sample=center is a single texel tap at the cell center.
gotcha
aux:points is built every evaluation even when unwired, since the cell grid is already CPU-side for the image pass — it is not gated behind whether anything consumes it.
gotcha
aux:points positions are cx/canvasWidth, cy/canvasHeight (a per-axis fraction), not the aspect-corrected canvas01 convention other point sources use.
gotcha
On a driver readback failure (e.g. context loss) this silently falls back to a plain uniform grid at block_max instead of erroring.
Parameters
Name
Type
Default
Mode
mode
enum
"quadtree"
uniform · quadtree · lattice
Block size (px)
size
scalar
2 … 256
16
Min block (px)
block_min
scalar
2 … 128
8
Max block (px)
block_max
scalar
8 … 512
64
Invert map
invert
boolean
false
Map response
gamma
scalar
0.25 … 4
1
Axes
lattice_axes
enum
"both"
both · columns · rows
Sampling
sample
enum
"center"
center · average
Opacity
opacity
scalar
0 … 1
1
Sockets
Direction
Name
Type
input*
image
image
image
input
Size Map
size_map
mask
outputprimary
out
image
outputaux
points
points
Advect Image
advect-image
Flow an image along a velocity field: each pixel back-traces `steps` samples through the field (re-sampled every step, so content follows the field's curves — unlike Displace's single push). Deterministic and scrub-safe: `distance` is the total flow, keyframe it 0→N to animate content streaming along the field. Field modes match Advect Points (`vector` = signed-RG velocity from Perlin curl / Spline Flow Field, `angle` = luminance heading, `gradient` / `contour` = flow toward / around brightness). Optional speed image multiplies flow by its luminance. The `uv` aux carries the final warp for reuse in any uv consumer.
space
param:distance=canvas01
gotcha
With no field wired, steps collapses to 0, the trace becomes an identity uv map, and pass 2 is a straight copy of the source image.
gotcha
distance is the TOTAL flow across all steps in aspect-corrected canvas-width units, not a per-step amount; keyframe it 0->N to stream content along the field without touching steps.
gotcha
steps controls curve fidelity: each step re-samples the field, so steps=1 degenerates to a single Displace-style push instead of following the field's curves.
gotcha
speed multiplies flow by the speed image's luminance (dot with Rec.709 weights) each step, ignoring its color and alpha.
gotcha
gradient/contour modes derive direction from a finite-difference of field luminance (GRAD_EPS = 0.004 of canvas width), so a noisy or low-res field image causes visible jitter.
gotcha
edge=transparent drops samples that land outside [0,1] to alpha 0; clamp/wrap/mirror instead remap them back inside range.
gotcha
angle_turns/angle_offset only affect field_mode=angle; midlevel only affects field_mode=vector.
gotcha
aux:uv is built every evaluation regardless of whether it's wired, since this node is cacheable and a cache hit would otherwise serve a stale empty uv once something gets wired to it.
Parameters
Name
Type
Default
Field
field_mode
enum
"vector"
vector · angle · gradient · contour
Distance
distance
scalar
0 … 1
0.08
Steps
steps
scalar
1 … 64
12
Invert flow
invert
boolean
false
Angle range (turns)
angle_turns
scalar
0 … 4
1
Angle offset (turns)
angle_offset
scalar
-1 … 1
0
Midlevel
midlevel
scalar
0 … 1
0.5
Edge
edge
enum
"clamp"
transparent · clamp · wrap · mirror
Opacity
opacity
scalar
0 … 1
1
Sockets
Direction
Name
Type
input*
image
image
image
input*
Field
field
image
input
Speed field
speed
image
outputprimary
out
image
outputaux
uv
uv
Apply LUT
lut
Apply a .cube 3D LUT (color grade) to the image. The optional log2 HDR shaper applies OCIO-baked (shaper + cube) LUTs to scene-linear footage.
gotcha
shaper_min_stops/shaper_max_stops only apply when shaper=log2; with shaper=none, input is clamped to the LUT's own declared domain (usually 0..1) instead.
gotcha
No LUT loaded, or text that fails to parse, falls back to a straight passthrough copy that silently ignores intensity and shaper.
gotcha
The 3D texture is (re)parsed and re-uploaded only when the .cube text changes, cached per node instance; a failed parse is also cached to avoid re-parsing every frame.
gotcha
The volume uploads as RGBA16F with trilinear filtering, so LUT outputs above 1.0 survive unclamped for HDR-to-HDR transforms.
Parameters
Name
Type
Default
LUT (.cube)
lut
lut_file
—
Intensity
intensity
scalar
0 … 1
1
HDR shaper
shaper
enum
"none"
none · log2
Shaper min (stops)
shaper_min_stops
scalar
-16 … 0
-10
Shaper max (stops)
shaper_max_stops
scalar
0 … 16
6.5
Sockets
Direction
Name
Type
input*
image
image
image
outputprimary
out
image
ASCII
asciipolymorphic
Render the input image as a grid of glyphs — text characters from a palette string (any font), or each image in a connected image_group. Glyph and per-cell background colors source from flat / the cell's own color / a color ramp driven by index, seeded hash, position, brightness, or a wired image. Brightness remapping, per-cell modulators, and aux index/brightness outputs match the Array node's conventions.
gotcha
Ascii's index aux always uses column-first cell ordering; it only matches Array's aux index when Array's direction is set to flow-columns, not Array's default flow-rows.
gotcha
brightness aux is the in_min/in_max remap only — it ignores out_min/out_max and the blank threshold, so it can be nonzero for cells the main output renders fully transparent.
gotcha
threshold gates the whole cell (glyph and background) to fully transparent below it, not just the glyph; 0 (default) disables the gate.
gotcha
In image_set mode fg_source/fg_color/fg_ramp have no effect: the atlas item's own RGBA replaces the glyph outright, only the background layer still composites.
gotcha
bg_transform makes the background fill only the glyph's scaled/rotated tile instead of the whole cell rect, so glyph_scale < 1 opens transparent gutters between cells.
gotcha
ramp_by=position projects the cell center on a steerable axis Y-DOWN (angle 0 = left-to-right, 90 = top-to-bottom), matching the per-subpath ColorRampBy position mode.
Parameters
Name
Type
Default
Mode
mode
enum
"text"
text · image_set
Palette
text
string
" .:-=+*#%@"
Font
font_family
enum
"Menlo"
Inter · Roboto · Open Sans · Lato · Montserrat · Poppins · Work Sans · DM Sans · Nunito · Raleway · Oswald · Bebas Neue · Playfair Display · Merriweather · Crimson Text · Cormorant Garamond · Space Mono · JetBrains Mono · Helvetica · Arial · Georgia · Times New Roman · Courier New
Figma-style auto layout: stacks element children horizontally or vertically with alignment, padding, and gap. Children size as fixed (layout units), hug-contents, or fill-container; the aux element output makes layouts nestable. Plain image wires coerce to full-canvas elements — use per-slot trim or a Frame node to size them.
gap, padding, width/height, cornerRadius and strokeWidth are layout units (1 unit = min(canvasW,canvasH)/1000 px), scaling with project resolution, unlike raw pixel params.
gotcha
translateX/Y and pivotX/Y are per-axis UV fractions (uv01, not aspect-corrected) applied directly to the container's own v_uv, not width-relative canvas01.
gotcha
The aux element output is the nesting hook: a parent Auto Layout's fill slot re-solves this container at the granted size; hug sizes measure children instead.
gotcha
The primary output is a full-canvas image with the container centered and transformed; for nesting, wire the aux element output instead of the primary.
gotcha
Each item's trim crops it to its cached alpha bounding box before layout, shrinking its hug size to the non-transparent region.
Remove the background from an image or video. RMBG (still) runs BRIA's model locally via Transformers.js — Bake captures the upstream frame + alpha once. RVM (video) runs PeterLin's Robust Video Matting ONNX in the browser; Bake walks an in/out range and recycles the model's recurrent states so the matte stays temporally consistent. Live `feather` / `threshold` tweak the edge without a re-bake. RVM bakes are session-only (reopen → re-bake). Outputs the cutout (primary) plus the mask as both a typed mask aux and a grayscale image aux.
space
param:feather=uv01
reads
time
gotcha
compute() never runs ML inference; RMBG/RVM only run via the Bake button in the custom param panel. feather/threshold are the only per-frame live params.
gotcha
feather is a 9-tap blur offset scaled by the mask's OWN resolution (u_invMask), not canvas resolution, so a downsampled RVM mask blurs more per canvas pixel than a full-res one.
gotcha
Passthrough (no bake yet, or a missing bitmap) fills the mask and mask_image aux to opaque white, meaning everything reads as foreground.
gotcha
threshold's edge sharpness is not linear: the smoothing knee is WIDEST (softest edge) at threshold=0.5 and narrows toward a hard cutoff near 0.1 or 0.9.
Slice the image into bento-box rectangles (recursive cuts driven by a luminance map — the Size Map input, or the image's own luminance) and offset every piece along a seeded scatter direction by Fac: 0 = assembled, 1 = split apart. Animate Fac to assemble or shatter the image. Steps > 1 makes pieces travel an axis-aligned staircase (2 = slide in sideways, then drop into place), with one easing curve per step. Emits one point per piece at its animated center for driving systems downstream.
distance is a fraction of canvas width (distance x W); piece_min/piece_max/gap are literal raster pixels that stay fixed as output resolution changes.
gotcha
The cut grid is cached by a layout signature plus the driver's (size_map or image) identity; only geometry params rebuild it, not fac/steps/easing.
gotcha
steps >= 2 alternates horizontal/vertical legs per first_axis, splitting fac's 0..1 domain into equal per-leg ranges each shaped by easing.
gotcha
aux:points' y is (cy+offY)/H, a plain UV fraction, not the aspect-corrected canvas01 convention other points outputs use, so it reads off on non-square canvases.
gotcha
piece_min is silently raised toward sqrt(W*H/65536) to keep the worst-case piece count bounded, regardless of the slider value.
Bevel & Emboss — fake-3D raised edge from a height source. Outer / Inner / Emboss / Pillow Emboss styles. Two lights (each with angle, elevation, opacity) shade the height field with shared highlight + shadow colors and blend modes. Outer/Inner/Pillow use a JFA-derived signed-distance height (cached per input image); Emboss uses the input's luminance directly.
space
param:depth,param:soften,param:feather=pixels
gotcha
depth (px) sets bevel reach; the height gradient is rescaled by depth so relief strength (face angle) stays consistent as depth changes, in every style including emboss.
gotcha
style=emboss skips the JFA distance field entirely and reads the source's alpha-weighted luminance as height directly; depth still scales relief strength but not falloff reach.
gotcha
Wiring the optional height input JFAs its OWN alpha instead of the main image's, decoupling the relief shape from the visible image.
gotcha
outer-bevel and pillow-emboss extend output alpha outward past the source shape, up to a depth-sized band, so the raised rim is not clipped by the original alpha.
gotcha
The JFA distance field is cached by the height source's object identity; an animated source (video, generative) recomputes the full JFA pass every frame.
Skia-layer Bevel & Emboss, emboss style: the upstream's luminance (or the height input's) is the height field, shaded by two lights with highlight / shadow colors and blend modes, fused into the layer's one runtime shader. Same params as Bevel & Emboss; the JFA bevel styles (outer, inner, pillow) need a distance-field pass and are not offered.
space
param:depth,param:soften,param:feather=pixels
flags
no-mask, gates-outputs
gotcha
Only style=emboss is ported: outer/inner/pillow read a JFA distance field (multipass), emit transparent and report unsupported.
gotcha
The height is the alpha-weighted luminance of the height input, or of the image while height is unwired.
gotcha
4 evaluations of the height per pixel, 36 once feather ≥ 0.5 px (a 3×3 blur per tap): feather is the expensive knob.
gotcha
depth / soften / feather are canvas pixels (1 / iResolution); on a device they are points.
Mip-chain bloom (COD / UE5 / Unity HDRP technique). Soft-knee threshold + Karis-average downsample + 9-tap tent upsample give a crisp bright core with hundreds of pixels of soft tail at constant cost. Optional per-mip tint, anamorphic stretch, and lens dirt overlay. Quality picks where the pyramid starts: balanced/performance trade the glow's finest detail octave(s) for most of the pre-composite GPU cost, keeping the halo width unchanged.
flags
gates-outputs
gotcha
levels is capped so the smallest mip stays a few pixels wide, and quality=balanced/performance shrink it further to hold the halo width — effective levels can undercut the param.
gotcha
anamorphic only squashes the tent's cross axis down to 15% at ±1, never to 0, so a faint bleed on the off-axis remains even at the extremes.
gotcha
quality=performance undersamples the threshold pass (4 taps over an 8x8 block), so isolated 1-2px highlights can shimmer in motion.
gotcha
karis_average only changes the first downsample step; every later step always uses the plain COD tap weights.
Parameters
Name
Type
Default
Threshold
threshold
scalar
0 … 2
0.7
Soft Knee
soft_knee
scalar
0 … 1
0.5
Intensity
intensity
scalar
0 … 5
1
Levels
levels
scalar
1 … 8
6
Karis (firefly fix)
karis_average
boolean
true
Quality
quality
enum
"high"
high · balanced · performance
Anamorphic
anamorphic
scalar
-1 … 1
0
Core Tint
tint_low
color
"#ffffff"
Halo Tint
tint_high
color
"#ffffff"
Tint Amount
tint_amount
scalar
0 … 1
0
Sockets
Direction
Name
Type
input*
image
image
image
input
Lens Dirt
lens_dirt
image
outputprimary
out
image
outputaux
bloom_only
image
Bloom (Skia)
skia-bloom
Bloom over the Skia drawing wired into it, with Bloom's params: a soft-knee bright pass (a runtime filter), one Gaussian blur per mip level sized like Bloom's mip chain, tinted per level and added over the content (React Native Skia <RuntimeShader> + <Blur> on Group layers). Wire a Skia drawing in and the result into Skia Group or Skia Canvas.
space
out=pixels
flags
no-mask
gotcha
Each mip level is one Gaussian blur of the bright pass (sigma ≈ 1, 3.8, 7.9, 16, 32, 64 px at quality high), matched to Bloom's chain; the content is drawn levels + 1 times on device.
gotcha
The glow adds where the content is (its alpha), exactly like Bloom: a transparent region gets no glow.
gotcha
Lens dirt and the bloom_only output are not ported; isolated 1-2 px highlights glow slightly brighter than with Karis averaging.
gotcha
Skia's layers are 8-bit: a level whose glow exceeds 1 before its blur clips (very high intensity), where Bloom keeps float16.
gotcha
The bright pass is a runtime image filter: native React Native Skia only (RN web has no RuntimeShader filter).
Parameters
Name
Type
Default
Threshold
threshold
scalar
0 … 2
0.7
Soft Knee
soft_knee
scalar
0 … 1
0.5
Intensity
intensity
scalar
0 … 5
1
Levels
levels
scalar
1 … 8
6
Karis (firefly fix)
karis_average
boolean
true
Quality
quality
enum
"high"
high · balanced · performance
Anamorphic
anamorphic
scalar
-1 … 1
0
Core Tint
tint_low
color
"#ffffff"
Halo Tint
tint_high
color
"#ffffff"
Tint Amount
tint_amount
scalar
0 … 1
0
Sockets
Direction
Name
Type
input*
Content
content
skia_draw
outputprimary
out
skia_draw
Blur
blurpolymorphic
Blur with selectable kernel. Gaussian for a plain soft blur, or Bokeh for true circular apertures (disc, ring, soft) with the flat-topped shape and hot rim a Gaussian cannot produce. Filters in premultiplied linear light.
space
param:radius=pixels
gotcha
radius is in pixels at render resolution and does not scale with output size; Gaussian sigma = radius/2, capped at 64 taps per side (radius ≈43).
gotcha
The kernel input only exists when mode=convolve; in gaussian/bokeh modes there is nothing to wire it to.
gotcha
Filters in premultiplied linear light, so transparent edges do not darken but results differ from an sRGB-space blur.
Parameters
Name
Type
Default
Mode
mode
enum
"gaussian"
gaussian · bokeh · convolve
Radius (px)
radius
scalar
≥ 0
0
Shape
shape
enum
"disc"
disc · ring · soft · hexagon · octagon · cats_eye · star
Components
components
scalar
1 … 4
3
Rank
rank
scalar
1 … 8
4
Rotation
rotation
scalar
-180 … 180
0
Normalize kernel
normalize
boolean
true
Ring
ring
scalar
0 … 1
0.5
Blur in linear light
linearize
boolean
true
Opacity
opacity
scalar
0 … 1
1
Sockets
Direction
Name
Type
input*
image
image
image
outputprimary
out
image
Blur (Skia)
skia-blur
Gaussian blur of the Skia drawing wired into it (React Native Skia <Blur> on a Group layer), with Blur's params: radius in px (sigma = radius / 2) and blur in linear light. Wire a Skia drawing in and the result into Skia Group or Skia Canvas.
space
out,param:radius=pixels
flags
no-mask
gotcha
radius is canvas px, sigma = radius/2 as in Blur; on device the exported canvas scales, and the blur scales with it.
gotcha
Blur's GL kernel stops at 64 taps per side (radius ≈ 43): past that this node uses the sigma with the same spread, but Blur's truncated kernel is boxier.
gotcha
linearize blurs in linear light through two 8-bit Skia layers (Blur keeps float16): dark gradients band by a few levels.
gotcha
Content off the canvas blurs into view (Skia sizes the layer to the blur); Blur clamps the edge texel instead, so shapes touching the edge differ there.
gotcha
Only mode=gaussian ports; bokeh and convolve are refused and the content passes through unblurred.
Parameters
Name
Type
Default
Mode
mode
enum
"gaussian"
gaussian · bokeh · convolve
Radius (px)
radius
scalar
≥ 0
0
Blur in linear light
linearize
boolean
true
Sockets
Direction
Name
Type
input*
Content
content
skia_draw
outputprimary
out
skia_draw
Chromatic Aberration
chromatic-aberration
Split the red and blue channels away from green to fake lens dispersion or a glitchy RGB shift. Radial (grows from a center) or directional (uniform angled shift).
space
param:amount,param:center=uv01
gotcha
amount/center are UV fractions added straight to v_uv with no aspect term, so the same amount shifts a different fraction of width vs height.
gotcha
mode=directional ignores center and falloff entirely — only angle drives the shift.
gotcha
falloff only reshapes the radial ramp (0 = uniform amount everywhere, >1 = edge-heavy) and has no effect in directional mode.
gotcha
Out-of-range taps sample transparent black; output alpha is the max of the three channel taps to avoid dark edge fringing.
Parameters
Name
Type
Default
Amount
amount
scalar
0 … 0.1
0.004
Mode
mode
enum
"radial"
radial · directional
Center
center
vec2
[0.5,0.5]
Falloff
falloff
scalar
0 … 2
1
Angle
angle
scalar
-180 … 180
0
Opacity
opacity
scalar
0 … 1
1
Sockets
Direction
Name
Type
input*
image
image
image
outputprimary
out
image
Chromatic Aberration (Skia)
skia-chromatic-aberration
Skia-layer Chromatic Aberration: splits red and blue away from green by re-evaluating the upstream shader at three offsets, fused into the layer's one runtime shader. Same params as Chromatic Aberration — radial (grows from a center, shaped by falloff) or directional (a uniform shift along angle).
space
param:amount,param:center=uv01
flags
no-mask, gates-outputs
gotcha
amount and center are uv fractions with no aspect term, as in Chromatic Aberration; center is Y-down like the param, flipped in the shader.
gotcha
Three taps of the upstream per pixel (R at +offset, G at the pixel, B at −offset): a 3× re-evaluation of everything fused above it.
gotcha
mode=directional ignores center and falloff; mode=radial ignores angle.
gotcha
Taps outside the frame read transparent; output alpha is the max of the three taps × opacity.
Parameters
Name
Type
Default
Amount
amount
scalar
0 … 0.1
0.004
Mode
mode
enum
"radial"
radial · directional
Center
center
vec2
[0.5,0.5]
Falloff
falloff
scalar
0 … 2
1
Angle
angle
scalar
-180 … 180
0
Opacity
opacity
scalar
0 … 1
1
Sockets
Direction
Name
Type
input
image
image
skia_shader
outputprimary
out
skia_shader
outputaux
preview
image
Coherence Shock
shock-filter
Painterly sharpening: smooths ALONG the image's flow while pushing pixels toward their local light/dark extreme ACROSS it, so soft gradients collapse into crisp, fluid brush edges — the operator behind most 'hand-painted' looks; stack after Kuwahara or Flow Bilateral. Steered by the `field` input (Image Flow Field or any velocity field) or an internal estimate when unwired. `Iterations` compounds the effect (deterministic — safe to scrub and export), `Radius` sets how far the sharpening reaches, `Smooth along` the per-iteration flow smoothing, `Amount` blends the shock against the smoothed base.
radius and smooth_along are pixel taps at render resolution (u_invRes-scaled loops capped at 16 taps per side), so they do not scale with output size.
gotcha
Without a `field` input it estimates its own flow field once (preBlur 1, `smooth` controls its smoothing) — `smooth` is ignored the moment `field` is wired.
gotcha
iterations compounds smooth+shock passes in one compute (1-8, clamped); the field is sampled from the ORIGINAL source each iteration, not re-estimated, so animation stays stable.
gotcha
The shock pass picks dilation or erosion per pixel from the sign of the second luminance derivative across the flow, then blends toward the extreme sample by `amount`.
gotcha
Luminance sampling is coverage-weighted (lum × alpha) and the amount blend runs in premultiplied space, so transparent surrounds never win a dilation.
Parameters
Name
Type
Default
Iterations
iterations
scalar
1 … 8
3
Radius
radius
scalar
1 … 16
2
Smooth along
smooth_along
scalar
0 … 10
2
Amount
amount
scalar
0 … 1
1
Internal smooth
smooth
scalar
0 … 32
4
Opacity
opacity
scalar
0 … 1
1
Sockets
Direction
Name
Type
input*
image
image
image
input
Field
field
image
outputprimary
out
image
Color Correction
color-correction
Hue, saturation, brightness, contrast, and per-channel RGB curves.
gotcha
Order: white balance, lift/gamma/gain/offset wheels, per-zone exposure and saturation, brightness/contrast (pivoted), midtone detail, hue/saturation, RGB curves last.
gotcha
Each wheel's balance (X,Y) reads as a clockwise angle from "up" (red at top, matching the panel's hue disc), with radius as push strength.
gotcha
Per-wheel Sat/Exp are blended by luma zone weight (dark/mid/light) — a wheel barely touches pixels outside its own zone.
gotcha
The saturation param is a separate legacy HSV control, independent of the four wheels' own per-zone Sat fields.
Parameters
Name
Type
Default
Hue (°)
hue
scalar
-180 … 180
0
Saturation
saturation
scalar
0 … 50
1
Contrast
contrast
scalar
0 … 4
1
Brightness
brightness
scalar
-1 … 1
0
RGB Curves
curves
curves
(complex)
dark balance X
darkX
scalar
-1 … 1
0
dark balance Y
darkY
scalar
-1 … 1
0
dark master
darkLum
scalar
-2 … 2
0
dark exposure
darkExp
scalar
-2 … 2
0
dark saturation
darkSat
scalar
0 … 2
1
shadow balance X
shadowX
scalar
-1 … 1
0
shadow balance Y
shadowY
scalar
-1 … 1
0
shadow master
shadowLum
scalar
-2 … 2
0
shadow exposure
shadowExp
scalar
-2 … 2
0
shadow saturation
shadowSat
scalar
0 … 2
1
light balance X
lightX
scalar
-1 … 1
0
light balance Y
lightY
scalar
-1 … 1
0
light master
lightLum
scalar
-2 … 2
0
light exposure
lightExp
scalar
-2 … 2
0
light saturation
lightSat
scalar
0 … 2
1
global balance X
globalX
scalar
-1 … 1
0
global balance Y
globalY
scalar
-1 … 1
0
global master
globalLum
scalar
-2 … 2
0
global exposure
globalExp
scalar
-2 … 2
0
global saturation
globalSat
scalar
0 … 2
1
Temp
temp
scalar
-1 … 1
0
Tint
tint
scalar
-1 … 1
0
Pivot
pivot
scalar
0 … 1
0.5
Midtone Detail
md
scalar
-1 … 1
0
Black Offset
boffset
scalar
-1 … 1
0
Sockets
Direction
Name
Type
input*
image
image
image
outputprimary
out
image
Color Correction (Skia)
skia-color-correction
Skia-layer Color Correction: white balance, lift/gamma/gain/offset wheels, per-zone exposure and saturation, pivoted brightness/contrast, midtone detail, hue/saturation and RGB curves, fused into the layer's one runtime shader. Same params as Color Correction.
flags
no-mask, gates-outputs
gotcha
Order: white balance, wheels, per-zone exposure/saturation, brightness/contrast (pivoted), midtone detail, hue/saturation, RGB curves last — as Color Correction.
gotcha
Wheel balance (X, Y) is a clockwise angle from up (red at top) with radius as strength; per-wheel Sat/Exp weight by luma zone.
gotcha
Curves reproduce the GL node's 8-bit 256-entry LUT; up to 16 points per curve (more emits transparent and reports it).
gotcha
Alpha passes through. Unwired, the output is opaque black like Color Correction's.
Parameters
Name
Type
Default
Hue (°)
hue
scalar
-180 … 180
0
Saturation
saturation
scalar
0 … 50
1
Contrast
contrast
scalar
0 … 4
1
Brightness
brightness
scalar
-1 … 1
0
RGB Curves
curves
curves
(complex)
dark balance X
darkX
scalar
-1 … 1
0
dark balance Y
darkY
scalar
-1 … 1
0
dark master
darkLum
scalar
-2 … 2
0
dark exposure
darkExp
scalar
-2 … 2
0
dark saturation
darkSat
scalar
0 … 2
1
shadow balance X
shadowX
scalar
-1 … 1
0
shadow balance Y
shadowY
scalar
-1 … 1
0
shadow master
shadowLum
scalar
-2 … 2
0
shadow exposure
shadowExp
scalar
-2 … 2
0
shadow saturation
shadowSat
scalar
0 … 2
1
light balance X
lightX
scalar
-1 … 1
0
light balance Y
lightY
scalar
-1 … 1
0
light master
lightLum
scalar
-2 … 2
0
light exposure
lightExp
scalar
-2 … 2
0
light saturation
lightSat
scalar
0 … 2
1
global balance X
globalX
scalar
-1 … 1
0
global balance Y
globalY
scalar
-1 … 1
0
global master
globalLum
scalar
-2 … 2
0
global exposure
globalExp
scalar
-2 … 2
0
global saturation
globalSat
scalar
0 … 2
1
Temp
temp
scalar
-1 … 1
0
Tint
tint
scalar
-1 … 1
0
Pivot
pivot
scalar
0 … 1
0.5
Midtone Detail
md
scalar
-1 … 1
0
Black Offset
boffset
scalar
-1 … 1
0
Sockets
Direction
Name
Type
input
image
image
skia_shader
outputprimary
out
skia_shader
outputaux
preview
image
Color Matrix (Skia)
skia-color-matrix
Recolours the Skia drawing wired into it with a 4×5 colour matrix (React Native Skia <ColorMatrix> on a Group layer) on unpremultiplied colour: built from brightness, contrast, saturation, hue rotation, sepia, invert and opacity (CSS-filter style, in that order), or 20 numbers verbatim in custom mode.
flags
no-mask
gotcha
The matrix acts on unpremultiplied colour in 0..1; the 5th column is an offset in 0..1 units (not 0..255).
gotcha
Adjustments apply in a fixed order: brightness, contrast, saturation, hue, sepia, invert, opacity.
gotcha
Native colour filters run on the canvas's working-space numbers: on a Display P3 iPhone hue/saturation act on P3 values (sRGB canvases are exact).
gotcha
An animated control exports as a per-frame sampled matrix, not a track.
Parameters
Name
Type
Default
Mode
mode
enum
"adjust"
adjust · custom
Brightness
brightness
scalar
-1 … 1
0
Contrast
contrast
scalar
0 … 4
1
Saturation
saturation
scalar
0 … 4
1
Hue (°)
hue
scalar
-180 … 180
0
Sepia
sepia
scalar
0 … 1
0
Invert
invert
scalar
0 … 1
0
Opacity
opacity
scalar
0 … 1
1
Matrix (4×5)
matrix
string
"1 0 0 0 0, 0 1 0 0 0, 0 0 1 0 0, 0 0 0 1 0"
Sockets
Direction
Name
Type
input*
Content
content
skia_draw
outputprimary
out
skia_draw
Color Ramp
color-ramp
Remaps the input's luminance through a gradient of user-defined color stops. Offset slides the ramp; values past 1 wrap so the gradient loops. Interpolation picks the curve between stops (eases, splines through the stops, a Gaussian smooth, hard steps); Color space picks where the blend happens (sRGB, linear light, OKLab, OKLCH).
gotcha
Remaps by luminance (Rec.709 weighted), not per channel; the source's own alpha is preserved and multiplied by the ramp stop's alpha.
gotcha
offset=0 pins t=1 exactly to the last stop; any nonzero offset wraps the whole 0..1 domain via fract(), shifting what shows at the extremes.
gotcha
Up to 16 stops — extras are silently dropped after sorting by position.
gotcha
The ramp aux always emits the sorted stop list, even with nothing wired into image — it works as a pure palette source into Stroke/Rasterize Spline.
gotcha
interpolation=cardinal can overshoot past a stop's color; monotone is the no-overshoot spline. bspline approximates: interior stops pull the curve rather than pin it.
gotcha
space=oklch / oklch_long rotate hue the short / long way round with chroma kept up; out-of-gamut samples lose chroma, not hue. Identical hues under oklch_long make a full rainbow turn.
gotcha
The ramp is sampled through a 1024-entry LUT: a `constant` step lands on a 1/1024 grid of t.
Skia-layer Color Ramp: remaps the upstream shader's luminance through the stops, fused into the layer's one runtime shader. Same params as Color Ramp; offset slides and wraps the ramp. Every interpolation (eases, cardinal / monotone / B-spline, Gaussian smooth, constant) and color space (sRGB, linear, OKLab, OKLCH short / long) is ported.
flags
no-mask, gates-outputs
gotcha
Remaps Rec.709 luminance of the straight-alpha input; output alpha = stop alpha × input alpha.
gotcha
Evaluates the stops per pixel instead of the GL node's 1024-texel LUT; only `constant` keeps the LUT's 1/1024 step grid.
gotcha
Spline tangents, OKLCH hue unwrapping and the `smooth` blur are fitted in JS per change of the stops and ride as uniform arrays; smooth is 32 Hermite samples of the blur.
gotcha
space=oklch/oklch_long gamut-map per pixel (14 chroma bisection steps), the costliest setting.
gotcha
Unwired, the output is opaque black like Color Ramp's; up to 16 stops after sorting by position, extras dropped.
Convert between color spaces (sRGB, linear, ACEScg, ACES2065-1, Rec.709, Display P3) with an optional view transform (ACES / AgX / Filmic tone mapping) — the display step for scene-linear EXR renders.
gotcha
view=None performs a pure gamut+transfer conversion with no clamping, so scene-linear HDR passes through unclamped when both ends are linear; ACES/AgX/Filmic all clamp to [0,1].
gotcha
"Rec.709" shares its gamut primaries with Linear sRGB/sRGB — choosing it only swaps the transfer curve to BT.1886 (gamma 2.4).
gotcha
The view transform runs in linear sRGB between the two gamut conversions, so it tone-maps before the `to` gamut/transfer is applied.
Skia-layer Color Space: converts between sRGB, Linear sRGB, ACEScg, ACES2065-1, Rec.709, Gamma 2.2 and Display P3 with an optional ACES / AgX / Filmic view transform, fused into the layer's one runtime shader. Same params as Color Space.
flags
no-mask, gates-outputs
gotcha
view=None is a pure gamut + transfer conversion with no clamp (values past 0..1 survive into the next Skia node); ACES/AgX/Filmic clamp to 0..1.
gotcha
The view transform runs in linear sRGB between the two gamut conversions, as in Color Space; Rec.709 only swaps the transfer to BT.1886 (gamma 2.4).
gotcha
The fused shader ends on an 8-bit sRGB canvas: a scene-linear result is clipped there, not carried as HDR.
gotcha
Alpha passes through × opacity (folded into the shader). Unwired, the output is transparent.
Glitch-melt two clips into each other. Wire two image inputs, then in the panel Bake each clip into the node, drag the two clips on the mini-timeline so they overlap, and the overlap is moshed: the incoming clip's motion warps a frozen frame of the outgoing clip (optical-flow advection). Flow Scale sets the smear distance, Smear its length, Decay its falloff, Refresh how fast the real incoming pixels bleed back. Preview plays forward live; Mosh bakes the result for clean scrubbing and export. (Inputs are session-only — reopen → re-bake.)
space
param:flowScale=uv01 param:searchRadius=pixels
reads
time
flags
simulation
gotcha
engine=codec is accepted and serialized but currently renders identically to flow; the panel disables selecting it (M2 stub).
gotcha
flowScale is a uv01 fraction (max per-pixel displacement, no aspect term); searchRadius (block estimator) is a texel count that does not scale with output size.
gotcha
aStart/aLen/bStart/bLen/sourceInA/B and bakeInA/OutA/bakeInB/OutB are hidden params written only by the node's custom timeline panel, not the normal param list.
gotcha
Live (not-yet-output-baked) mode only accumulates forward: scrubbing before the last built frame reseeds from clip A's frozen frame at the overlap start.
gotcha
If a needed strip frame isn't decoded yet, the node holds the last accumulated frame (or clears to transparent) instead of blocking, and retries next eval.
Parameters
Name
Type
Default
Engine
engine
enum
"flow"
flow · codec
Algorithm
estimator
enum
"gradient"
gradient · residual · block
Search
searchRadius
scalar
1 … 8
4
Flow Scale
flowScale
scalar
0 … 1
0.08
Smear
smear
scalar
1 … 64
8
Decay
decay
scalar
0 … 1
0.85
Refresh
refresh
scalar
0 … 1
0.05
Remove I-frames
iframeRemoval
boolean
true
P-frame Dup
pframeDup
scalar
0 … 8
0
Sockets
Direction
Name
Type
input
clip A
imageA
image
input
clip B
imageB
image
outputprimary
out
image
Depth Anything
depth-anything
Estimate a depth map from an image locally via Transformers.js (Depth Anything V2). The header toggle switches the output between the depth map (grayscale, near=white) and a normal map derived from the depth gradient. Invert flips near/far; Near/Far set the depth black/white points. For animated inputs, Preview runs depth on the current frame and Bake runs the model over an in/out frame range and caches a depth map per frame (held in memory for this session — reopen → re-bake). Note: the model normalizes each frame independently, so brightness can drift over a clip.
reads
time
gotcha
precision (fp32/fp16/q8) only affects inference quality when the panel bakes or previews; compute() and the render path ignore it entirely.
gotcha
near/far only affect outputMode=depth; the normal map uses only invert and normalStrength and ignores near/far.
gotcha
If far <= near, the denominator clamps to a 1e-4 epsilon instead of erroring, blowing the depth remap out to near-solid black or white.
gotcha
Before any Preview/Bake, the primary passes the input through unchanged while the aux depth mask reads solid 0, not transparent or undefined.
Parameters
Name
Type
Default
Output
outputMode
enum
"depth"
depth · normal
Model
model
enum
"v2-small"
v2-small · v2-base · v2-large
Quality
precision
enum
"fp32"
fp32 · fp16 · q8
Invert
invert
boolean
false
Near
near
scalar
0 … 1
0
Far
far
scalar
0 … 1
1
Normal Strength
normalStrength
scalar
0 … 8
1
Sockets
Direction
Name
Type
input*
image
image
image
outputprimary
out
image
outputaux
depth
mask
Displace
displacepolymorphic
Offset by a vector read from a displacement field. Wire an image and each pixel is pushed; wire a spline or points value and each anchor/point is pushed (sampled at its own UV). Channel X/Y pick the per-axis channels (RG vector field, or luminance for both); midlevel is the neutral value (0.5 for signed maps). Rotate (optional) also spins from the map's luminance — images around the frame center, points via per-point rotation, splines via each anchor's handles.
amountX/Y are per-axis UV fractions ((channel − midlevel) × amount), not aspect-corrected: 0.05 pushes 5% of width in X and 5% of height in Y.
gotcha
Polymorphic on the image socket: an image is pushed per pixel; a spline or points value is pushed per anchor/point, each sampling the map at its own UV.
gotcha
midlevel is the neutral map value (0.5 for signed 8-bit maps, 0 for unsigned); channelX/Y pick which map channels drive each axis.
gotcha
rotate spins from the map's luminance: images about the frame center, points via per-point rotation, splines via anchor handles.
gotcha
wrap (Edge, image path only): transparent → alpha 0 past the frame; clamp → edge pixel; mirror reflects only out-of-range samples (1.2 → 0.8, −0.3 → 0.3), in-range ones untouched.
Parameters
Name
Type
Default
Amount X
amountX
scalar
-1 … 1
0.05
Amount Y
amountY
scalar
-1 … 1
0.05
Channel X
channelX
enum
"r"
r · g · b · a · luminance
Channel Y
channelY
enum
"g"
r · g · b · a · luminance
Midlevel
midlevel
scalar
0 … 1
0.5
Rotate
rotate
boolean
false
Amount (°)
rotateAmount
scalar
-360 … 360
180
Edge
wrap
enum
"clamp"
transparent · clamp · mirror
Sockets
Direction
Name
Type
input*
image
image
image
input*
displace
displacement
image
outputprimary
out
image
Displace (Skia)
skia-displace
Skia-layer Displace: pushes each pixel of the upstream shader by a vector read from the displacement shader, fused into the layer's one runtime shader. Same params as Displace (per-axis amount in uv fractions, channel X/Y, midlevel, optional rotate from the map's luminance, edge rule). Image branch only — spline and points displacement stay on the default layer.
space
param:amountX,param:amountY=uv01
flags
no-mask, gates-outputs
gotcha
amountX/Y are per-axis uv fractions ((channel − midlevel) × amount), not aspect-corrected, exactly as Displace.
gotcha
The map is read at the pixel's own uv; the source is re-evaluated once at the displaced uv (a 1-tap gather), not sampled from a texture.
gotcha
Unwired map: passes the source through, like Displace (the port feeds a constant map at midlevel).
gotcha
wrap=mirror reflects only out-of-range samples (period-2 mirror tile, 1 − |fract(uv/2)·2 − 1|): identity inside the frame, 1.2 → 0.8 and −0.3 → 0.3, exactly as Displace.
gotcha
Image branch only: the Skia sockets carry shaders, so spline / points displacement has no Skia form.
Parameters
Name
Type
Default
Amount X
amountX
scalar
-1 … 1
0.05
Amount Y
amountY
scalar
-1 … 1
0.05
Channel X
channelX
enum
"r"
r · g · b · a · luminance
Channel Y
channelY
enum
"g"
r · g · b · a · luminance
Midlevel
midlevel
scalar
0 … 1
0.5
Rotate
rotate
boolean
false
Amount (°)
rotateAmount
scalar
-360 … 360
180
Edge
wrap
enum
"clamp"
transparent · clamp · mirror
Sockets
Direction
Name
Type
input
image
image
skia_shader
input
displace
displacement
skia_shader
outputprimary
out
skia_shader
outputaux
preview
image
Dither
dither
Quantize the input image with error-diffusion or ordered dithering.
gotcha
pixel_scale downsamples to width/height divided by pixel_scale before dithering, then upscales with nearest filtering for blocky pixels, not a blur.
gotcha
color_mode=false quantizes to black/white using threshold (1..255); color_mode=true quantizes each RGB channel to levels steps instead and ignores threshold.
gotcha
spread only scales the diffused error in the kernel algorithms (floyd-steinberg/atkinson/stucki/burkes/sierra/jarvis); ordered and threshold ignore it.
gotcha
algorithm=ordered uses an 8x8 Bayer matrix against threshold or levels; it never reads spread.
Skia-layer Dither: ordered (8×8 Bayer) or threshold quantization to black/white or per-channel levels, at a pixel scale, fused into the layer's runtime shader. Same params as Dither; the error-diffusion algorithms have no per-pixel form and are reported unsupported.
flags
no-mask, gates-outputs
gotcha
Only algorithm=ordered and threshold are ported; the error-diffusion algorithms (the default floyd-steinberg included) emit transparent and report it.
gotcha
pixel_scale makes round(canvas/pixel_scale) blocky cells, Bayer rows counted from the bottom, each the source sampled bilinearly at its centre.
gotcha
color_mode=false thresholds Rec. 601 gray (0..255) against threshold; color_mode=true quantizes each channel to levels steps and ignores threshold.
gotcha
The source is read 1 tap per cell centre when it lands on a texel, else 4 (bilinear). Unwired, the output is opaque black like Dither's.
A drop shadow (or inner shadow) under the Skia drawing wired into it — React Native Skia <Shadow> on a Group layer: the content's alpha offset by dx/dy px, blurred (radius px, sigma = radius/2) and tinted. shadow_only keeps the shadow and drops the content.
space
out,param:dx,param:dy,param:blur=pixels
flags
no-mask
gotcha
dx / dy / blur are canvas px (y down); blur is a radius like CSS box-shadow: sigma = blur/2.
gotcha
The shadow is cast by the content's alpha only; its colour is `color` (alpha included) whatever the content's colours.
gotcha
inner draws the shadow inside the shape's edge (React Native Skia's inner shadow); shadow_only keeps just the shadow.
Parameters
Name
Type
Default
Offset X (px)
dx
scalar
-512 … 512
6
Offset Y (px)
dy
scalar
-512 … 512
6
Blur (px)
blur
scalar
0 … 256
12
Color
color
color
"#000000b3"
Mode
mode
enum
"drop"
drop · inner
Shadow only
shadow_only
boolean
false
Sockets
Direction
Name
Type
input*
Content
content
skia_draw
outputprimary
out
skia_draw
Edge Detect
edge-detect
Extract edges via Sobel or Prewitt 3×3 gradient convolution. Output is grayscale gradient magnitude — brighter = stronger edge. Threshold clamps weak responses to zero for binary edge maps.
gotcha
threshold clips the strength-scaled magnitude, not the raw gradient, so raising strength also lowers the effective threshold.
gotcha
Samples at a fixed 1-pixel offset (1/src.width, 1/src.height), so edge width is always one source pixel and does not scale with output resolution.
gotcha
Output is fully opaque (alpha forced to 1) and grayscale (R=G=B=magnitude); the source's own alpha channel is discarded.
Parameters
Name
Type
Default
Algorithm
algorithm
enum
"sobel"
sobel · prewitt
Strength
strength
scalar
0 … 10
1
Threshold
threshold
scalar
0 … 1
0
Sockets
Direction
Name
Type
input*
image
image
image
outputprimary
out
image
Edge Detect (Skia)
skia-edge-detect
Skia-layer Edge Detect: Sobel or Prewitt gradient magnitude of the upstream shader's luminance, fused into the layer's one runtime shader by re-evaluating the upstream at the eight neighbor pixels. Same params as Edge Detect; output is opaque grey.
flags
no-mask, gates-outputs
gotcha
Eight evaluations of the upstream per pixel (the center is not read): 8× the cost of a procedural chain fused above it.
gotcha
Taps are one canvas pixel apart (1 / iResolution); on a device that is one point, so edges widen with pixel density.
gotcha
threshold clips the strength-scaled magnitude, so raising strength lowers the effective threshold.
gotcha
Output is opaque grey (the source alpha is discarded); unwired, it is transparent like Edge Detect's.
Parameters
Name
Type
Default
Algorithm
algorithm
enum
"sobel"
sobel · prewitt
Strength
strength
scalar
0 … 10
1
Threshold
threshold
scalar
0 … 1
0
Sockets
Direction
Name
Type
input
image
image
skia_shader
outputprimary
out
skia_shader
outputaux
preview
image
Flow Bilateral
flow-bilateral
Edge-preserving smoothing aligned to the image's flow: a 1D bilateral filter runs across the local edge direction, then along it, per iteration — flattening texture and noise while edges and flowing detail survive. Much cheaper than a full 2D bilateral and calmer on video. The abstraction base of the toon look (follow with Posterize and Line Art). Steered by the `field` input (Image Flow Field or any velocity field) or an internal estimate when unwired. `Spatial σ` is the smoothing reach in px, `Range σ` how different colors must be to count as an edge (lower preserves more), `Across scale` shrinks the across-edge reach to protect edges harder.
space
param:sigma_s=pixels
gotcha
sigma_s is the spatial reach in pixels of each 1D pass; across_scale multiplies it only for the across-edge (gradient-axis) pass, not the along-edge (tangent) pass.
gotcha
field unwired computes its own orientation field internally from the source each evaluation; smooth only affects that internal estimate and is ignored while field is wired.
gotcha
Each iteration runs one across-edge pass then one along-edge pass in that fixed order; iterations (1..4) roughly multiply the cost.
gotcha
Both the spatial/range weighting and the range-distance comparison operate on premultiplied RGBA, so a soft edge is smoothed by its composited color, not its stored straight color.
Parameters
Name
Type
Default
Iterations
iterations
scalar
1 … 4
2
Spatial σ
sigma_s
scalar
0.5 … 10
4
Range σ
sigma_r
scalar
0.01 … 1
0.1
Across scale
across_scale
scalar
0.1 … 2
1
Internal smooth
smooth
scalar
0 … 32
4
Opacity
opacity
scalar
0 … 1
1
Sockets
Direction
Name
Type
input*
image
image
image
input
Field
field
image
outputprimary
out
image
Flow Blur
flow-blur
Smear the image along a flow field's streamlines (line integral convolution) — the painterly Van Gogh blur, and the standard way to SEE a field (wire noise into the image input). With `field` unwired the node derives flow from the image itself (structure tensor at `Internal smooth`); wire an Image Flow Field, Perlin curl, Spline Flow Field, or a sim's velocity aux to steer it. `Length` is the total ± smear distance (canvas-width fraction), `Samples` the quality per side. `Tangent` mode follows edge orientation regardless of direction; `Velocity` mode follows a directed field, smearing further where it's faster.
space
param:length=canvas01
gotcha
length is the total canvas-width-fraction smear distance split across both walk directions; step per sample = length/samples.
gotcha
field unwired: derives an internal structure-tensor field from the image itself (1px pre-blur, smooth param); wiring field ignores smooth entirely.
gotcha
mode=tangent walks the sign-coherent, direction-agnostic tangent; mode=velocity treats the field RG as a directed velocity, so smear length scales with local speed.
gotcha
Averages samples in premultiplied color and un-premultiplies on write, avoiding the darkened-fringe bug of straight-alpha averaging at soft edges.
Parameters
Name
Type
Default
Length
length
scalar
0 … 1
0.06
Samples
samples
scalar
1 … 64
16
Mode
mode
enum
"tangent"
tangent · velocity
Falloff
falloff
enum
"gauss"
gauss · box
Internal smooth
smooth
scalar
0 … 32
4
Opacity
opacity
scalar
0 … 1
1
Sockets
Direction
Name
Type
input*
image
image
image
input
Field
field
image
outputprimary
out
image
Flow Blur (Skia)
skia-flow-blur
Skia-layer Flow Blur: smears the upstream shader along a wired flow field's streamlines (line integral convolution), fused into the layer's one runtime shader. Same params as Flow Blur — Length is the total ± smear (canvas-width fraction), Samples the steps per side (≤ 64), Tangent or Velocity mode. The field must be wired: the internal structure-tensor field is a multipass and is not offered.
space
param:length=canvas01
flags
no-mask, gates-outputs
gotcha
The field must be wired: Flow Blur's internal structure-tensor field is multipass, so unwired the node emits transparent and says why; smooth is unused.
gotcha
Both inputs are gathers re-read at every step: 1 + 2 × samples evaluations of the image AND of the field per pixel.
gotcha
samples rounds to 1..64 (the loop cap, Flow Blur's own clamp); step = length / samples, y scaled by aspect.
gotcha
Averages in premultiplied color and un-premultiplies on write; opacity scales the output alpha.
Parameters
Name
Type
Default
Length
length
scalar
0 … 1
0.06
Samples
samples
scalar
1 … 64
16
Mode
mode
enum
"tangent"
tangent · velocity
Falloff
falloff
enum
"gauss"
gauss · box
Internal smooth
smooth
scalar
0 … 32
4
Opacity
opacity
scalar
0 … 1
1
Sockets
Direction
Name
Type
input
image
image
skia_shader
input
Field
field
skia_shader
outputprimary
out
skia_shader
outputaux
preview
image
Flow Obstacle
flow-obstacle
Make a velocity field respect a shape: wire a field (Perlin Noise curl / Spline Flow Field) and an obstacle mask (any spline or shape coerces in), and flow deflects around the silhouette instead of passing through it. `deflect` redirects flow to slide along the boundary (and stalls it inside); `block` just damps flow by coverage for a dead-water stop. `radius` sets how far out the boundary's influence reaches — blur the mask upstream for an even softer approach. Chain several obstacles in series for multiple shapes.
space
param:radius=canvas01
gotcha
radius sets the finite-difference tap distance (canvas-width fraction, y-tap scaled by aspect) that mints a soft shell around the obstacle mask.
gotcha
mode=deflect removes the velocity component pointing into the shell and damps flow deep inside (coverage above ~0.5); mode=block just damps by coverage everywhere.
gotcha
Coverage is a 5-tap weighted average ((2*center + N+S+E+W)/6), not a true SDF falloff, so a hard mask still gets a soft shell from the taps alone.
gotcha
obstacle unwired: falls back to sampling the field texture itself as a dummy and forces strength to 0, i.e. a pure pass-through.
Parameters
Name
Type
Default
Mode
mode
enum
"deflect"
deflect · block
Radius
radius
scalar
0.002 … 0.2
0.03
Strength
strength
scalar
0 … 1
1
Sockets
Direction
Name
Type
input*
field
field
image
input*
obstacle
obstacle
mask
outputprimary
out
image
GLSL Expression
glsl-expressionpolymorphic
Write the body of a GLSL fragment shader and run it as one fullscreen pass — the per-pixel counterpart to Point Expression. Read wired images with texture(u_a, v_uv) (u_b–u_d), the clock with u_time/u_frame, and canvas size with u_res; write fragColor with straight alpha. Top-level function and struct definitions are hoisted above main() (or wrap them in // functions … // body). Declare tunables as one-line // comments and hit Sync to mint controls that the template exposes by name: // ch("k", 0.5, 0, 1) → uniform float k (slider); // toggle("on", true) → uniform bool on (pill); // pick("mode", "a", "b") → uniform int mode plus const int mode_a=0, mode_b=1 (2–3-way pill / dropdown); // color("tint", "#ff8800") → uniform vec4 tint (straight alpha, swatch); // ramp("ink", "#000000", "#ffffff") → vec4 ink(float t) (gradient editor, seeded by the hex list); // curve("falloff", 1, 0) → float falloff(float x) (float-curve editor, seeded by the y list). Sync is add-only (rows keep their tuned value — tune with set_param by channel NAME); scalar/toggle/color/ramp channels are also wireable inputs (scalar/scalar/vec4/color_ramp). Names can't start with u_. A compile error still renders passthrough/transparent — the WebGL info log is on get_shader_errors / get_graph (shaderError), and once in the console.
reads
time
gotcha
The cache only busts on the clock when u_time or u_frame appears literally in the source text; time-driven behavior reached only through a channel value won't invalidate the cache.
gotcha
Channel names may not contain a double underscore (GLSL reserves __identifiers) even if they'd otherwise pass the u_-prefix rule.
gotcha
Image inputs occupy texture units 0-3; ramp/curve channels bind LUTs starting at unit 4, capped at 12 ramp/curve channels per node (extras sample black).
gotcha
resolveInputs adds a wireable socket named in:<channel id> (not the channel's display name) for scalar/toggle/color/ramp channels.
gotcha
Top-level function and struct definitions are hoisted above main(); if auto-hoist misses a helper, wrap it in // functions … // body.
Parameters
Name
Type
Default
Channels
inputs
expr_inputs
[]
Shader
expression
string
"// Body of main(), plus optional helpers above it.\n// Top-level function / struct definitions are hoisted out of main()\n// (or wrap them in // functions … // body). Read inputs with\n// texture(u_a, v_uv) (u_b..u_d), the clock with u_time, canvas size\n// with u_res. Declare tunables as comments, hit Sync, then read them\n// by name:\n// // ch(\"amount\", 0.5, 0, 1) float amount\n// // toggle(\"invert\", false) bool invert\n// // pick(\"mode\", \"soft\", \"hard\") int mode (mode_soft, mode_hard)\n// // color(\"tint\", \"#ff8800\") vec4 tint\n// // ramp(\"ink\", \"#000000\", \"#ffffff\") vec4 ink(float t)\n// // curve(\"falloff\", 1, 0) float falloff(float x)\n// Write fragColor (straight alpha).\nvec4 a = texture(u_a, v_uv);\nfragColor = a;"
On error
on_error
enum
"passthrough"
passthrough · transparent
Sockets
Direction
Name
Type
input
a
a
image
input
b
b
image
input
c
c
image
input
d
d
image
outputprimary
out
image
Grain
grain
Procedural grain in six models — fine hash grain, soft blurred grain with coarser octaves, Boolean film discs, digital sensor noise, analog video snow, or a wired image / video plate — with a tonal response curve, display / linear / log working space, a per-channel colour model and film-stock presets, frame-locked to the project clock with a variance-preserving temporal kernel for smooth or looping evolution, composited over an optional image through a choice of mix modes or emitted alone on 50% gray.
space
param:scale=pixels
reads
time
flags
gates-outputs
gotcha
Size is grain cell size in render px; size_unit=1080p px scales by canvas height/1080, um units take size_um through the film gauge width; aspect stretches cells (video: streak x Size).
gotcha
model=classic is the pre-v2 shader for saved projects (float hash, seed re-rolls continuously); a preset other than custom drives every look param from its table - pick custom to edit them.
gotcha
luminance/chromatic = display std at mid-grey in every space; response scales grain by input luminance; saturation 0 = mono chromatic grain; no image = grain alone on 50% gray (also the grain aux).
gotcha
soft blurs the plate (softness cells, sum w^2 = 1) and mixes 2x/4x octaves by irregularity; film draws Boolean disc grains at canvas/quality res whose count follows -ln(1-Y)*density.
gotcha
sensor = fine + fixed pattern, banding, chroma blotch; video = streak x Size cells with line jitter/dropout; plate tiles the wired image; parametric tiles a CPU AR(1) template (cost ~ taps).
gotcha
motion=animated steps grain time from the project clock at rate fps: rate 0 = new grain once per project frame in preview and export alike; rate 12 at 24 fps holds each grain frame two frames.
gotcha
smoothness = sigma (grain frames) of a Gaussian moving average over hashed frames, renormalised by 1/sqrt(sum w^2) so contrast never breathes (kernel=bspline: 4 taps); drift wanders the layer, px/s.
gotcha
evolution is a continuous grain-time offset in every mode (a wired ramp morphs smoothly when smoothness > 0); looping repeats every loop_frames project frames, which must divide the project loop.
Parameters
Name
Type
Default
Preset
preset
enum
"custom"
custom · 35mm fine · 35mm 500T · super 16 · super 8 · digital ISO 800 · digital ISO 6400 · video snow
Model
model
enum
"fine"
fine · soft · film · parametric · sensor · video · plate · classic
Size
scale
scalar
1 … 32
1
Size (µm)
size_um
scalar
1 … 200
20
Size Unit
size_unit
enum
"px"
px · 1080p px · um 35mm · um super 16 · um super 8
Grain's classic model over the Skia drawing wired into it — a runtime image filter (React Native Skia <RuntimeShader>): hashed cells of `scale` px, luminance + chromatic grain, Grain's seven mix modes, mix and view. Keyframe `seed` to animate it, as with classic Grain.
space
out,param:scale=pixels
flags
no-mask
gotcha
Only model=classic ports (its float hash is ES2-safe); the v2 models hash with uint pcg3d and are refused.
gotcha
scale is the grain cell in canvas px; on device the exported canvas scales and the cells scale with it.
gotcha
Classic grain is static unless seed animates (keyframe it) — the v2 models' frame-locked motion is not part of this node.
gotcha
A runtime image filter: native React Native Skia only (RN web has no RuntimeShader filter), so a web export doesn't play it.
Parameters
Name
Type
Default
Model
model
enum
"classic"
classic · fine · soft · film · parametric · sensor · video · plate
Detect up to two hands in an incoming image using MediaPipe HandLandmarker. Primary output is the hand skeleton as a spline (21 landmarks wired into 21 bones per hand). Aux outputs expose left/right wrist positions as vec2; toggling Show fingers adds per-finger tip vec2s (thumb / index / middle / ring / pinky on each side). Detection is throttled via `detect_fps` and the output is exponentially smoothed so downstream motion stays fluid between detect calls.
space
out,aux:left,aux:right=uv01
flags
unstable
gotcha
aux/out landmark positions are per-axis image-normalized (uv01), not width-isotropic canvas01, so a non-square input image skews the skeleton and finger positions.
gotcha
Detection throttles to detect_fps; between detect calls the node re-smooths the last-known landmarks every eval, keeping motion fluid at low detect_fps.
gotcha
A hand that exits frame keeps emitting its last smoothed skeleton (latched) instead of vanishing; a side never yet seen defaults vec2/points aux to (0.5,0.5).
gotcha
enabled=false freezes detection but keeps re-emitting the last latched, smoothed output, so the composed scene holds while paused.
gotcha
finger_output_mode swaps the aux socket set: vec2/point add ten per-side-finger sockets each; points adds one combined fingers points socket instead.
gotcha
flip_handedness (default true) swaps MediaPipe's person-perspective left/right label, since a mirrored front-facing webcam reports it backwards.
Parameters
Name
Type
Default
Enabled
enabled
boolean
true
Model
model
enum
"lite"
lite · full
Max hands
max_hands
enum
"1"
1 · 2
Confidence
confidence
scalar
0 … 1
0.5
Input size
input_size
scalar
128 … 1024
192
Detect rate (fps)
detect_fps
scalar
1 … 60
8
Smoothing
smoothing
scalar
0 … 0.99
0.5
Flip handedness
flip_handedness
boolean
true
Finger output
finger_output_mode
enum
"off"
off · vec2 · point · points
Sockets
Direction
Name
Type
input*
image
image
image
outputprimary
out
spline
outputaux
left
vec2
outputaux
right
vec2
Image Flow Field
image-flow-field
Estimate the flow of an image: per-pixel edge orientation (smoothed structure tensor) encoded as a field image — tangent direction in RG (velocity-field convention, so Advect Image/Points and Displace consume it directly) and anisotropy/coherence in B (0 = flat region, 1 = strong directed edge). The steering input for painterly nodes (Flow Blur, and the rest of the program as it lands): compute the field once, drive many consumers. `Pre-blur` gates pixel noise out of the gradient; `Smooth` sets the tensor-blur scale — larger values give longer, more coherent stroke directions. The `coherence` aux exposes B as a mask for driving anything (scatter density, thresholds…). Visualize the field by wiring it plus a noise image into Flow Blur. Don't matte or mask a field image — matte the consumer's output.
space
param:pre_blur,param:smooth=pixels
flags
no-mask
gotcha
pre_blur and smooth are Gaussian sigma in pixels at render resolution (radius = ceil(3σ), capped at 32 taps, a no-op below σ≈0.3px).
gotcha
RG encodes a π-periodic unit tangent (velocity-field convention, Y-down): t and −t are the same orientation; the producer picks the tx>0 representative.
gotcha
Gradients are computed on coverage-weighted luminance (lum × alpha), so a shape on transparency encodes its silhouette's orientation, not the background's.
gotcha
An unwired or non-image source returns a neutral field (zero tangent, coherence 0) rather than skipping the node.
gotcha
coherence is the B channel, (λ1−λ2)/(λ1+λ2) in [0,1]; any plain velocity image fed to an orientation consumer decodes coherence as 0 (flat).
Parameters
Name
Type
Default
Pre-blur
pre_blur
scalar
0 … 8
1
Smooth
smooth
scalar
0 … 32
4
Sockets
Direction
Name
Type
input*
source
source
image
outputprimary
out
image
outputaux
coherence
mask
Keyer
keyer
Keys pixels transparent by luminance (luma key), RGB similarity to a key color (color key), chroma-plane similarity (chroma key — the green-screen mode, brightness-independent), or similarity to a drawn color selection (sample mode — select the node and scrub across the canvas to sample the colors to remove). Softness feathers the matte edge, Spill Suppression desaturates key-color bleed on kept pixels, and Invert swaps keep/key. The matte is also available as a mask aux output.
gotcha
threshold only applies in mode=luma; key_color, tolerance, and spill apply only in color/chroma/sample modes.
gotcha
softness feathers symmetrically around threshold in mode=luma, but only outward past tolerance in the other modes.
gotcha
In mode=sample, the despill reference (used for spill) is the mean of the sampled colors, not key_color.
gotcha
sample_colors is a hidden list scrubbed from the canvas via the node's overlay; empty list keys nothing, capped at 64 colors.
Parameters
Name
Type
Default
Mode
mode
enum
"luma"
luma · color · chroma · sample
Threshold
threshold
scalar
0 … 1
0.5
Key Color
key_color
color
"#00ff00"
Tolerance
tolerance
scalar
0 … 1
0.2
Softness
softness
scalar
0 … 1
0.1
Spill Suppression
spill
scalar
0 … 1
0
Invert
invert
boolean
false
Sockets
Direction
Name
Type
input*
image
image
image
outputprimary
out
image
outputaux
matte
mask
Keyer (Skia)
skia-keyer
Skia-layer Keyer: keys pixels transparent by luminance (luma), RGB distance to a key color (color), chroma-plane distance (chroma — green screen) or the nearest of a scrubbed color set (sample), with softness, invert and spill suppression. Same params as Keyer; the matte multiplies alpha, fused into the layer's one runtime shader.
flags
no-mask, gates-outputs
gotcha
threshold applies only in mode=luma; key_color, tolerance and spill only in color/chroma/sample modes.
gotcha
softness feathers symmetrically around threshold (luma) but only outward past tolerance in the other modes.
gotcha
sample mode keys by the nearest of up to 64 sample_colors and despills against their mean; empty, it keys nothing.
gotcha
No matte aux: the matte is the output alpha (source alpha × matte). Unwired, the output is transparent.
Parameters
Name
Type
Default
Mode
mode
enum
"luma"
luma · color · chroma · sample
Threshold
threshold
scalar
0 … 1
0.5
Key Color
key_color
color
"#00ff00"
Tolerance
tolerance
scalar
0 … 1
0.2
Softness
softness
scalar
0 … 1
0.1
Spill Suppression
spill
scalar
0 … 1
0
Invert
invert
boolean
false
Sockets
Direction
Name
Type
input
image
image
skia_shader
outputprimary
out
skia_shader
outputaux
preview
image
Kuwahara
kuwahara
Painterly smoothing: each pixel pools the least-varying sectors of its neighborhood, flattening texture into paint-like facets while edges stay crisp. `Anisotropic` (default) stretches the facets into strokes along the local flow — steered by the `field` input (Image Flow Field, or any velocity field), or by an internal estimate when unwired — and is the temporally stable choice for video. `Generalized` keeps round isotropic facets. `Radius` is the brush size (cost grows with its square); `Sharpness`/`Hardness` set how crisply the winning facet dominates; `Anisotropy` scales how strongly flow coherence elongates strokes, with `Min coherence` forcing elongation where the field reports none (e.g. plain velocity fields).
space
param:radius=pixels
gotcha
radius is in pixels at render resolution and does not scale with output size; the ellipse major half-axis caps at 48px (MAX_EXTENT) regardless of anisotropy.
gotcha
anisotropy and min_coherence only apply in mode=anisotropic; generalized mode uses isotropic sectors and ignores the field entirely.
gotcha
field unwired computes an internal structure-tensor field from the source using the smooth param; smooth is ignored once field is wired.
gotcha
Sector means/variances accumulate in premultiplied color and un-premultiply on write, matching the convolve/boundary.ts convention.
Parameters
Name
Type
Default
Radius
radius
scalar
1 … 32
6
Mode
mode
enum
"anisotropic"
anisotropic · generalized
Sharpness
sharpness
scalar
1 … 16
8
Hardness
hardness
scalar
0.1 … 100
8
Anisotropy
anisotropy
scalar
0 … 2
1
Min coherence
min_coherence
scalar
0 … 1
0
Internal smooth
smooth
scalar
0 … 32
4
Opacity
opacity
scalar
0 … 1
1
Sockets
Direction
Name
Type
input*
image
image
image
input
Field
field
image
outputprimary
out
image
Kuwahara (Skia)
skia-kuwahara
Skia-layer Kuwahara: painterly smoothing that pools the least-varying of 8 sectors around each pixel, fused into the layer's one runtime shader by re-evaluating the upstream at every pixel of the kernel. Same params as Kuwahara. Generalized (round facets) works alone; Anisotropic needs a wired field to stretch strokes along. Radius up to 24 px.
space
param:radius=pixels
flags
no-mask, gates-outputs
gotcha
Re-evaluates the upstream ≈ π·radius² times per pixel (113 at radius 6): over a procedural chain this is the most expensive node in the library.
gotcha
radius caps at 24 px (refused above); anisotropic strokes clip at a 24 px major half-axis, where Kuwahara clips at 48.
gotcha
mode=anisotropic needs the field wired (Kuwahara's internal structure-tensor field is multipass); generalized ignores the field.
gotcha
radius is in canvas pixels (1 / iResolution); on a device it is points. smooth is unused.
Parameters
Name
Type
Default
Radius
radius
scalar
1 … 32
6
Mode
mode
enum
"anisotropic"
anisotropic · generalized
Sharpness
sharpness
scalar
1 … 16
8
Hardness
hardness
scalar
0.1 … 100
8
Anisotropy
anisotropy
scalar
0 … 2
1
Min coherence
min_coherence
scalar
0 … 1
0
Internal smooth
smooth
scalar
0 … 32
4
Opacity
opacity
scalar
0 … 1
1
Sockets
Direction
Name
Type
input
image
image
skia_shader
input
Field
field
skia_shader
outputprimary
out
skia_shader
outputaux
preview
image
Lens Flare
lens-flare
Lens flare: chromatic ghosts + halo ring + anamorphic streak. Outputs the FLARE LAYER ONLY, on opaque black (alpha 1 everywhere) — composite back over the source with a Merge in add or screen mode (black adds nothing) so you can pre-blur / tint between if you want.
ghost_dispersal, chromatic_aberration, halo_width, and streak_length are UV-fraction offsets in raw v_uv space (uv01), not aspect-corrected.
gotcha
Ghosts, halo, and streak all render at half the source resolution before the full-res composite; threshold/soft_knee affect all three via one shared bright-pass buffer.
gotcha
streak is skipped entirely (rendered blank) when streak_strength or streak_length is 0, instead of computing a zero-length blur.
gotcha
num_ghosts is rounded and clamped to 0..8 in JS; the shader loop itself hard-caps at 8 ghosts regardless of the param value.
Parameters
Name
Type
Default
Threshold
threshold
scalar
0 … 2
0.8
Soft Knee
soft_knee
scalar
0 … 1
0.4
Intensity
intensity
scalar
0 … 5
1
Tint
tint
color
"#ffffff"
Ghosts
num_ghosts
scalar
0 … 8
4
Ghost Dispersal
ghost_dispersal
scalar
0 … 1
0.4
Chromatic
chromatic_aberration
scalar
0 … 0.1
0.015
Halo Width
halo_width
scalar
0 … 1
0.5
Halo Intensity
halo_intensity
scalar
0 … 2
1
Streak Strength
streak_strength
scalar
0 … 2
0.6
Streak Length
streak_length
scalar
0 … 1
0.2
Streak Axis
streak_axis
enum
"horizontal"
horizontal · vertical · diagonal
Sockets
Direction
Name
Type
input*
image
image
image
outputprimary
out
image
Lens Flare (Skia)
skia-lens-flare
Lens Flare over the Skia drawing wired into it, with Lens Flare's params: chromatic ghosts, a halo ring and a streak from its soft-knee bright pass, as runtime image filters (React Native Skia <RuntimeShader>). Outputs the FLARE LAYER ONLY on opaque black, like Lens Flare, or with output=over the flare added over the content (Lens Flare → Merge add).
output=flare is the flare alone on opaque black (alpha 1), as Lens Flare writes it; output=over adds it over the content.
gotcha
A Skia Group's blendMode can't add the flare over its source: it reaches the draws inside a filter's layers, not the layer.
gotcha
ghost_dispersal, chromatic_aberration, halo_width and streak_length are uv fractions of the canvas (not aspect-corrected), as in Lens Flare.
gotcha
Runs at full resolution with a 1.3 px blur standing in for Lens Flare's half-res passes; the content is drawn twice (ghosts and streak branches).
gotcha
Runtime image filters: native React Native Skia only (RN web has no RuntimeShader filter).
Parameters
Name
Type
Default
Threshold
threshold
scalar
0 … 2
0.8
Soft Knee
soft_knee
scalar
0 … 1
0.4
Intensity
intensity
scalar
0 … 5
1
Tint
tint
color
"#ffffff"
Ghosts
num_ghosts
scalar
0 … 8
4
Ghost Dispersal
ghost_dispersal
scalar
0 … 1
0.4
Chromatic
chromatic_aberration
scalar
0 … 0.1
0.015
Halo Width
halo_width
scalar
0 … 1
0.5
Halo Intensity
halo_intensity
scalar
0 … 2
1
Streak Strength
streak_strength
scalar
0 … 2
0.6
Streak Length
streak_length
scalar
0 … 1
0.2
Streak Axis
streak_axis
enum
"horizontal"
horizontal · vertical · diagonal
Output
output
enum
"flare"
flare · over
Sockets
Direction
Name
Type
input*
Content
content
skia_draw
outputprimary
out
skia_draw
Line Art
line-art
Extract stylized ink lines (XDoG): a soft-thresholded difference-of-Gaussians that sweeps from clean line art (low `Softness`) through charcoal tone (high). With `Flow` on the measurement runs across the image's flow and integrates along it (FDoG), consolidating broken contours into coherent hand-drawn strokes — visibly calmer on video than plain edge detection. Output is ink on transparency, ready to Merge over a Kuwahara / Flow Bilateral base (the painterly and toon stacks). `Size` is the line scale, `Threshold` how strong an edge must be, `Sharpen` the tone contrast. The optional `paper` mask modulates the threshold per-pixel — wire noise for hatching/charcoal texture. A wired universal mask mattes the ink.
space
param:size,param:flow_length=pixels
gotcha
size (line scale, sigma) and flow_length are pixels at render resolution and do not scale with output size; each Gaussian sums at most 24 taps per side.
gotcha
flow=true runs FDoG (across-flow measurement integrated along the tangent streamline); flow=false runs isotropic XDoG via two separable Gaussian blurs.
gotcha
flow_length and smooth only apply when flow is true; smooth drives the internal structure-tensor field and is ignored once field is wired.
gotcha
paper perturbs the threshold epsilon per-pixel, scaled by paper_strength; unwired paper leaves the threshold unmodified.
Parameters
Name
Type
Default
Flow
flow
boolean
true
Size
size
scalar
0.5 … 8
1.4
Contrast (k)
contrast
scalar
1.2 … 3
1.6
Sharpen
sharpen
scalar
1 … 50
20
Threshold
threshold
scalar
0 … 1
0.5
Softness
softness
scalar
0.01 … 2
0.3
Flow length
flow_length
scalar
0 … 16
4
Paper strength
paper_strength
scalar
0 … 1
0.5
Ink
color
color
"#000000"
Invert
invert
boolean
false
Internal smooth
smooth
scalar
0 … 32
4
Opacity
opacity
scalar
0 … 1
1
Sockets
Direction
Name
Type
input*
image
image
image
input
Field
field
image
input
Paper
paper
mask
outputprimary
out
image
Liquid Glass
liquid-glass
Apple-style refractive liquid glass over a backdrop. Wire the image behind the glass into the backdrop input; a built-in rounded-rect/superellipse panel (optionally smooth-merged with a second shape) refracts, disperses, and glares over it. Wire an SDF into `shape` for any analytic shape — circles, booleans, smooth-union blobs, or splines via SDF Spline.
posX/posY/width/height and bPosX/bPosY/bWidth/bHeight are per-axis UV fractions (uv01): X scales by canvas width, Y independently by height, not aspect-corrected.
gotcha
cornerRadius and bCornerRadius are 0..1 fractions of the shape's own min(halfWidth, halfHeight) in px, not an absolute distance.
gotcha
thickness, strength, blur, mergeRadius, and shapeSmooth are raw pixels at render resolution and do not scale with output size.
gotcha
shapeMode=replace makes a wired shape SDF fully replace the built-in shape(s); shapeMode=merge smooth-unions it in via mergeRadius.
gotcha
Texture-backed SDFs (e.g. SDF From Image) auto-smooth their normal at 3px to avoid a moire/faceted look, unless shapeSmooth overrides it.
gotcha
blurEdge (Frost) blends the whole glass toward the blurred backdrop; off, it blends sharp-to-blurred only from center to edge across thickness.
gotcha
The shader recompiles whenever the wired shape SDF's structural topology changes; animating existing SDF params only rebinds uniforms.
Parameters
Name
Type
Default
Shape
shapeType
enum
"rounded-rect"
rounded-rect · circle
Position X
posX
scalar
0 … 1
0.5
Position Y
posY
scalar
0 … 1
0.5
Width
width
scalar
0 … 1
0.4
Height
height
scalar
0 … 1
0.26
Corner Radius
cornerRadius
scalar
0 … 1
0.6
Roundness (squircle)
roundness
scalar
2 … 12
5
Second Shape
secondShape
boolean
false
Shape B
bShapeType
enum
"circle"
circle · rounded-rect
B Position X
bPosX
scalar
0 … 1
0.66
B Position Y
bPosY
scalar
0 … 1
0.5
B Width
bWidth
scalar
0 … 1
0.16
B Height
bHeight
scalar
0 … 1
0.16
B Corner Radius
bCornerRadius
scalar
0 … 1
1
B Roundness
bRoundness
scalar
2 … 12
5
Merge
mergeRadius
scalar
0 … 400
60
Shape Input
shapeMode
enum
"replace"
replace · merge
Shape Smoothing (px)
shapeSmooth
scalar
0 … 16
0
Edge Thickness
thickness
scalar
1 … 400
80
Refraction (IOR)
ior
scalar
1 … 2.5
1.5
Refraction Strength
strength
scalar
0 … 400
60
Dispersion
dispersion
scalar
0 … 3
0.4
Backdrop Blur (px)
blur
scalar
0 … 60
8
Frost (blur center)
blurEdge
boolean
false
Tint
tint
color
"#ffffff"
Tint Opacity
tintOpacity
scalar
0 … 1
0
Fresnel
fresnelFactor
scalar
0 … 3
1
Fresnel Range
fresnelRange
scalar
1 … 500
120
Fresnel Hardness
fresnelHardness
scalar
-1 … 1
0
Glare
glareFactor
scalar
0 … 2
0.6
Glare Angle
glareAngle
scalar
-180 … 180
45
Glare Range
glareRange
scalar
1 … 500
120
Glare Hardness
glareHardness
scalar
-1 … 1
0
Glare Convergence
glareConvergence
scalar
0 … 1
0.4
Glare Opposite
glareOppositeFactor
scalar
0 … 1
0.5
Opacity
opacity
scalar
0 … 1
1
Sockets
Direction
Name
Type
input*
backdrop
image
image
input
shape (SDF)
shape
sdf
outputprimary
out
image
Merge
mergepolymorphic
Blends a base image with one or more layer images. Every image input carries its own mask input underneath — the matte for that layer (multiplies its per-pixel coverage, like a track matte). In AI recipes: `layers: [{mode, opacity, maskInvert?, name?}, …]` *replaces* the default stack (N entries → N layers, new ids — not appended onto lyr-initial). Later set_param patches keep ids by index so existing wires never dangle. Wire inputs ordinally — layer1, layer2, … (mask1, mask2, … for the mattes) — they resolve to the real per-layer sockets, growing the stack when layerN points past the end.
flags
no-mask
gotcha
mode=mix is a legacy alias identical to normal (same blend formula); it only exists so old saves keep loading.
gotcha
A layer's enabled=false skips it from compositing but keeps its socket, so the wire stays connected without contributing.
gotcha
maskInvert only inverts a layer that has a mask actually wired; on an unmasked layer it is a no-op, not a full blank.
gotcha
A layer's optional name is display-only (socket label, panel card, track name); sockets are still addressed as layer:<id> / mask:<id>, and ordinal aliases layerN / maskN still resolve by position.
gotcha
Composites in straight (non-premultiplied) alpha via source-over; every RGB blend formula (multiply, screen, etc.) operates on straight color.
Skia-layer Merge: blends a base shader with up to 8 layer shaders inside the layer's one runtime shader, with Merge's own formulas (all 29 modes, straight-alpha source-over), per-layer opacity (keyframable, exports as tracks) and per-layer masks. Same `layers` param and sockets as Merge (base, mask:base, layer:<id>, mask:<id>); every socket takes a Skia shader, and a mask reads it as luma × alpha. To blend separate draws (shapes, text, images) use the draw's own Skia blendMode instead.
flags
no-mask, gates-outputs
gotcha
Formulas are Merge's, derived from its BLEND_FS at load (straight-alpha source-over; mix = normal). An unwired base renders opaque black, like Merge.
gotcha
At most 8 enabled layers (fixed shader slots): more is reported, so chain a second Merge (Skia). Wiring the last open layer adds one, as on Merge, up to 8; disabled / unwired layers are skipped.
gotcha
A mask socket takes any Skia shader and mattes by Rec.709 luma × alpha (the engine's image → mask rule); maskInvert only applies while that mask is wired.
gotcha
Layer opacity keyframes (layer_opacity:<id>) export as timeline tracks; the evaluator clamps them to 0..1, so an overshooting curve exports sampled per frame instead.
gotcha
Pick Merge (Skia) for parity with a default-layer Merge and for the 14 modes Skia lacks: subtract, divide, average, vivid/pin/linear light, hard mix, lighter/darker color, linear burn, etc.
gotcha
Pick a draw's own Skia blendMode to blend separate draws (shapes, text, images) natively: W3C math, hue/saturation/color/luminosity, no shader. Skia Fill ignores an enclosing Group's mode.
gotcha
Over an opaque base Skia's native modes equal Merge's except softLight (base ≤ 0.25 under a layer > 0.5, off by ≤ 0.06) and plus (premultiplied clamp; Merge's add clamps straight color).
gotcha
Over a translucent base every native mode but srcOver disagrees broadly: Skia shows the layer unblended where the backdrop is transparent; Merge blends it with the base color regardless.
Dilate (grow) or erode (shrink) the Skia drawing wired into it by a radius in px — React Native Skia <Morphology> on a Group layer: a square (2r + 1)-px window, each channel's max (dilate) or min (erode), radius rounded to whole pixels.
space
out,param:radius=pixels
flags
no-mask
gotcha
The window is a square, not a disc: dilating a dot gives a square, and diagonals grow by √2 × radius.
gotcha
radius rounds to whole pixels (1.4 → 1, 1.5 → 2), after the canvas scale on device.
gotcha
Channels dilate independently (premultiplied): colours of neighbouring shapes mix at the seam.
Parameters
Name
Type
Default
Operation
op
enum
"dilate"
dilate · erode
Radius (px)
radius
scalar
0 … 128
2
Sockets
Direction
Name
Type
input*
Content
content
skia_draw
outputprimary
out
skia_draw
Object Tracker
object-tracker
Detect objects in an incoming image using MediaPipe. Emits bounding-box rectangles (spline) and per-detection centers (points). IDs persist across frames via IoU matching.
space
out,aux:positions=uv01
writes
attr:scale
flags
unstable
gotcha
Box and point coordinates are raw per-axis width/height fractions (uv01), not aspect-corrected canvas01, so geometry skews on non-square canvases.
gotcha
Detection only actually runs at detect_fps; frames in between reuse the last frame's tracked boxes unchanged.
gotcha
IDs persist via greedy IoU matching (threshold 0.3) within the same category only; unmatched previous boxes are dropped and new ones get fresh IDs.
gotcha
Switching model reloads asynchronously: the old model keeps running until the new one is ready, and output is empty while no detector is loaded (including the first-run download).
gotcha
retimeable: false — Time Offset cannot rewind this node; it always passes through the current frame's live detections unshifted.
Parameters
Name
Type
Default
Model
model
enum
"lite0"
lite0 · lite2
Confidence
confidence
scalar
0 … 1
0.4
Max results
max_results
scalar
1 … 25
5
Input size
input_size
scalar
128 … 1024
320
Detect rate (fps)
detect_fps
scalar
1 … 60
15
Sockets
Direction
Name
Type
input*
image
image
image
outputprimary
out
spline
outputaux
positions
points
Pixelate
pixelate
Mosaic the image into blocks, sampling each block's center. Square or circular cells; aspect stretches the grid for non-square mosaics.
space
param:size=pixels
gotcha
size is in pixels of the source texture (u_res), not canvas-relative; the same value looks larger on a lower-resolution render target.
gotcha
aspect stretches only the cell width (u_cell.x = size*aspect); cell height stays size, so aspect only widens/narrows tiles, it does not scale them uniformly.
gotcha
shape=circle sets RGBA to 0 (not just alpha) outside the inscribed disc, so the corners become true transparent-black holes rather than a softened edge.
Parameters
Name
Type
Default
Block size (px)
size
scalar
1 … 256
16
Cell shape
shape
enum
"square"
square · circle
Aspect
aspect
scalar
0.25 … 4
1
Opacity
opacity
scalar
0 … 1
1
Sockets
Direction
Name
Type
input*
image
image
image
outputprimary
out
image
Pixelate (Skia)
skia-pixelate
Skia-layer Pixelate: mosaics the upstream shader into blocks, sampling each block's center, with square or circular cells and an aspect that widens the cells; fused into the layer's one runtime shader. Same params as Pixelate. The `preview` output renders it through CanvasKit.
space
param:size=pixels
flags
no-mask, gates-outputs
gotcha
size is the cell height in canvas pixels (the Skia layer's resolution); aspect multiplies only the cell width.
gotcha
Cells count from the bottom-left corner (v_uv), so a partial row/column lands at the top/right; its center may lie past the edge and reads the edge pixel.
gotcha
One upstream evaluation per pixel at the cell center: an exact point sample, where the GL node bilinearly filters its texture there.
gotcha
shape=circle sets RGBA to 0 outside the inscribed disc (transparent black holes, no soft edge); opacity multiplies alpha.
Parameters
Name
Type
Default
Block size (px)
size
scalar
1 … 256
16
Cell shape
shape
enum
"square"
square · circle
Aspect
aspect
scalar
0.25 … 4
1
Opacity
opacity
scalar
0 … 1
1
Sockets
Direction
Name
Type
input
Image
image
skia_shader
outputprimary
out
skia_shader
outputaux
preview
image
Polar Coords
polar-coords
Map an image between rectangular and polar coordinates. Rect ⇒ Polar wraps the source around the center (kaleidoscope / tunnel looks); Polar ⇒ Rect unrolls a circular source into a strip. Center, rotation, and radial scale set the pivot and orientation.
space
param:center_x,param:center_y=uv01
gotcha
center_x/center_y are per-axis UV fractions (not aspect-corrected); radius = length(v_uv − center) mixes both axes with no aspect term, so circles skew into ellipses on non-square canvases.
gotcha
mode=rect-to-polar reads the source as (angle=x·2π, radius=y) and paints the resulting disc; mode=polar-to-rect is the inverse unroll, not a simple parameter swap.
gotcha
wrap=transparent leaves out-of-range samples as alpha 0; clamp clamps UV to [0,1]; mirror reflects with abs(fract(uv*0.5)*2−1), a full mirror-tile rather than one flip.
gotcha
rotation is in radians and offsets the angle axis directly, independent of scale_radius.
Parameters
Name
Type
Default
Mode
mode
enum
"rect-to-polar"
rect-to-polar · polar-to-rect
Center X
center_x
scalar
-1 … 2
0.5
Center Y
center_y
scalar
-1 … 2
0.5
Rotation (rad)
rotation
scalar
-3.141592653589793 … 3.141592653589793
0
Radius scale
scale_radius
scalar
0 … 4
1
Edge
wrap
enum
"clamp"
transparent · clamp · mirror
Sockets
Direction
Name
Type
input*
image
image
image
outputprimary
out
image
Polar Coords (Skia)
skia-polar-coords
Skia-layer Polar Coords: maps the upstream shader between rectangular and polar coordinates (rect-to-polar wraps it around the center, polar-to-rect unrolls it), fused into the layer's one runtime shader. Same params as Polar Coords. The `preview` output renders it through CanvasKit.
space
param:center_x,param:center_y=uv01
flags
no-mask, gates-outputs
gotcha
center_x/center_y are per-axis uv fractions and the radius has no aspect term, so circles skew into ellipses on non-square canvases (as Polar Coords).
gotcha
mode=rect-to-polar reads the upstream at (angle=x·2π, radius=y); mode=polar-to-rect is the inverse unroll.
gotcha
wrap=transparent leaves out-of-range samples at alpha 0; clamp clamps uv to [0,1]; mirror is abs(fract(uv*0.5)*2−1), which flips even inside [0,1].
gotcha
rotation is radians and offsets the angle axis directly; the upstream is re-evaluated at the mapped point, not read from a filtered texture.
Parameters
Name
Type
Default
Mode
mode
enum
"rect-to-polar"
rect-to-polar · polar-to-rect
Center X
center_x
scalar
-1 … 2
0.5
Center Y
center_y
scalar
-1 … 2
0.5
Rotation (rad)
rotation
scalar
-3.141592653589793 … 3.141592653589793
0
Radius scale
scale_radius
scalar
0 … 4
1
Edge
wrap
enum
"clamp"
transparent · clamp · mirror
Sockets
Direction
Name
Type
input
Image
image
skia_shader
outputprimary
out
skia_shader
outputaux
preview
image
Posterize
posterize
Quantize the image to a small number of tonal levels for a flat, screen-print / cel-shaded look. Per-channel (rgb) or brightness-only (luma, preserves hue).
gotcha
quant() snaps to L evenly spaced values inclusive of both endpoints, in de-gamma'd space (pow(x, 1/gamma)) then re-gamma'd back on output.
gotcha
channels=luma quantizes brightness only and rescales rgb by ql/l to preserve hue; near-black pixels (l<1e-4) rescale to pure black instead of dividing by ~0.
gotcha
levels is floored to at least 2 and gamma to at least 0.001 even if a wired value goes lower.
gotcha
alpha passes through unchanged; channels=rgb quantizes r/g/b independently, which can shift hue near band edges unlike luma mode.
Parameters
Name
Type
Default
Levels
levels
scalar
2 … 32
6
Channels
channels
enum
"rgb"
rgb · luma
Gamma
gamma
scalar
0.2 … 4
1
Opacity
opacity
scalar
0 … 1
1
Sockets
Direction
Name
Type
input*
image
image
image
outputprimary
out
image
Posterize (Skia)
skia-posterize
Skia-layer Posterize: quantizes the upstream shader to `levels` tonal steps per channel (rgb) or by brightness only (luma, hue kept), with a gamma that shifts where the bands land. Same params as Posterize; fuses into the layer's one runtime shader.
flags
no-mask, gates-outputs
gotcha
Snaps to L evenly spaced values including both ends, in de-gamma'd space (pow(x, 1/gamma)), then re-gammas; levels floors at 2 and gamma at 0.001.
gotcha
channels=luma rescales rgb by quantized/original Rec. 709 luma (near-black pixels go black), so hue is kept but rgb may exceed 1.
gotcha
Alpha passes through × opacity (folded into the shader). Unwired, the output is transparent like Posterize's.
Parameters
Name
Type
Default
Levels
levels
scalar
2 … 32
6
Channels
channels
enum
"rgb"
rgb · luma
Gamma
gamma
scalar
0.2 … 4
1
Opacity
opacity
scalar
0 … 1
1
Sockets
Direction
Name
Type
input
image
image
skia_shader
outputprimary
out
skia_shader
outputaux
preview
image
Reaction Diffusion
reaction-diffusionpolymorphic
Gray-Scott / FitzHugh-Nagumo reaction-diffusion patterns. Pick a preset, tune step count. Optional seed image sets the initial U/V fields (R → U, G → V); otherwise starts from a central splat.
reads
time
flags
simulation, unstable
gotcha
Seed image only reads the R channel as brightness × seed_intensity to set V (U = 1−V); it is not a literal R→U, G→V split despite the description.
gotcha
Steps only run while ctx.playing is true; scrubbing or pausing freezes the sim unless drive_by_scene_time is on, which instead steps when the wired time input advances past its last value.
gotcha
Resets (re-seed from the seed image or the central splat) whenever scene time drops back near 0 after being nonzero, i.e. a loop, or on the first eval.
gotcha
resolution (0.1-1) scales the internal sim texture relative to canvas size, floored at 4px per side; output upsamples the V channel to full canvas with linear filtering.
gotcha
preset != custom ignores feed/kill/alpha/beta/gamma entirely; only preset=custom reads those raw model params.
Tone curves — a combined RGB curve plus individual R / G / B curves.
gotcha
The master (combined RGB) curve applies to all three channels first, then each per-channel R/G/B curve is applied on top of that result.
gotcha
Alpha passes through unmodified; only RGB is curved.
gotcha
Each channel's curve bakes to a 256-entry monotone-cubic LUT that is then linearly interpolated per lookup, not evaluated exactly per input value.
Parameters
Name
Type
Default
Curves
curves
curves
(complex)
Sockets
Direction
Name
Type
input*
image
image
image
outputprimary
out
image
RGB Curves (Skia)
skia-rgb-curves
Skia-layer RGB Curves: a master curve on every channel, then the R / G / B curves, fused into the layer's one runtime shader. Same `curves` param as RGB Curves (monotone cubic through the points, up to 16 per curve).
flags
no-mask, gates-outputs
gotcha
Master (combined) curve first, then each channel's own; alpha passes through.
gotcha
Reproduces the GL node's 256-entry 8-bit LUT with linear filtering (two curve evaluations per lookup), not the exact curve.
gotcha
Up to 16 points per curve; more emits transparent and reports it. Unwired, the output is opaque black like RGB Curves'.
Parameters
Name
Type
Default
Curves
curves
curves
(complex)
Sockets
Direction
Name
Type
input
image
image
skia_shader
outputprimary
out
skia_shader
outputaux
preview
image
Segment Anything
segment-anything
Segment images locally via Transformers.js. Dots mode: select the node and click the canvas to drop dots on what you want — each dot adds another object (SlimSAM); outputs a cutout + selection mask. Auto modes need no points and output an evolving multicolor segment map: Semantic (SegFormer classes, inherently stable colors), Panoptic (DETR instances, overlap-matched across frames), Auto Grid (SAM point grid, slow but class-agnostic) — set Levels for the segment count, and use the grayscale id-map aux with a Color Ramp for custom palettes. For animated inputs, Bake runs the model over an in/out frame range and caches a mask per frame; Free Bake clears it.
gotcha
ML inference never runs in compute(): dots run SlimSAM on click, Bake runs it over inFrame..outFrame — compute only picks the cached mask for ctx.frame and uploads it.
gotcha
mode=dots outputs a cutout + selection mask; mode=semantic/panoptic/grid colorize per-slot IDs instead, with `levels` normalizing the id map for Color Ramp.
gotcha
Baked masks are session-only: dots/inFrame/outFrame/feather/threshold save with the project, the decoded bitmaps do not — reopening a project needs one re-bake.
gotcha
Before any dot is placed (or before the first baked frame decodes), the node passes the source through with an all-white mask so downstream wiring isn't blocked.
gotcha
In offline export, an undecoded baked frame blocks via pushMediaSettle for frame accuracy; realtime playback instead shows the previous mask texture for a frame.
gotcha
Index masks upload with NEAREST filtering and no color-space conversion — slot IDs are exact integers, not colors to interpolate.
Parameters
Name
Type
Default
Mode
mode
enum
"dots"
dots · semantic · panoptic · grid
Levels
levels
scalar
2 … 32
6
Feather
feather
scalar
0 … 1
0
Threshold
threshold
scalar
0.1 … 0.9
0.5
Sockets
Direction
Name
Type
input*
image
image
image
outputprimary
out
image
outputaux
mask
mask
outputaux
mask_image
image
Sharpen
sharpen
Sharpen an image with one of four kernel shapes. Box/diamond/cross are Laplacian-style (crisp); unsharp uses a Gaussian reference for softer falloff.
gotcha
All four algorithms are single-pass 3×3 taps at the native pixel grid (u_invRes = 1/width, 1/height) — they do not scale with a wired kernel or radius param.
gotcha
algorithm=box/diamond/cross are Laplacian sharpens (amount scales neighbor subtraction); unsharp uses Gaussian-weighted 3×3 weights instead, for softer falloff and less ringing.
gotcha
algorithm=cross taps only the four diagonal neighbors, so it sharpens diagonal edges while leaving axis-aligned edges untouched.
gotcha
Output RGB is not clamped and can overshoot [0,1] on high-contrast edges; alpha always passes through untouched.
Parameters
Name
Type
Default
Algorithm
algorithm
enum
"box"
box · diamond · cross · unsharp
Amount
amount
scalar
0 … 5
1
Sockets
Direction
Name
Type
input*
image
image
image
outputprimary
out
image
Sharpen (Skia)
skia-sharpen
Skia-layer Sharpen: a 3×3 sharpen of the upstream shader (box, diamond, cross or unsharp kernel), fused into the layer's one runtime shader by re-evaluating the upstream at the eight neighbor pixels. Same params as Sharpen.
flags
no-mask, gates-outputs
gotcha
Taps are one canvas pixel apart (1 / iResolution); on a device that is one point, so the kernel widens with pixel density.
gotcha
Up to 9 evaluations of the upstream per pixel (box and cross 5): cheap over an image child, 9× the cost of a procedural chain fused above it.
gotcha
Output RGB is not clamped and can overshoot [0,1] on edges; alpha passes through.
gotcha
Edge pixels repeat the outermost row / column, as the GL node's clamped texture does.
Parameters
Name
Type
Default
Algorithm
algorithm
enum
"box"
box · diamond · cross · unsharp
Amount
amount
scalar
0 … 5
1
Sockets
Direction
Name
Type
input
image
image
skia_shader
outputprimary
out
skia_shader
outputaux
preview
image
SkSL Filter (Skia)
skia-sksl-filterpolymorphic
An SkSL program over the Skia drawing wired into it — React Native Skia's <RuntimeShader> image filter on a Group layer. Write helpers plus `half4 main(float2 fragCoord)`; sample the input with content.eval(xy) (premultiplied sRGB), fragCoord is px Y-DOWN, iResolution.xy the canvas size, iTime seconds; return premultiplied sRGB. Tunables are // ch("k", 0.5, 0, 1) / color() / toggle() / pick() comments synced into channels — keyframe them or wire Animated Value / Color in, as on SkSL Shader. Colors are sRGB numbers on every canvas (the node converts for Display P3).
reads
time
flags
no-mask
gotcha
content.eval is the drawing below, premultiplied; fragCoord is the filter layer's local px, Y-DOWN (canvas px at the scene root).
gotcha
Return premultiplied colour (rgb * a), as on device.
gotcha
A runtime image filter: native React Native Skia only — RN web has no RuntimeShader filter, so a web export doesn't play it.
gotcha
Sampling content far from fragCoord reads transparent past the drawn area; Skia doesn't promise content outside the layer.
gotcha
ramp() and curve() channels are refused: their lookup tables would be extra child shaders, and an RN RuntimeShader filter binds only content.
"// SkSL filter over the drawing wired into this node — the same program\n// React Native Skia runs as a <RuntimeShader> image filter.\n// content.eval(xy) is that drawing, PREMULTIPLIED sRGB; fragCoord is px,\n// Y-DOWN; iResolution.xy = canvas size, iTime = seconds. Return\n// PREMULTIPLIED sRGB. Tunables are comments Sync turns into controls:\n// ch → float, color → float4, toggle → int 0/1, pick → int + consts.\n// ch(\"amount\", 0.6, 0, 1)\n// color(\"tint\", \"#ffb070\")\n\nhalf4 main(float2 fragCoord) {\n float4 c = content.eval(fragCoord);\n float2 uv = fragCoord / iResolution.xy;\n float vignette = 1.0 - amount * smoothstep(0.25, 0.75, length(uv - 0.5));\n float3 rgb = mix(c.rgb, c.rgb * tint.rgb, 0.5 * amount) * vignette;\n return half4(float4(rgb, c.a));\n}"
Sockets
Direction
Name
Type
input*
Content
content
skia_draw
input
amount
in:ein-pvjltl
scalar
input
tint
in:ein-fxv309
vec4
outputprimary
out
skia_draw
Stipple
stipple
Re-renders the input as a field of dots whose size and density follow input darkness. Grid (jittered halftone-ish), Screen (stochastic), Packed (organic relaxed-Poisson), and Packed-Flow (persistent dots with fade-in/out + position lerp for animated inputs) modes share the same edge-noise and colorization layer. Dots are either a flat color or sample the image's color at each dot center (pointillism color transfer); the optional `color` input substitutes a different image for that sampling.
space
param:maxMatchDistance=uv01
gotcha
dotMinSize/dotMaxSize scale radius as a fraction of the grid cell's pixel size (mix(min,max,density) × cellPx × 0.5), not absolute pixels.
gotcha
mode=grid rejects a cell below `threshold` density; mode=screen instead accepts each cell stochastically with probability density^densityCurve.
gotcha
Packed relax only runs on the GPU (WebGPU compute) when pointCount ≥ 6000 and relaxIterations > 0; otherwise, or on WebGPU failure, it runs on the CPU.
gotcha
relaxIterations=0 skips relaxation entirely: packed/packed-flow show the raw density-weighted scatter with no Poisson-like spacing.
gotcha
Only packed-flow keeps dot identity across rebakes, matching new targets to old dots within maxMatchDistance; unmatched dots fade out, new ones fade in.
gotcha
colorSource=image samples the `color` input at each dot's center when wired, else falls back to sampling the main image input.
gotcha
GPU relax is asynchronous: the first frame after a change shows an unrelaxed seed set; offline export waits for the settled result before capturing.
gotcha
samplerResolution sets the density field's downsample size as a multiple of the grid cell count, trading bake speed for input detail.
Parameters
Name
Type
Default
Mode
mode
enum
"grid"
grid · screen · packed · packed-flow
Grid Density
gridSize
scalar
10 … 500
120
Density Curve
densityCurve
scalar
0.1 … 5
1
Invert
invert
boolean
false
Threshold
threshold
scalar
0 … 1
0.02
Point Count
pointCount
scalar
100 … 20000
4000
Relax Iterations
relaxIterations
scalar
0 … 20
4
Relax Strength
relaxStrength
scalar
0 … 3
1
Seed
packedSeed
scalar
0 … 9999
1
Sampler Resolution
samplerResolution
scalar
0.5 … 8
2
Fade In (s)
fadeInTime
scalar
0.01 … 5
0.4
Fade Out (s)
fadeOutTime
scalar
0.01 … 5
0.6
Flow Speed
flowSpeed
scalar
0.1 … 30
4
Max Match Dist
maxMatchDistance
scalar
0 … 1
0.08
Dot Min Size
dotMinSize
scalar
0 … 3
0.1
Dot Max Size
dotMaxSize
scalar
0 … 3
1.4
Jitter Min
jitterMin
scalar
0 … 1
0
Jitter Max
jitterMax
scalar
0 … 1
0.6
Jitter Bias
jitterBias
scalar
0.1 … 5
1
Edge Noise Scale
noiseScale
scalar
0.1 … 40
6
Edge Roughness
noiseRoughness
scalar
0 … 1
0.4
Edge Strength
noiseStrength
scalar
0 … 1
0.18
Color Source
colorSource
enum
"flat"
flat · image
Dot Color
dotColor
color
"#4ca134"
Background
backgroundColor
color
"#ece5d0"
Background Alpha
backgroundAlpha
scalar
0 … 1
1
Sockets
Direction
Name
Type
input*
image
image
image
input
color
color
image
outputprimary
out
image
Swift-Hohenberg
swift-hohenbergpolymorphic
Swift–Hohenberg pattern formation (labyrinthine stripes, hexagons, spots). Pick a preset, tune wavelength; optional seed image sets the initial signed field.
space
param:wavelength=canvas01
reads
time
flags
simulation, unstable
gotcha
wavelength is the pattern period as a canvas-width fraction; the sim grid is sized so one period is ~8 texels at resolution=0.5 (resolution scales texels-per-wave, not canvas size).
gotcha
Steps run while ctx.playing or (ctx.offline and time advanced); pause/scrub freezes unless drive_by_scene_time, which steps when the wired time input increases.
gotcha
Resets the field (seed image or hash noise) on first eval or when scene time wraps back near 0.
Seed image stores (luma-0.5)*2*seed_intensity as the signed field; unwired, init is hash noise in ±seed_intensity.
gotcha
Output is 0.5+0.5*tanh(u*contrast) of the signed field; quadratic=0 gives stripes/rolls, nonzero quadratic breaks ±u symmetry into hexagons/spots.
gotcha
periodic=true (default) sets REPEAT wrap so patterns tile; periodic=false clamps and tends to pin rolls to the frame edges.
Parameters
Name
Type
Default
Preset
preset
enum
"hexagons"
labyrinth · hexagons · spots · custom
Epsilon
epsilon
scalar
0 … 1.5
0.3
Quadratic
quadratic
scalar
-2 … 2
0.35
Cubic
cubic
scalar
0 … 4
1
Time step
dt
scalar
0.001 … 0.2
0.03
Wavelength
wavelength
scalar
0.01 … 0.25
0.05
Steps / frame
steps_per_frame
scalar
1 … 80
16
Resolution
resolution
scalar
0.1 … 1
0.5
Seed intensity
seed_intensity
scalar
0 … 1
0.15
Init seed
init_seed
scalar
0 … 1000
0
Contrast
contrast
scalar
0.05 … 2
1
Periodic
periodic
boolean
true
Drive by scene time
drive_by_scene_time
boolean
false
Sockets
Direction
Name
Type
input
seed
seed
image
outputprimary
out
image
Threshold
threshold
Binary luminance threshold — pixels brighter than the threshold become white, darker become black. Softness widens the transition into a feathered smoothstep edge; Invert swaps the two sides. Uses Rec. 709 luma weighting.
gotcha
softness is the full width of the smoothstep transition window, centered on threshold (threshold ± softness/2), not a multiplier on it.
gotcha
Output alpha is passed through unchanged from the source; only RGB is replaced by the grayscale threshold result (t,t,t).
gotcha
Luminance is Rec. 709 luma of the source RGB, ignoring source alpha.
Parameters
Name
Type
Default
Threshold
threshold
scalar
0 … 1
0.5
Softness
softness
scalar
0 … 1
0
Invert
invert
boolean
false
Sockets
Direction
Name
Type
input*
image
image
image
outputprimary
out
image
Threshold (Skia)
skia-threshold
Skia-layer Threshold: pixels whose Rec. 709 luminance is above the threshold turn white, the rest black; softness widens the cut into a centred smoothstep, invert swaps the sides. Same params as Threshold; fuses into the layer's one runtime shader.
flags
no-mask, gates-outputs
gotcha
softness is the full width of the smoothstep window, centred on threshold (threshold ± softness/2); 0 is a hard step.
gotcha
RGB becomes the grey result (t, t, t); the source alpha passes through, so transparent pixels stay transparent.
gotcha
Luma is Rec. 709 of the straight-alpha source RGB. Unwired, the output is transparent like Threshold's.
Parameters
Name
Type
Default
Threshold
threshold
scalar
0 … 1
0.5
Softness
softness
scalar
0 … 1
0
Invert
invert
boolean
false
Sockets
Direction
Name
Type
input
image
image
skia_shader
outputprimary
out
skia_shader
outputaux
preview
image
Trails
trailspolymorphic
Temporal trails — each echo is a faded copy of an earlier frame (opacity only, no color mix). Primary is the current frame over the echoes; the trail aux is echoes only. Feedback: continuous motion trail. Ring: stepped stop-motion echoes. Velocity: directional motion blur along a vector or UV field. History advances only while the timeline plays, and wipes on skip-to-start, loop wrap, or hitting Play.
space
param:velocity_x,param:velocity_y=uv01
reads
time
flags
simulation, unstable
gotcha
History only advances while the timeline is playing or exporting; paused param tweaks or repeated same-time evals freeze it instead of decaying further.
gotcha
The history buffer wipes on skip-to-start / scene-time wrap back near 0, and on every Play (resume from pause); pause itself does not wipe.
gotcha
mode=ring captures a new echo only every step_frames active frames; between captures the primary output freezes on the last captured composite.
gotcha
mode=velocity always re-captures the raw current input as history (not the blurred output), so the directional blur doesn't compound frame over frame.
gotcha
velocity_x/y are the full backward-sweep UV offset at the last tap, not a per-tap or per-frame step; wiring vel_uv overrides them entirely.
gotcha
vel_uv decodes its RG channels from [0,1] into a [-1,1] per-pixel UV vector, used as the local velocity in place of velocity_x/y.
gotcha
decay compounds per captured frame in feedback/ring (echo alpha keeps fading each capture) but falls off per tap within one eval in velocity mode.
gotcha
The trail aux is echoes only, with no current frame composited on top, and is only rendered when actually wired downstream.
Parameters
Name
Type
Default
Mode
mode
enum
"feedback"
feedback · ring · velocity
Decay
decay
scalar
0 … 1
0.92
Step (frames)
step_frames
scalar
1 … 30
2
Velocity X
velocity_x
scalar
-0.5 … 0.5
0
Velocity Y
velocity_y
scalar
-0.5 … 0.5
0
Taps
taps
scalar
1 … 64
8
Sockets
Direction
Name
Type
input*
image
image
image
outputprimary
out
image
outputaux
trail
image
Vector Field
vector-fieldpolymorphic
Turn an image or SDF into a signed-RG velocity field (the encoding Advect Points / Advect Image / Displace / Point Expression already read). Attract pulls toward the feature (inward on an SDF, toward bright on an image); repel pushes away; orbit circulates; isoline follows level sets at unit speed. `radius` is falloff-from-surface on an SDF, and the finite-difference tap on an image (blur a mask upstream for a volumetric well — a hard edge only has gradient at the rim). Chain an upstream field through the optional `field` input to sum flows. Don't matte a field image — matte the consumer.
space
in:source=raster|canvas01 param:radius=canvas01
flags
no-mask
gotcha
radius/falloff is a canvas01 width-relative, aspect-corrected distance in both modes; in SDF mode only, turning off aspect_correct makes it a uv01 per-axis fraction instead.
gotcha
mode=isoline uses a hard band (metric < radius) instead of the smoothstep falloff the other three modes use.
gotcha
field additively sums an already RG-encoded velocity field; chain upstream fields here instead of matting the output image.
gotcha
On an image source, radius also sets the finite-difference tap, so a hard binary mask only has gradient at its rim — blur it upstream for a volumetric well.
gotcha
Attract pulls inward on an SDF (−∇d) but toward brighter pixels on an image (+∇luma); repel/orbit rotate or invert that same gradient.
Parameters
Name
Type
Default
Mode
mode
enum
"attract"
attract · repel · orbit · isoline
Strength
strength
scalar
-2 … 2
0.5
Radius
radius
scalar
0.001 … 1
0.15
Invert
invert
boolean
false
Aspect Correct
aspect_correct
boolean
true
Sockets
Direction
Name
Type
input
Source
source
image
input
Field (add)
field
image
outputprimary
out
image
Vector Field (Skia)
skia-vector-field
Skia-layer Vector Field (image source): turns the upstream shader's luminance gradient into a signed-RG velocity field — attract (toward bright), repel, orbit or isoline — fused into the layer's one runtime shader. Same params as Vector Field; chain another field into `field` to sum flows. The SDF-source branch is not part of this node.
space
param:radius=canvas01
flags
no-mask, gates-outputs
gotcha
Image source only: radius is the finite-difference tap (x in width fractions, y × aspect), so a hard mask only has gradient at its rim.
gotcha
Four evaluations of the source per pixel, `radius` apart; the field input is read once at the pixel.
gotcha
Unwired source: the field passes through unchanged (or the neutral 0.5, 0.5, 0, 1); aspect_correct belongs to the SDF branch and is ignored.
gotcha
Output is the velocity encoding (R/G = 0.5 + v/2, Y-down, B 0, A 1): don't matte it — matte the consumer.
Parameters
Name
Type
Default
Mode
mode
enum
"attract"
attract · repel · orbit · isoline
Strength
strength
scalar
-2 … 2
0.5
Radius
radius
scalar
0.001 … 1
0.15
Invert
invert
boolean
false
Aspect Correct
aspect_correct
boolean
true
Sockets
Direction
Name
Type
input
Source
source
skia_shader
input
Field (add)
field
skia_shader
outputprimary
out
skia_shader
outputaux
preview
image
Utilities
Frame
frame
Wraps an image in a fixed-size element for Auto Layout. Width and height are in layout units (1/1000 of the canvas's smaller dimension); fit controls how the image fills the rect.
flags
no-mask
gotcha
width/height are layout units: 1 unit = 1/1000 of min(canvas width, canvas height) in px, isotropic on both axes (unlike canvas01's width-only scaling).
gotcha
With no image wired, both the element primary and the aux image fall back to an empty zero-size element instead of erroring.
gotcha
preferredSizing is hug on both axes, so a layout slot set to hug takes exactly the authored width x height; fit only matters once the slot differs from that size.
Parameters
Name
Type
Default
Width (units)
width
scalar
1 … 4000
300
Height (units)
height
scalar
1 … 4000
300
Fit
fit
enum
"cover"
cover · contain · stretch
Sockets
Direction
Name
Type
input*
image
image
image
outputprimary
out
element
outputaux
image
image
Image Trace
image-trace
Trace a raster into splines. Potrace fits a binary silhouette; VTracer (WASM) clusters color regions. Fill color is stamped on each subpath as color + driver.
mode=potrace traces a binary silhouette (darker-than-threshold, after invert); mode=vtracer clusters color (clustering=color) or thresholds (clustering=bw).
gotcha
max_size, turdsize, and filter_speckle are in the downsampled trace pixel grid, not canvas pixels, so raising output resolution does not add detail unless max_size is raised too.
gotcha
Until the chosen WASM backend finishes loading, the node emits an empty spline and re-evaluates via a pipeline-bump once ready; offline export waits on the in-flight trace.
gotcha
attrs.color is 0–1 RGB from the SVG fill; driver is Rec.709 luminance of that fill. Cutout holes become separate subpaths (evenodd is not preserved on the spline wire).
Parameters
Name
Type
Default
Engine
mode
enum
"potrace"
Potrace · VTracer
Max size (px)
max_size
scalar
32 … 2048
512
Invert
invert
boolean
false
Threshold
threshold
scalar
0 … 1
0.5
Suppress specks
turdsize
scalar
0 … 100
2
Corner (α max)
alphamax
scalar
0 … 1.334
1
Curve tolerance
opttolerance
scalar
0 … 1
0.2
Turn policy
turnpolicy
enum
"minority"
minority · majority · black · white · left · right