Scripting API
The node library as functions inside the JavaScript node. Generate, filter, and blend images in code.
Inside a JavaScript node, magerie.generators and magerie.ops put the node library at your fingertips as plain functions. Anything you can wire up as nodes, you can also write as a line of code:
//@output image out
let { noise } = magerie.generators
let { mul, blur } = magerie.ops
let a = noise("voronoi", 234234)
let b = noise("simplex", { scale: 2.4 })
out.value = blur(mul(a, b), 4)Results are images. Pass them to other ops, assign one to an image output to feed the graph, or ctx.drawImage(img, x, y) it inside a draw recipe. The editor autocompletes every function with its full signature and options.
Calling ops#
Every function follows one convention:
- Images first. Filters take one (
blur(img, ...)), blends and warps take two (mul(a, b)). - A kind string, when the op has modes:
noise("voronoi"),blend(a, b, "screen"),invert(img, "g"). - A primary number: the seed for noise, radius for blur, opacity for blends, gamma for levels.
- An options object for everything else:
noise("voronoi", 7, { scale: 4, octaves: 6 }). Options are validated: unknown names and bad enum values throw with the valid choices listed, numbers clamp to their slider ranges.
Ops with a -> { ... } in their signature produce several images at once. Destructure the result: let { r, g, b } = channelSplit(img). Each member is a normal image you can pass along or dispose() independently.
Calls are cached per node: the same call with the same inputs reuses its texture instead of re-rendering, so calling ops inside a per-frame draw recipe is free as long as the parameters hold still. Everything a script creates is freed automatically the next time the node runs.
Errors land on the node itself: a typo like noise({ sclae: 2 }) shows the message on the node's error strip when you press Run.
magerie.generators#
noise(type?, seed?, opts?)#
Generates procedural noise textures using various algorithms
| option | type | default | range / options | notes |
|---|---|---|---|---|
type |
select | "simplex" | perlin, simplex, voronoi, white | Noise algorithm to use |
scale |
slider | 8 | 0.1..100 | X-axis frequency (higher = more detail). Y uses the same value unless Scale Y is set. |
scaleY |
slider | 0.1..100 | Y-axis frequency. When equal to Scale the noise is isotropic; set higher than Scale for vertical streaks (grass blades, wood grain), lower for horizontal stripes (ripples, sediment). | |
aspect |
select | "square" | square, stretch | Square: same proportions at any output size. Stretch: follows the output's shape. |
voronoiMode |
select | "f1" | f1, f2, f2-f1, cell | Voronoi sub-mode: F1 = distance to nearest cell point (default). F2 = distance to second-nearest. F2-F1 = the gap; dark at cell boundaries (the 'cracks' pattern for ice / concrete). Cell = the nearest cell's own value, giving flat facets for scales, tiles and stone slabs. |
fractal |
select | "standard" | standard, ridged, billow | Per-octave shaping. Standard is plain fBm. Ridged folds each octave at its midpoint and squares it - veins, creases, fibres, cracked rock. Billow takes the absolute fold - puffed lobes for clouds, blotches, rust. Folding per octave is what makes these read as their own families; a downstream Curves node cannot reproduce it. |
offsetX |
number | 0 | Horizontal offset (driven by Time node for animation) | |
offsetY |
number | 0 | Vertical offset (driven by Time node for animation) | |
octaves |
slider | 4 | 1..8 | Number of noise layers for fractal detail |
persistence |
slider | 0.5 | 0..1 | Amplitude reduction per octave |
warp |
slider | 0 | 0..2 | Domain warp strength. Displaces each sample by a second noise field before evaluating, turning round blobs into torn, capillary shapes. 0 is off. The single biggest lever for grunge. |
lacunarity |
slider | 2 | 1.1..4 | Frequency multiplier per octave. 2 is the classic value. Below ~1.7 the octaves overlap into soft cloud; above ~2.5 they separate into distinct scales. |
seed |
number | 0 | Random seed for reproducibility | |
width |
stepped | auto | 1..8192 | Output width in pixels (Auto uses document default) |
height |
stepped | auto | 1..8192 | Output height in pixels (Auto uses document default) |
gradient(type?, opts?)#
Generates color gradients in various styles
| option | type | default | range / options | notes |
|---|---|---|---|---|
type |
select | "linear" | linear, radial, angular | Gradient style |
colors |
gradient | stops | Start and end colors of the gradient (multi-stop coming in a future release) | |
angle |
slider | 0 | 0..360 | Gradient angle in degrees (for linear) |
centerX |
slider | 0..1 | Horizontal center position (for radial/angular) | |
centerY |
slider | 0..1 | Vertical center position (for radial/angular) | |
width |
number | 1024 | 1..8192 | Output width in pixels |
height |
number | 1024 | 1..8192 | Output height in pixels |
solidColor(color?, opts?)#
Generates a solid color fill
| option | type | default | range / options | notes |
|---|---|---|---|---|
color |
color | "#808080" | Fill color | |
width |
number | 1024 | 1..8192 | Output width in pixels |
height |
number | 1024 | 1..8192 | Output height in pixels |
sdfShape(shape?, size?, opts?)#
Generates signed distance field shapes with configurable geometry and colors as a colored image
| option | type | default | range / options | notes |
|---|---|---|---|---|
shape |
select | "circle" | circle, box, roundedBox, ellipse, capsule, trapezoid, parallelogram, equilateralTriangle, pentagon, hexagon, octagon, pie, arc, ring, heart, cross, moon, egg, vesica | Shape type to generate |
size |
slider | 1.5 | 0.01..2 | Overall scale of the shape |
rotation |
slider | 0 | 0..360 | Rotation in degrees |
offsetX |
number | 0 | -1..1 | Horizontal offset |
offsetY |
number | 0 | -1..1 | Vertical offset |
shapeWidth |
slider | 0.5 | 0.1..1 | Width for rectangular shapes |
shapeHeight |
slider | 0.5 | 0.1..1 | Height for rectangular shapes |
capsuleLength |
slider | 0.6 | 0.1..1.5 | Total length of the capsule |
capsuleThickness |
slider | 0.3 | 0.05..1 | Thickness (diameter) of the capsule |
cornerRadius |
slider | 0.05 | 0..0.5 | Corner radius for rounded shapes |
angle |
slider | 270 | 0..360 | Angle for pie/arc shapes (degrees) |
thickness |
slider | 0.05 | 0.01..0.5 | Thickness for arc/ring shapes |
eggHeight |
slider | 0.45 | 0.1..2 | Height of the egg |
eggBottomRadius |
slider | 0.3 | 0.05..1 | Width at the bottom of the egg |
eggTopRadius |
slider | 0.1 | 0.01..0.5 | Width at the top of the egg (smaller = pointier) |
eggBulge |
slider | 0.75 | 0.1..2 | Controls the curve smoothness |
moonDistance |
slider | 0.2 | 0..2 | Distance between the two circles |
moonOuterRadius |
slider | 0.45 | 0.1..1.5 | Radius of the outer circle |
moonInnerRadius |
slider | 0.32 | 0.1..1.5 | Radius of the inner (cut-out) circle |
vesicaWidth |
slider | 0.5 | 0.1..2 | Width of the vesica |
vesicaHeight |
slider | 0.8 | 0.1..2 | Height of the vesica |
fillColor |
color | "#ffffff" | Color inside the shape | |
edgeColor |
color | "#ff6b6b" | Color at the shape edge | |
edgeBand |
slider | 0 | 0..0.1 | Width of colored edge band (0 = no edge color) |
backgroundColor |
color | "#000000" | Color outside the shape | |
transparentBackground |
checkbox | false | Force the background fully transparent (alpha 0) - cut the shape out for compositing. | |
width |
stepped | auto | 1..8192 | Output width in pixels (Auto uses document default) |
height |
stepped | auto | 1..8192 | Output height in pixels (Auto uses document default) |
pattern(type?, scale?, opts?)#
Generates procedural patterns like checkerboard, stripes, dots, and grid
| option | type | default | range / options | notes |
|---|---|---|---|---|
type |
select | "checkerboard" | checkerboard, stripes, dots, grid | Pattern type to generate |
scale |
slider | 8 | 1..32 | Pattern scale / repeat count |
angle |
slider | 0 | 0..360 | Rotation in degrees |
thickness |
slider | 0.5 | 0.01..0.99 | Line or dot thickness (0-1) |
colorA |
color | "#ffffff" | First color | |
colorB |
color | "#000000" | Second color | |
width |
stepped | auto | 1..8192 | Output width in pixels (Auto uses document default) |
height |
stepped | auto | 1..8192 | Output height in pixels (Auto uses document default) |
scatter(stamp, layout?, count?, opts?) -> { image, id }#
Stamps copies of an input image across the output (random or grid layout), with per-instance random scale / rotation / opacity. Also emits a per-instance ID map for downstream variation.
| option | type | default | range / options | notes |
|---|---|---|---|---|
layout |
select | "random" | random, grid | Placement strategy. Grid = SD Tile Sampler style; Random = legacy organic scatter. |
count |
slider | 20 | 1..200 | Number of stamps (random layout). Grid layout uses Grid X x Grid Y instead. |
gridX |
slider | 8 | 1..64 | Grid columns (grid layout). |
gridY |
slider | 8 | 1..64 | Grid rows (grid layout). |
positionJitter |
slider | 0.5 | 0..1 | Per-cell offset as a fraction of cell size. 0 = aligned grid; 1 = up to ±half-cell. |
seed |
number | 42 | 0..99999 | Random seed for reproducibility |
scaleMin |
slider | 0.5 | 0.1..2 | Minimum random scale |
scaleMax |
slider | 1 | 0.1..2 | Maximum random scale |
rotationRange |
slider | 360 | 0..360 | Max random rotation in degrees |
opacityMin |
slider | 0.5 | 0..1 | Minimum random opacity |
opacityMax |
slider | 1 | 0..1 | Maximum random opacity |
bgColor |
color | "#00000000" | Background fill color | |
width |
stepped | auto | 1..8192 | Output width in pixels (Auto uses document default) |
height |
stepped | auto | 1..8192 | Output height in pixels (Auto uses document default) |
magerie.ops#
blend(a, b, mode?, opacity?, opts?)#
Combines two images using various blend modes
| option | type | default | range / options | notes |
|---|---|---|---|---|
mode |
select | "normal" | normal, multiply, screen, overlay, softLight, hardLight, add, darken, lighten, colorDodge, colorBurn, linearBurn, vividLight, linearLight, pinLight, difference, exclusion, subtract, hue, saturation, color, luminosity | Blend mode |
opacity |
slider | 1 | 0..1 | Blend opacity (0 to 1) |
mul(a, b, opacity?, opts?)#
Blend a and b with mode "multiply" (fixed: mode="multiply")
| option | type | default | range / options | notes |
|---|---|---|---|---|
opacity |
slider | 1 | 0..1 | Blend opacity (0 to 1) |
add(a, b, opacity?, opts?)#
Blend a and b with mode "add" (fixed: mode="add")
| option | type | default | range / options | notes |
|---|---|---|---|---|
opacity |
slider | 1 | 0..1 | Blend opacity (0 to 1) |
sub(a, b, opacity?, opts?)#
Blend a and b with mode "subtract" (fixed: mode="subtract")
| option | type | default | range / options | notes |
|---|---|---|---|---|
opacity |
slider | 1 | 0..1 | Blend opacity (0 to 1) |
screen(a, b, opacity?, opts?)#
Blend a and b with mode "screen" (fixed: mode="screen")
| option | type | default | range / options | notes |
|---|---|---|---|---|
opacity |
slider | 1 | 0..1 | Blend opacity (0 to 1) |
overlay(a, b, opacity?, opts?)#
Blend a and b with mode "overlay" (fixed: mode="overlay")
| option | type | default | range / options | notes |
|---|---|---|---|---|
opacity |
slider | 1 | 0..1 | Blend opacity (0 to 1) |
darken(a, b, opacity?, opts?)#
Blend a and b with mode "darken" (fixed: mode="darken")
| option | type | default | range / options | notes |
|---|---|---|---|---|
opacity |
slider | 1 | 0..1 | Blend opacity (0 to 1) |
lighten(a, b, opacity?, opts?)#
Blend a and b with mode "lighten" (fixed: mode="lighten")
| option | type | default | range / options | notes |
|---|---|---|---|---|
opacity |
slider | 1 | 0..1 | Blend opacity (0 to 1) |
difference(a, b, opacity?, opts?)#
Blend a and b with mode "difference" (fixed: mode="difference")
| option | type | default | range / options | notes |
|---|---|---|---|---|
opacity |
slider | 1 | 0..1 | Blend opacity (0 to 1) |
blur(img, radius?, opts?)#
Applies Gaussian blur to soften an image
| option | type | default | range / options | notes |
|---|---|---|---|---|
radius |
slider | 5 | 0..100 (unbounded above) | Blur radius in pixels |
quality |
select | "medium" | low, medium, high | No longer used: the blur kernel is separable and always runs at full quality |
levels(img, gamma?, opts?)#
Adjusts input/output black and white points with gamma correction
| option | type | default | range / options | notes |
|---|---|---|---|---|
inputBlack |
slider | 0 | 0..1 | Input black point (0-1) |
inputWhite |
slider | 1 | 0..1 | Input white point (0-1) |
gamma |
slider | 1 | 0.1..10 | Gamma correction (0.1-10) |
outputBlack |
slider | 0 | 0..1 | Output black point (0-1) |
outputWhite |
slider | 1 | 0..1 | Output white point (0-1) |
colorAdjust(img, opts?)#
Adjusts brightness and contrast (legacy hue/saturation in the collapsed group; prefer HSL Adjust for those)
| option | type | default | range / options | notes |
|---|---|---|---|---|
brightness |
slider | 0 | -1..1 | Brightness adjustment (-1 to 1) |
contrast |
slider | 1 | 0..2 | Contrast multiplier (0 to 2) |
saturation |
slider | 1 | 0..2 | Saturation multiplier (0 to 2). Prefer HSL Adjust for hue/saturation work. |
hue |
slider | 0 | -180..180 | Hue rotation in degrees (-180 to 180). Prefer HSL Adjust for hue/saturation work. |
sharpen(img, amount?, opts?)#
Enhances edge detail using unsharp mask
| option | type | default | range / options | notes |
|---|---|---|---|---|
amount |
slider | 0.5 | 0..2 | Sharpening strength (0 to 2) |
radius |
slider | 1 | 0.1..16 | Sharpening radius in pixels - the width of the detail band it enhances |
transform(img, opts?)#
Applies geometric transformations to an image
| option | type | default | range / options | notes |
|---|---|---|---|---|
translateX |
number | 0 | Horizontal offset in pixels | |
translateY |
number | 0 | Vertical offset in pixels | |
scale |
slider | 1 | 0.1..4 | Scale factor (1.0 = 100%) |
rotation |
slider | 0 | -180..180 | Rotation angle in degrees |
flipX |
checkbox | false | Flip horizontally | |
flipY |
checkbox | false | Flip vertically |
invert(img, channels?, opts?)#
Inverts image colors to create a negative
| option | type | default | range / options | notes |
|---|---|---|---|---|
channels |
select | "rgb" | rgb, r, g, b | Which color channels to invert |
threshold(img, threshold?, opts?)#
Converts image to black and white based on brightness threshold
| option | type | default | range / options | notes |
|---|---|---|---|---|
threshold |
slider | 0.5 | 0..1 | Brightness cutoff (0-1) |
smooth |
checkbox | false | Enable anti-aliased transition | |
smoothness |
slider | 0.02 | 0..0.2 | Width of smooth transition zone |
gradientMap(img, opts?)#
Remaps image luminance to a user-defined color gradient
| option | type | default | range / options | notes |
|---|---|---|---|---|
colors |
gradient | stops | ||
alphaFromInput |
select | "off" | off, luma, alpha | Take the output alpha from the input so the ramp foot outside a shape goes transparent instead of flooding the canvas. Off = opaque (default). |
hslAdjust(img, hue?, opts?)#
Adjusts hue, saturation, and lightness with optional per-hue targeting
| option | type | default | range / options | notes |
|---|---|---|---|---|
hue |
slider | 0 | -180..180 | Hue rotation in degrees (-180 to 180) |
saturation |
slider | 0 | -1..1 | Saturation adjustment (-1 to 1, negative desaturates) |
lightness |
slider | 0 | -1..1 | Lightness adjustment (-1 to 1) |
targetHue |
slider | 0 | 0..360 | Center of targeted hue range (0-360 degrees) |
targetRange |
slider | 180 | 0..180 | Width of hue range (0-180, 180 = affect all hues) |
targetFalloff |
slider | 0.5 | 0..1 | Edge softness of hue range (0-1) |
tile(img, repeatX?, opts?)#
Tiles/repeats an image across the output
| option | type | default | range / options | notes |
|---|---|---|---|---|
repeatX |
slider | 2 | 1..16 | Horizontal tile count |
repeatY |
slider | 2 | 1..16 | Vertical tile count |
offsetX |
slider | 0 | 0..1 | Horizontal offset (fraction of tile width) |
offsetY |
slider | 0 | 0..1 | Vertical offset (fraction of tile height) |
mirror |
checkbox | false | Mirror alternate tiles for seamless patterns |
crop(img, opts?)#
Crops image to a rectangular sub-region
| option | type | default | range / options | notes |
|---|---|---|---|---|
left |
slider | 0 | 0..1 | Left edge position (0-1) |
top |
slider | 0 | 0..1 | Top edge position (0-1) |
right |
slider | 1 | 0..1 | Right edge position (0-1) |
bottom |
slider | 1 | 0..1 | Bottom edge position (0-1) |
warp(src, displacement, strength?, opts?)#
Displaces pixels using a displacement map
| option | type | default | range / options | notes |
|---|---|---|---|---|
strength |
slider | 10 | 0..100 (unbounded above) | Displacement strength in pixels |
mask(img, mask, opts?)#
Uses a grayscale mask to modulate the alpha channel
| option | type | default | range / options | notes |
|---|---|---|---|---|
invert |
checkbox | false | Invert the mask (white becomes transparent) |
channelSplit(img) -> { r, g, b, a }#
Splits an image into its R, G, B, A channels as separate grayscale images
colorSpaceDecompose(img, colorSpace?, opts?) -> { c1, c2, c3, a }#
Splits an image into channels of HSL, HSV, LAB, or OKLCH color spaces
| option | type | default | range / options | notes |
|---|---|---|---|---|
colorSpace |
select | "hsl" | hsl, hsv, lab, oklch | Color space to decompose into |
magerie.anim#
Pure motion helpers for scripted animation. Every function is a pure function of the stepped clock, so an Image to Video bake reproduces Play exactly (no Math.random / Date.now). Read the clock from a draw recipe's time argument.
ease.<curve>(t)#
Easing curves, each mapping a clamped 0..1 progress to an eased value: linear, inQuad, outQuad, inOutQuad, inCubic, outCubic, inOutCubic, outBack, outElastic (the last two overshoot within a bounded range).
const p = magerie.anim.ease.outCubic(time / 0.5) // ease in over the first 0.5senvelope(t, { fadeIn?, fadeOut?, duration }) -> 0..1#
An opacity ramp: fades up from 0 over fadeIn seconds, holds at 1, then fades back to 0 over the last fadeOut seconds ending at duration. fadeIn and fadeOut default to 0 (no ramp on that side).
shake(t, { freq?, amp?, seed? }) -> { x, y }#
Deterministic 2D jitter: per axis, the mean of three seeded, incommensurate sinusoids. Bounded by amp (default 1); freq sets the rate (default 15); distinct seeds stay out of phase so parallel shakes do not sync.
typewriter(text, t, { cps?, delay? }) -> string#
The leading substring of text visible at time t, revealing cps characters per second (default 20) after delay seconds (default 0).
spring(t, { frequency?, damping? }) -> number#
Analytic damped-spring step response: 0 at t <= 0, settling at 1. damping (default 0.55) below 1 overshoots and rings (bouncy entrances); 1 or above is critically damped (no overshoot). frequency (default 3) sets the oscillation rate in Hz. Closed-form, so it is a pure function of t.
const p = magerie.anim.spring(time - 0.2, { frequency: 2.2, damping: 0.5 })
ctx.fillRect(40, 240, 420 * Math.max(0, p), 90) // panel boings open at t=0.2stagger(t, index, { each?, duration?, delay?, ease? }) -> 0..1#
Eased progress of item index in a staggered sequence: item i starts at delay + i * each (each default 0.06) and animates over duration (default 0.35) with ease (a curve name like "outBack", default "outCubic", or a custom (t) => number). THE primitive for per-char / per-word / per-line reveals - pair it with magerie.text.layout slot indices.
cycle(t, { period, mode? }) -> 0..1#
Normalized phase of a repeating cycle: "loop" (sawtooth, default), "pingpong" (triangle 0->1->0), "sine" (smooth 0->1->0). For pulsing underlines, looping accents, breathing panels.
cueAt(t, cues) -> { index, progress }#
The active cue at time t from an array of { start, end, ... } spans: index into the array (-1 when no cue covers t - gaps count) and progress 0..1 inside the cue. The karaoke primitive: highlight word cues[index] while drawing all words.
spreadCues(text, { start?, duration, by? }) -> [{ start, end, text }]#
Split text into word (default) or char cues spread across duration seconds from start, each unit's share proportional to its character count (longer words hold longer). Word indices line up with magerie.text.layout(..., { by: "word" }) slot indices, so cueAt(...).index maps straight onto layout slots. For real speech timing, pass hand-timed cues to cueAt instead.
magerie.text#
Measurement/layout helpers for text effects. Positions are computed with the ctx's CURRENT font - set ctx.font before calling. Per-unit widths ignore kerning between neighbours (each unit measured alone); use letterSpacing to tune rhythm.
layout(ctx, text, { x, y, by?, align?, letterSpacing?, wordSpacing?, maxWidth?, lineHeight? }) -> slots#
Measures and places each char (by: "char", default) or word (by: "word") of text, returning { text, x, y, width, index, line } slots. Explicit \n breaks lines and maxWidth word-wraps (an over-wide single word keeps its own line; wrapping collapses runs of spaces). x,y anchor the FIRST line's baseline per align ("left" default, "center", "right", applied per line); further lines step by lineHeight (default 1.25x the px size parsed from ctx.font). Whitespace advances the cursor but produces no slot, so index is contiguous across the whole block and feeds anim.stagger / anim.cueAt directly; use line to stagger line-by-line (anim.stagger(t, slot.line, { each: 0.25 })). Draw a slot with ctx.fillText(slot.text, slot.x + dx, slot.y + dy).
ctx.font = "bold 56px sans-serif"
const slots = magerie.text.layout(ctx, "HELLO WORLD", { x: out.width / 2, y: 200, align: "center" })
for (const s of slots) {
const p = magerie.anim.stagger(time, s.index, { each: 0.05, duration: 0.4, ease: "outBack" })
ctx.globalAlpha = p
ctx.fillText(s.text, s.x, s.y + 24 * (1 - p)) // rise-in, char by char
}
ctx.globalAlpha = 1Next#
- Code Nodes: the JavaScript node's execution model, directives, and draw recipes