Thunk Machine
Contents

Thunk Machine shader reference

A shader changes the image your patches draw. It can soften edges, break the picture into blocks, bend it, change its colors, or leave trails. You can start by adding one effect to an ordinary scene array.

The first sections walk from a simple effect to more controlled combinations. The effect index then gives you every built-in effect method, with all its parameters. Looking for noiseWarp? Start with a gently moving distortion, or jump to its parameter table.

Start with one effect

Put your drawing patches in an array, then add an effect after the closing bracket:

const picture = () => {
  background(12, 16, 30);
  noStroke();
  fill(240, 100, 160);
  circle(width * 0.4, height * 0.5, height * 0.45);
  fill(70, 210, 230);
  rect(width * 0.55, height * 0.25, width * 0.2, height * 0.5);
};

const scene = [picture].blur(3);
scene.activate();

Run this scene cell. The drawing creates the image, and .blur(3) softens it. The number is a blur radius in pixels. Try 1, then 6, and rerun the cell to see the difference. You do not need to create a ShaderChain object for this.

The following variations replace the scene declaration and activation while keeping picture above them. Try each variation separately.

If you already have a patch, use its name in place of picture. A group works too: [myPatch, anotherPatch].blur(3) blurs the image they draw together. These effect methods are supplied by Thunk Machine; they are available in its editor.

Add a gently moving distortion

Replace the example's scene declaration with:

const scene = [picture].noiseWarp(0.02, 4, 0.1);
scene.activate();

noiseWarp bends the image with smoothly varying noise. Its three arguments are amount, scale, speed, in that order:

Argument This example What to try
amount 0.02 Distortion strength. Try 0.005 for a slight drift or 0.06 for a stronger bend. 0 leaves the image unchanged.
scale 4 Pattern frequency. Lower values make broad bends; higher values make finer ripples.
speed 0.1 How fast the noise travels. 0 holds the pattern still.

This effect is built into Thunk Machine and is ready to use directly in your scene code.

Combine effects in order

Add another method to process the result of the first:

const scene = [picture]
  .noiseWarp(0.02, 4, 0.1)
  .pixelate(64, 36)
  .saturate(0.6);
scene.activate();

This bends the picture, samples it onto a 64-column by 36-row grid, then reduces its saturation. Read the effects from top to bottom. The order changes the result: blurring after pixelation softens the blocks; pixelating after blur gives hard blocks sampled from a soft picture.

Try one or two effects first so you can see what each contributes. Most methods in this reference work directly on arrays. Every parameter entry states whether that shorthand is available.

Choose which patches get the effect

Effects apply to the array they are attached to. Nest an affected group inside your scene when you want to keep the background or other patches clear of the effect:

const backdrop = () => background(12, 16, 30);
const orb = () => {
  noStroke();
  fill(70, 210, 230);
  circle(width * 0.5, height * 0.5, height * 0.35);
};
const sharpLine = () => {
  stroke(255);
  strokeWeight(2);
  line(0, height * 0.7, width, height * 0.7);
};

const scene = [
  backdrop,
  [orb].blur(4),
  sharpLine,
];
scene.activate();

Here only the orb is blurred. The backdrop is outside its group, and the line is drawn afterward. To blur the entire composition, put .blur(4) after the outer closing bracket instead.

Set only the parameters you need

Yes: an effect that accepts an options object lets you supply just a few properties. Every omitted property keeps its documented default. Property order does not matter.

const scene = [picture].lensWarp({
  strength: 0.25,
  twist: 0.3,
});
scene.activate();

This sets only the lens strength and twist. Its center remains at x: 0.5, y: 0.5, its radius stays 0.4, feather stays 0.15, and wrap stays 0. You do not need to copy the whole parameter table. Twist is in radians here.

The same approach works for effects with many settings. For example, .lightRays({ samples: 12, strength: 0.6 }) changes two settings, and .atmosphere({ opacity: 0.4 }) changes one. Omitted settings retain their behavior; omission does not switch them off.

There are two calling styles, and the signature in each reference entry tells you which one to use:

Signature style How to supply settings Example
.lensWarp({ strength, x, y, … }) An object with whichever named properties you want to change. .lensWarp({ twist: 0.3 })
.noiseWarp(amount, scale, speed) Values in the listed order. You can omit values at the end. .noiseWarp(0.02)

For positional methods, use undefined to keep an earlier default while setting a later argument:

const scene = [picture].noiseWarp(undefined, undefined, 0);
scene.activate();

That keeps noiseWarp's default amount 0.03 and scale 4, while setting speed to 0. An options object is supported only where the signature shows braces. noiseWarp, blur, rotate, and other positional methods do not accept a named options object. The choice between an array and an explicit ShaderChain does not change the method's argument style.

Connect motion audio and controls

A number gives a fixed setting. A function supplies a changing value on each rendered frame. You can do this directly on an array:

const scene = [picture]
  .noiseWarp(({ audio }) => 0.01 + audio.bass * 0.04, 4, 0.1)
  .hue(({ time }) => time * 0.02);
scene.activate();

Bass now increases the distortion strength, while time slowly cycles the colors. The other noiseWarp arguments remain ordinary numbers. In an options object, individual properties can be functions too:

const scene = [picture].lensWarp({
  strength: 0.25,
  twist: ({ controls }) => controls.twist,
});
scene.activate();

Create a Control named twist in the performance or scene before using that example. The callback reads the Control; it does not create it. To map a 0–1 knob to a half turn, use ({ controls }) => controls.twist * Math.PI. To read a declared modulation instead, use modulations in place of controls.

Use time for global elapsed seconds or sceneTime for scene-relative motion. Keep callbacks small and return finite numbers. Four-component parameters can use a callback returning four numbers; a palette callback returns the entire palette. A fixed phase holds still unless the effect also has a speed or rate that advances it.

When to use ShaderChain

Array effects cover many everyday treatments. An explicit ShaderChain is useful when you want to name the effect separately from its source, control the blend of the entire treatment, temporarily bypass it, or use an effect that has no array method.

Here is an effect built separately and placed after the picture:

const softWarp = new ShaderChain()
  .noiseWarp(0.02, 4, 0.1)
  .blur(2);

const scene = [picture, softWarp];
scene.activate();

It processes the pixels drawn before it in its current layer. Placing it after two patches processes their combined image. Putting it inside a nested group, such as [backdrop, [orb, softWarp], sharpLine], limits its input to that group.

The methods and parameters are the same ones used on arrays. An options object can still contain just one or two properties:

const lensLook = new ShaderChain().lensWarp({ twist: 0.3 });

Include lensLook after a source in your scene to see it. Like the earlier partial-object example, this retains the default lens strength, position, radius, feather, and edge behavior.

What a separate chain lets you control

Whole-chain controls apply to the combined treatment:

const softWarp = new ShaderChain()
  .noiseWarp(0.02, 4, 0.1)
  .blur(2)
  .mix(0.6);

const scene = [picture, softWarp];
scene.activate();

.mix(0.6) blends 60% of the completed treatment with 40% of the original input. Use .mix(0) for the original picture or .mix(1) for the full treatment. You can also pass a live callback to mix. This is wet/dry mixing; it is different from making the entire layer transparent with array .opacity().

Capability What you can do
.mix(amount) Fade the whole treatment against its original input.
.blend('screen') Choose how the completed effect combines with its original input. Other modes include add, multiply, and difference.
.bypass(true) and .bypass(false) Temporarily skip the chain's drawing and resume it without removing its definition.
A named chain such as softWarp Keep the effect definition separate and place it after different source patches. Use separate chains or .clone() when you need independent instances.
Effects marked Explicit ShaderChain only Use native grading, palette, and surface operators such as gradeColor, paletteMap, and halftone, plus tvDistortion and glasses3D.

For example, native color grading accepts a short options object and requires an explicit chain:

const warmGrade = new ShaderChain().gradeColor({
  temperature: 0.2,
  saturation: 0.85,
});
const scene = [picture, warmGrade];
scene.activate();

Build an effect array or a chain while evaluating your code, outside draw(). Animate its numeric parameters through callbacks. Appending operators every frame would keep growing the treatment.

Find the right parameter table

This guide covers all 84 built-in effect methods. Every name in the effect index is a method you can call in scene code. The index shows whether it works on arrays, an explicit ShaderChain, or both. The array name colorShift is listed alongside its ShaderChain equivalent, shift.

Each entry gives the call signature, parameter defaults, units, ranges, and a short example. For example, use .recursiveCopies({ angle: 0.15 }) to rotate nested copies. The braces show that you can supply only the named settings you need, and the angle table tells you that this value is in radians.

Examples that create an unnamed chain demonstrate the method call; assign the chain a name and place it after a source to use it in a scene. Names such as secondImage and displacementMap stand for patches you supply. See Imported and custom shaders for effects defined outside this built-in API.

Read units and defaults correctly

Convention Meaning
Canvas fractions X uses width and Y uses height; 0.5, 0.5 is the center.
Shorter-edge fractions 1 equals the shorter canvas dimension. This keeps circles and distortion distances consistent on wide screens.
Pixels Pixels of the current render target. A radius of 10 can look different at another render resolution.
Angles Radians unless explicitly described as turns; π is 180°.
Hue and cycle phase Turns/cycles where stated; 1 is a full cycle. Noise phase is a travel/evolution coordinate, not necessarily periodic.
Colors Normally normalized 0–1; four-component arguments require [r, g, b, a]. Some operators ignore the supplied alpha, as noted.
Default The value used when omitted. It is not necessarily an identity value.
Clamped The implementation enforces the listed interval.
Start around / try A suggested working range, not an enforced limit or a performance guarantee.

For positional calls, argument order matters. Use undefined to retain an earlier default while setting a later argument, such as .bloom(undefined, 8, 0.6). Option-object methods accept named settings, such as .lensWarp({ radius: 0.3 }).

Some surprising defaults are intentional in the current implementation: new ShaderChain().rotate() uses 10 radians, .scale() uses 1.5, .scroll() offsets half an image on each axis, .contrast() uses 1.6, and .brightness() adds 0.4. Array rotate() and scale() override those two defaults to 0 and 1. Pass explicit values when the desired starting point is neutral.

Choose an effect

Visual goal Useful starting point
Coarse pixels or repeated tiles pixelate, repeat, tilePanels
Mirrors or circular arrangements reflectAxis, kaleidoscopeFold, radialCopies
Organic warping or local twisting noiseWarp, displacementField, rippleWarp, waveWarp, lensWarp
Soft focus or a bright halo blur, lightBloom, alphaGlow
Reveal, key, or shape an image windowMask, wipeMask, compoundMask, chromaKey
Change the palette paletteMap, duotone, gradeColor
Printed texture halftone, surfaceDither, surfaceGrain, emboss
Tape damage or temporal trails vhsTape, glitch, datamosh, feedback
Combine separate sources composite, screen, modulate, mask, alphaMask
Generate soft forms or atmosphere metaballField, atmosphere

Effect index

These are the built-in effect methods, alphabetically. Choose a name to see its full signature and parameters. Use the arguments shown in that entry; the parentheses below identify callable methods and do not imply that every method can be called without arguments.

Method Where it works
.add() Arrays and ShaderChain.
.alphaGlow() Arrays and ShaderChain.
.alphaMask() Arrays and ShaderChain.
.atmosphere() Arrays and ShaderChain.
.bloom() Arrays and ShaderChain.
.blur() Arrays and ShaderChain.
.brightness() Arrays and ShaderChain.
.chromaKey() Arrays and ShaderChain.
.color() Arrays and ShaderChain.
.colorama() Arrays and ShaderChain.
.colorShift() Arrays; use .shift() on ShaderChain.
.composite() Arrays and ShaderChain.
.compoundMask() Arrays and ShaderChain.
.contrast() Arrays and ShaderChain.
.crop() Arrays and ShaderChain.
.darken() Arrays and ShaderChain.
.datamosh() Arrays and ShaderChain.
.difference() Arrays and ShaderChain.
.displacementField() Arrays and ShaderChain.
.duotone() Explicit ShaderChain only.
.edgeDetect() Arrays and ShaderChain.
.emboss() Explicit ShaderChain only.
.feedback() Arrays and ShaderChain.
.focusVignette() Arrays and ShaderChain.
.glasses3D() Explicit ShaderChain only.
.glitch() Arrays and ShaderChain.
.gradeColor() Explicit ShaderChain only.
.halftone() Explicit ShaderChain only.
.hue() Arrays and ShaderChain.
.invert() Arrays and ShaderChain.
.kaleid() Arrays and ShaderChain.
.kaleidoscopeFold() Arrays and ShaderChain.
.lensWarp() Arrays and ShaderChain.
.lightBloom() Arrays and ShaderChain.
.lighten() Arrays and ShaderChain.
.lightRays() Arrays and ShaderChain.
.lightSweep() Arrays and ShaderChain.
.luma() Arrays and ShaderChain.
.lumaMask() Arrays and ShaderChain.
.mask() Arrays and ShaderChain.
.metaballField() Arrays and ShaderChain.
.mirror() Arrays and ShaderChain.
.modulate() Arrays and ShaderChain.
.multiply() Arrays and ShaderChain.
.noiseMask() Arrays and ShaderChain.
.noiseWarp() Arrays and ShaderChain.
.overlay() Arrays and ShaderChain.
.paletteMap() Explicit ShaderChain only.
.pixelate() Arrays and ShaderChain.
.posterize() Arrays and ShaderChain.
.posterizeDither() Explicit ShaderChain only.
.radialCopies() Arrays and ShaderChain.
.recursiveCopies() Arrays and ShaderChain.
.reflectAxis() Arrays and ShaderChain.
.repeat() Arrays and ShaderChain.
.repeatX() Arrays and ShaderChain.
.repeatY() Arrays and ShaderChain.
.rgba() Arrays and ShaderChain.
.rgbSplit() Arrays and ShaderChain.
.rippleWarp() Arrays and ShaderChain.
.rotate() Arrays and ShaderChain.
.saturate() Arrays and ShaderChain.
.scale() Arrays and ShaderChain.
.screen() Arrays and ShaderChain.
.scroll() Arrays and ShaderChain.
.scrollX() Arrays and ShaderChain.
.scrollY() Arrays and ShaderChain.
.sharpen() Arrays and ShaderChain.
.shift() ShaderChain; use .colorShift() on arrays.
.spatialFade() Arrays and ShaderChain.
.subtract() Arrays and ShaderChain.
.sum() Arrays and ShaderChain.
.surfaceDither() Explicit ShaderChain only.
.surfaceGrain() Explicit ShaderChain only.
.thresh() Arrays and ShaderChain.
.tilePanels() Arrays and ShaderChain.
.transform() Arrays and ShaderChain.
.tvDistortion() Explicit ShaderChain only.
.vhrDistortion() Arrays and ShaderChain.
.vhsTape() Arrays and ShaderChain.
.videoNoise() Arrays and ShaderChain.
.vignette() Arrays and ShaderChain.
.waveWarp() Arrays and ShaderChain.
.windowMask() Arrays and ShaderChain.
.wipeMask() Arrays and ShaderChain.

Position and repetition

transform

Move, resize, and rotate an image around an anchor. Sampling wraps at the image edges; use crop or a mask when you need a bounded region.

.transform(x, y, scaleX, scaleY, angle, anchorX, anchorY) — Also available on arrays.

Parameter Default Meaning and range
x 0 Translation in canvas-width fractions; positive values move the picture right. Start around −0.25–0.25.
y 0 Translation in canvas-height fractions; positive values move the picture down. Start around −0.25–0.25.
scaleX 1 Horizontal size multiplier; 1 unchanged, 2 twice as wide. Use positive, nonzero values.
scaleY 1 Vertical size multiplier; 1 unchanged, 2 twice as tall. Use positive, nonzero values.
angle 0 Radians. 0 is the initial orientation; π/2 is a quarter turn. Animate with a callback.
anchorX 0.5 Transform pivot X in canvas-width fractions; 0.5 centers it.
anchorY 0.5 Transform pivot Y in canvas-height fractions; 0.5 centers it.
new ShaderChain().transform(0.1, 0, 0.8, 0.8, 0.2, 0.5, 0.5);

rotate

Rotate around the image center, with optional continuous rotation.

.rotate(angle, speed) — Also available on arrays.

Parameter Default Meaning and range
angle 10 Radians. 0 is the initial orientation; π/2 is a quarter turn. Animate with a callback.
speed 0 Radians per second added to angle using global time. 0 holds still; try 0.1.
new ShaderChain().rotate(0, 0.1);

The explicit chain default is 10 radians, whereas [patch].rotate() defaults to 0. Pass an angle explicitly. This older operator uses normalized rectangular coordinates and wraps the sampled image.

scale

Zoom around a chosen center. Values above 1 enlarge the picture.

.scale(amount, xMult, yMult, offsetX, offsetY) — Also available on arrays.

Parameter Default Meaning and range
amount 1.5 Overall magnification. Try 0.5–2; absolute magnitude is used.
xMult 1 Additional horizontal multiplier, multiplied by amount.
yMult 1 Additional vertical multiplier, multiplied by amount.
offsetX 0.5 Zoom center X in canvas-width fractions.
offsetY 0.5 Zoom center Y in canvas-height fractions.
new ShaderChain().scale(1.2, 1, 1, 0.5, 0.5);

The explicit chain default is 1.5, while [patch].scale() defaults to 1. Negative values do not mirror this operator: it uses absolute magnitudes. Sampling wraps.

mirror

Fold each chosen axis into a reflected pair.

.mirror(horizontal, vertical) — Also available on arrays.

Parameter Default Meaning and range
horizontal 1 Horizontal folding amount, clamped 0–1; 1 produces left/right symmetry.
vertical 0 Vertical folding amount, clamped 0–1; 1 produces top/bottom symmetry.
new ShaderChain().mirror(1, 0);

This creates mirrored repetition; it is not a simple whole-image flip.

crop

Hide pixels outside a rectangular region without resizing the remaining picture.

.crop(left, right, top, bottom) — Also available on arrays.

Parameter Default Meaning and range
left 0 Left visible bound, canvas-width fraction.
right 1 Right visible bound, canvas-width fraction; keep greater than left.
top 0 Top visible bound, canvas-height fraction.
bottom 1 Bottom visible bound, canvas-height fraction; keep greater than top.
new ShaderChain().crop(0.1, 0.9, 0.2, 0.8);

pixelate

Sample the picture on a coarse grid. Fewer cells make larger blocks.

.pixelate(pixelX, pixelY) — Also available on arrays.

Parameter Default Meaning and range
pixelX 20 Horizontal cell count, not cell width in pixels. Absolute magnitude, minimum 1; try 8–160.
pixelY 20 Vertical cell count, independent of pixelX. Absolute magnitude, minimum 1; try 8–90.
new ShaderChain().pixelate(32, 18);

Omitting pixelY uses its own default of 20; it does not copy pixelX.

repeat

Tile the image in both directions, optionally staggering alternating rows and columns.

.repeat(repeatX, repeatY, offsetX, offsetY) — Also available on arrays.

Parameter Default Meaning and range
repeatX 3 Horizontal repeat count; absolute magnitude, minimum 1. Try integer counts 1–8.
repeatY 3 Vertical repeat count; absolute magnitude, minimum 1.
offsetX 0 Horizontal shift of alternating rows, in tile-width units; 0.5 makes a half-tile stagger.
offsetY 0 Vertical shift of alternating columns, in tile-height units.
new ShaderChain().repeat(4, 3, 0.5, 0);

repeatX

Tile in the horizontal direction.

.repeatX(reps, offset) — Also available on arrays.

Parameter Default Meaning and range
reps 3 Repeat count; absolute magnitude, minimum 1. Start with integers 2–8.
offset 0 Vertical offset of alternating tiles, in normalized image units; try 0–0.5.
new ShaderChain().repeatX(4, 0.25);

repeatY

Tile in the vertical direction.

.repeatY(reps, offset) — Also available on arrays.

Parameter Default Meaning and range
reps 3 Repeat count; absolute magnitude, minimum 1. Start with integers 2–8.
offset 0 Horizontal offset of alternating tiles, in normalized image units; try 0–0.5.
new ShaderChain().repeatY(4, 0.25);

kaleid

Fold the picture into radial mirrored sectors around its center.

.kaleid(sides) — Also available on arrays.

Parameter Default Meaning and range
sides 4 Sector count; absolute magnitude, minimum 1. Start with integers 3–12; fractional counts can create discontinuities.
new ShaderChain().kaleid(6);

For an adjustable center and aspect-correct circular geometry, use kaleidoscopeFold.

scroll

Offset texture sampling in both directions and wrap at the edges.

.scroll(x, y, speedX, speedY) — Also available on arrays.

Parameter Default Meaning and range
x 0.5 Horizontal sampling offset in image widths; +0.1 makes features appear 10% left.
y 0.5 Vertical sampling offset in image heights; positive values make features appear upward.
speedX 0 Horizontal sampling offset per second, in image widths.
speedY 0 Vertical sampling offset per second, in image heights.
new ShaderChain().scroll(0, 0, 0.03, 0);

The defaults offset both axes by half an image. Use zeros for no initial shift. Scroll moves sampling coordinates, opposite to transform translation.

scrollX

Scroll the horizontal sampling coordinate and wrap.

.scrollX(x, speed) — Also available on arrays.

Parameter Default Meaning and range
x 0.5 Initial horizontal sampling offset as a fraction of the image dimension. Positive values move picture features left/up.
speed 0 Image dimensions per second. 0 holds still; start around −0.1–0.1.
new ShaderChain().scrollX(0, 0.03);

scrollY

Scroll the vertical sampling coordinate and wrap.

.scrollY(y, speed) — Also available on arrays.

Parameter Default Meaning and range
y 0.5 Initial vertical sampling offset as a fraction of the image dimension. Positive values move picture features left/up.
speed 0 Image dimensions per second. 0 holds still; start around −0.1–0.1.
new ShaderChain().scrollY(0, 0.03);

Distortion and symmetry

noiseWarp

Warp sampling with smoothly varying value noise.

Use it directly on an array: [picture].noiseWarp(0.02, 4, 0.1). The arguments are positional: amount, scale, then speed. See the introductory example to try it in a scene.

.noiseWarp(amount, scale, speed) — Also available on arrays.

Parameter Default Meaning and range
amount 0.03 Normalized displacement strength; each axis moves by up to half this value. 0 unchanged; start with 0.005–0.08.
scale 4 Noise frequency across normalized image coordinates; absolute magnitude, minimum 0.0001. Larger values make finer distortion.
speed 0.15 Noise-coordinate drift per second, driven by global time; 0 freezes the pattern.
new ShaderChain().noiseWarp(0.025, 5, 0.15);

displacementField

Apply aspect-correct noise displacement with a repeatable seed and explicit phase.

.displacementField({ amountX, amountY, frequency, roughness, seed, phase, angle, wrap }) — Also available on arrays.

Parameter Default Meaning and range
amountX 0.03 Horizontal displacement in shorter-edge fractions, clamped −2–2. Start around 0.01–0.1.
amountY 0.03 Vertical displacement in shorter-edge fractions, clamped −2–2.
frequency 3 Spatial noise frequency; absolute magnitude, minimum 0.001. Start around 1–12.
roughness 0.4 Fine-noise contribution, clamped 0–1; larger values add smaller details.
seed 1 Repeatable numeric seed. Change it to choose a different noise or random arrangement.
phase 0 Noise travel coordinate. Fixed values hold still; try ({ time }) => time * 0.1.
angle 0 Radians. 0 is the initial orientation; π/2 is a quarter turn. Animate with a callback.
wrap 0 0 makes sampling outside the image transparent; 1 repeats the image at its edges.
new ShaderChain().displacementField({ amountX: 0.04, amountY: 0.02, phase: ({ time }) => time * 0.1 });

rippleWarp

Create a radial wave concentrated around a ring.

.rippleWarp({ amount, frequency, phase, x, y, radius, width, wrap }) — Also available on arrays.

Parameter Default Meaning and range
amount 0.025 Signed displacement in shorter-edge fractions, clamped −2–2. Start around 0.01–0.05.
frequency 10 Wave cycles per shorter-edge unit; minimum 0. Larger values pack ripples closer.
phase 0 Wave phase in cycles; advancing by 1 repeats the wave.
x 0.5 Center X as a fraction of canvas width: 0 left, 0.5 center, 1 right.
y 0.5 Center Y as a fraction of canvas height: 0 top, 0.5 center, 1 bottom.
radius 0.25 Center radius of the strongest ripple band, in shorter-edge fractions; minimum 0.
width 0.4 Width controlling the Gaussian ripple envelope, in shorter-edge fractions. Absolute magnitude, minimum 0.0001.
wrap 0 0 makes sampling outside the image transparent; 1 repeats the image at its edges.
new ShaderChain().rippleWarp({ radius: 0.3, phase: ({ time }) => time * 0.4 });

waveWarp

Bend the image with directional sine waves.

.waveWarp({ amount, secondary, frequency, phase, angle, wrap }) — Also available on arrays.

Parameter Default Meaning and range
amount 0.04 Primary displacement perpendicular to the wave direction, in shorter-edge fractions; clamped −2–2.
secondary 0 Additional displacement along the wave direction, clamped −2–2. 0 leaves only the primary wave.
frequency 4 Wave cycles per shorter-edge unit; minimum 0.
phase 0 Phase in cycles; advancing by 1 repeats the wave.
angle 0 Wave direction in radians. At 0, the primary displacement is vertical and varies across X.
wrap 0 0 makes sampling outside the image transparent; 1 repeats the image at its edges.
new ShaderChain().waveWarp({ amount: 0.04, frequency: 5, phase: ({ time }) => time * 0.2 });

lensWarp

Magnify or pinch a local area, optionally twisting it.

.lensWarp({ strength, x, y, radius, feather, twist, wrap }) — Also available on arrays.

Parameter Default Meaning and range
strength 0.45 Clamped −0.9–0.9. Positive magnifies the center; negative pinches it; 0 leaves only any twist.
x 0.5 Center X as a fraction of canvas width: 0 left, 0.5 center, 1 right.
y 0.5 Center Y as a fraction of canvas height: 0 top, 0.5 center, 1 bottom.
radius 0.4 Affected radius in shorter-edge fractions; 0 disables the lens.
feather 0.15 Outer transition width in shorter-edge fractions; minimum 0.0001.
twist 0 Local twist angle in radians, strongest near the center.
wrap 0 0 makes sampling outside the image transparent; 1 repeats the image at its edges.
new ShaderChain().lensWarp({ strength: 0.5, radius: 0.35, twist: 0.6 });

reflectAxis

Reflect one side across an adjustable line.

.reflectAxis({ x, y, angle, flip }) — Also available on arrays.

Parameter Default Meaning and range
x 0.5 Center X as a fraction of canvas width: 0 left, 0.5 center, 1 right.
y 0.5 Center Y as a fraction of canvas height: 0 top, 0.5 center, 1 bottom.
angle 0 Normal direction of the mirror line in radians. 0 gives a vertical dividing line; π/2 gives a horizontal one.
flip 0 0 copies the positive side across the line; 1 copies the other side. Switches at 0.5.
new ShaderChain().reflectAxis({ x: 0.5, angle: Math.PI / 4 });

kaleidoscopeFold

Make a circular, aspect-correct kaleidoscope with adjustable framing.

.kaleidoscopeFold({ segments, x, y, angle, zoom, sourceAngle }) — Also available on arrays.

Parameter Default Meaning and range
segments 6 Sector count, floored and clamped to 2–64.
x 0.5 Center X as a fraction of canvas width: 0 left, 0.5 center, 1 right.
y 0.5 Center Y as a fraction of canvas height: 0 top, 0.5 center, 1 bottom.
angle 0 Orientation of the output sector pattern, in radians.
zoom 1 Magnification of sampled content; absolute magnitude, minimum 0.001. Start at 0.5–2.
sourceAngle 0 Direction of the sampled source wedge in radians. Animate this to change the contents while keeping the sector layout fixed.
new ShaderChain().kaleidoscopeFold({ segments: 8, sourceAngle: ({ time }) => time * 0.1 });

radialCopies

Place separate copies around a circle. Transparent sources make the individual copies easiest to see.

.radialCopies({ count, x, y, radius, copyScale, angle, orientation, follow, sourceX, sourceY, fade, original }) — Also available on arrays.

Parameter Default Meaning and range
count 6 Copy count, floored and clamped to 0–32.
x 0.5 Center X as a fraction of canvas width: 0 left, 0.5 center, 1 right.
y 0.5 Center Y as a fraction of canvas height: 0 top, 0.5 center, 1 bottom.
radius 0.25 Distance from layout center to each copy, in shorter-edge fractions.
copyScale 0.3 Copy magnification relative to the source; absolute magnitude, minimum 0.001.
angle 0 Starting placement angle around the circle, in radians.
orientation 0 Additional rotation of each copy, in radians.
follow 1 Clamped 0–1: 1 turns each copy with its placement angle; 0 keeps all copies at the same orientation.
sourceX 0.5 Source pivot X in canvas-width fractions.
sourceY 0.5 Source pivot Y in canvas-height fractions.
fade 1 Clamped 0–1; copy i has opacity multiplied by fade^i.
original 0 Original image opacity, clamped 0–1. 0 shows copies only.
new ShaderChain().radialCopies({ count: 8, radius: 0.3, copyScale: 0.18 });

More copies require more texture samples per output pixel.

recursiveCopies

Overlay progressively smaller copies of the current image, with repeated offsets and rotation.

.recursiveCopies({ depth, ratio, x, y, offsetX, offsetY, angle, fade, original }) — Also available on arrays.

Parameter Default Meaning and range
depth 6 Number of inset copies, floored and clamped 0–16.
ratio 0.72 Size ratio per generation, clamped 0.05–0.99.
x 0.5 Center X as a fraction of canvas width: 0 left, 0.5 center, 1 right.
y 0.5 Center Y as a fraction of canvas height: 0 top, 0.5 center, 1 bottom.
offsetX 0 Horizontal step per generation, in shorter-edge fractions.
offsetY 0 Vertical step per generation, in shorter-edge fractions.
angle 0 Additional rotation per generation, in radians.
fade 0.9 Opacity multiplier per generation, clamped 0–1.
original 1 Original image opacity, clamped 0–1.
new ShaderChain().recursiveCopies({ depth: 6, ratio: 0.72, angle: 0.15 });

For a gentle back-and-forth turn of 4–12 degrees, convert the animated value to radians:

new ShaderChain().recursiveCopies({
  angle: ({ time }) =>
    (8 + Math.sin((time ?? 0) * 0.4) * 4) * Math.PI / 180,
});

This repeats the current image within one frame; feedback instead uses the previous frame. More depth adds texture samples.

Blur light and edges

blur

Soften nearby pixels using a nine-tap filter.

.blur(radius) — Also available on arrays.

Parameter Default Meaning and range
radius 2 Sample offset in render-target pixels; minimum 0. Start with 1–8. Larger values spread the taps rather than adding taps.
new ShaderChain().blur(3);

sharpen

Increase local contrast against four neighboring samples.

.sharpen(amount) — Also available on arrays.

Parameter Default Meaning and range
amount 0.5 Sharpening strength. 0 unchanged; start around 0.2–1. Values are not clamped; high values can clip highlights and shadows.
new ShaderChain().sharpen(0.6);

edgeDetect

Replace RGB with a grayscale edge image while retaining the incoming alpha.

.edgeDetect(amount, radius, alphaEdges) — Also available on arrays.

Parameter Default Meaning and range
amount 1 Edge brightness multiplier. 0 produces black RGB, not the original picture. Start around 0.5–3.
radius 1 Neighbor offset in pixels; minimum 0.
alphaEdges 0 Contribution from changes in transparency, clamped 0–1. Use 1 to include silhouettes.
new ShaderChain().edgeDetect(1.5, 1, 1);

bloom

Add a compact glow from four neighboring bright samples.

.bloom(amount, radius, threshold) — Also available on arrays.

Parameter Default Meaning and range
amount 0.8 Nonnegative glow strength; 0 adds no glow. Start around 0.2–1.5.
radius 4 Sample offset in pixels; minimum 0. Start around 2–12.
threshold 0.55 Luminance cutoff; start 0–0.95 and keep below 1 for a well-defined transition.
new ShaderChain().bloom(0.6, 5, 0.55);

For a wider, tinted halo with adjustable threshold softness, use lightBloom.

vignette

Darken the picture toward the screen edges.

.vignette(amount, softness) — Also available on arrays.

Parameter Default Meaning and range
amount 0.5 Edge-darkening strength, clamped 0–1.
softness 0.35 Transition width in aspect-correct screen units. Start around 0.1–0.8; use a positive value.
new ShaderChain().vignette(0.4, 0.4);

For a movable center, elliptical shape, and adjustable radius, use focusVignette.

rgbSplit

Separate red and blue samples in opposite directions, leaving green in place.

.rgbSplit(amount, angle) — Also available on arrays.

Parameter Default Meaning and range
amount 3 Offset of each shifted channel in pixels. 0 unchanged; start around 1–8. Negative values swap the directions.
angle 0 Separation direction in radians; 0 horizontal, π/2 vertical.
new ShaderChain().rgbSplit(4, 0);

lightBloom

Spread light from bright image regions into a tinted halo.

.lightBloom({ strength, radius, threshold, softness, tint, amount }) — Also available on arrays.

Parameter Default Meaning and range
strength 0.8 Glow intensity, clamped 0–8. Start around 0.3–1.5.
radius 18 Halo radius in pixels, clamped 0–256.
threshold 0.65 Luminance gate, clamped 0–1. Lower values include more source pixels.
softness 0.1 Gate transition half-width, clamped 0–1; 0 is a near-hard cutoff.
tint [1, 1, 1, 1] Four-number normalized RGBA array. RGB tints the glow; the fourth component is required but not used.
amount 1 Glow contribution, clamped 0–1; 0 adds none.
new ShaderChain().lightBloom({ radius: 24, strength: 0.8, tint: [0.4, 0.8, 1, 1] });

Uses 25 halo taps per pixel. A transparent source is needed for alphaGlow to outline a shape; an opaque background makes the whole layer its silhouette.

alphaGlow

Build a colored halo from the source silhouette, using alpha rather than brightness.

.alphaGlow({ strength, radius, threshold, softness, tint, amount, outerOnly }) — Also available on arrays.

Parameter Default Meaning and range
strength 1 Glow intensity, clamped 0–8. Start around 0.3–1.5.
radius 24 Halo radius in pixels, clamped 0–256.
threshold 0.01 Alpha gate, clamped 0–1. Lower values include more source pixels.
softness 0.1 Gate transition half-width, clamped 0–1; 0 is a near-hard cutoff.
tint [0.41, 0.83, 0.84, 1] Four-number normalized RGBA array. RGB tints the glow; the fourth component is required but not used.
amount 1 Glow contribution, clamped 0–1; 0 adds none.
outerOnly 1 Clamped 0–1: 1 suppresses glow over opaque source pixels; 0 also lights the interior.
new ShaderChain().alphaGlow({ radius: 24, strength: 0.8, tint: [0.4, 0.8, 1, 1] });

Uses 25 halo taps per pixel. A transparent source is needed for alphaGlow to outline a shape; an opaque background makes the whole layer its silhouette.

lightRays

Draw radial shafts of light from bright regions toward an origin.

.lightRays({ x, y, reach, samples, decay, threshold, softness, strength, tint, amount }) — Also available on arrays.

Parameter Default Meaning and range
x 0.5 Center X as a fraction of canvas width: 0 left, 0.5 center, 1 right.
y 0.5 Center Y as a fraction of canvas height: 0 top, 0.5 center, 1 bottom.
reach 0.9 Fraction of the path toward the origin to sample, clamped 0–1.
samples 24 Sample count, floored and clamped 4–32; more samples can smooth rays at higher cost.
decay 0.96 Weight retained per sample, clamped 0–1. Lower values favor the nearest samples.
threshold 0.65 Luminance gate, clamped 0–1.
softness 0.1 Gate transition half-width, clamped 0–1.
strength 1 Ray brightness, clamped 0–8.
tint [1, 0.93, 0.8, 1] Four-number normalized RGBA array; RGB is used, alpha is ignored.
amount 1 Ray contribution, clamped 0–1.
new ShaderChain().lightRays({ x: 0.5, y: 0.2, reach: 0.7, samples: 16 });

lightSweep

Move a bright band across existing pixels without expanding their alpha.

.lightSweep({ phase, angle, width, softness, wrap, strength, tint, amount }) — Also available on arrays.

Parameter Default Meaning and range
phase 0.5 Sweep progress; 0 and 1 are opposite outer edges. With wrap 1, advancing by 1 repeats.
angle 0 Travel direction in radians; 0 sweeps horizontally.
width 0.15 Band width in shorter-edge fractions, clamped 0–2. 0 disables it.
softness 0.6 Fraction of half-width used for feathering, clamped 0–1.
wrap 1 1 repeats phase using its fractional part; 0 clamps phase to 0–1.
strength 0.5 Added light strength, clamped 0–4.
tint [1, 0.96, 0.86, 1] Normalized RGBA array; RGB is used, alpha is ignored.
amount 1 Light contribution, clamped 0–1.
new ShaderChain().lightSweep({ phase: ({ time }) => time * 0.2, width: 0.15 });

focusVignette

Shade outside an adjustable elliptical focus area while preserving alpha.

.focusVignette({ x, y, radius, softness, aspect, angle, strength, tint, amount }) — Also available on arrays.

Parameter Default Meaning and range
x 0.5 Center X as a fraction of canvas width: 0 left, 0.5 center, 1 right.
y 0.5 Center Y as a fraction of canvas height: 0 top, 0.5 center, 1 bottom.
radius 0.55 Outer radius in shorter-edge fractions, clamped 0.00001–2.
softness 0.25 Inward transition width in shorter-edge fractions, clamped 0–2.
aspect 1 Horizontal ellipse stretch, clamped 0.1–10; 1 circular before rotation.
angle 0 Radians. 0 is the initial orientation; π/2 is a quarter turn. Animate with a callback.
strength 0.65 Shading amount, clamped 0–1.
tint [0, 0, 0, 1] Normalized RGBA array. Outer RGB is multiplied by this RGB; black darkens, white leaves it unchanged. Alpha is ignored.
amount 1 Treatment strength, clamped 0–1.
new ShaderChain().focusVignette({ radius: 0.5, softness: 0.25, strength: 0.6 });

Masks and cutouts

lumaMask

Make dark pixels transparent while keeping bright pixels.

.lumaMask(threshold, softness, invert) — Also available on arrays.

Parameter Default Meaning and range
threshold 0.25 Luminance cutoff; useful range 0–1.
softness 0.1 Transition half-width around the cutoff; absolute magnitude, minimum 0.00001. Start around 0.01–0.2.
invert 0 Clamped to 0–1. 0 keeps the mask; 1 reverses it; intermediate values blend the two.
new ShaderChain().lumaMask(0.3, 0.08, 0);

luma

Make dark pixels transparent using a luminance key.

.luma(threshold, tolerance) — Also available on arrays.

Parameter Default Meaning and range
threshold 0.5 Luminance cutoff; useful range 0–1.
tolerance 0.1 Transition half-width around the cutoff; absolute magnitude plus a tiny epsilon. Start around 0.01–0.2.
new ShaderChain().luma(0.4, 0.1);

Like lumaMask with inversion off. It changes alpha, whereas thresh changes RGB to black and white.

chromaKey

Remove pixels close to a chosen RGB color.

.chromaKey(r, g, b, tolerance, softness) — Also available on arrays.

Parameter Default Meaning and range
r 0 Key red channel, normalized 0–1.
g 1 Key green channel, normalized 0–1.
b 0 Key blue channel, normalized 0–1.
tolerance 0.18 RGB distance fully removed; minimum 0. Start around 0.05–0.3. This is color distance, not a percentage.
softness 0.08 Additional color-distance band that feathers to opaque; absolute magnitude, minimum 0.00001.
new ShaderChain().chromaKey(0, 1, 0, 0.18, 0.08);

Place the keyed image in a nested layer over the replacement background. Applying the key after both layers also removes matching pixels from the background.

wipeMask

Reveal the image with a directional edge, a moving band, or a radial sweep.

.wipeMask({ mode, progress, angle, width, softness, x, y, invert, amount }) — Also available on arrays.

Parameter Default Meaning and range
mode 0 0 linear reveal; 1 narrow band; 2 radial sweep. Use integer modes.
progress 0.5 0–1 reveal progress, or the band center position in mode 1.
angle 0 Linear travel direction or radial start direction, in radians.
width 0.2 Band width as a fraction of the projected image span; used only in mode 1. 0 hides the band.
softness 0.02 Edge transition width in projected-span fractions, or turn fractions in radial mode.
x 0.5 Center X as a fraction of canvas width: 0 left, 0.5 center, 1 right.
y 0.5 Center Y as a fraction of canvas height: 0 top, 0.5 center, 1 bottom.
invert 0 Clamped to 0–1. 0 keeps the mask; 1 reverses it; intermediate values blend the two.
amount 1 Wet/dry amount, clamped to 0–1: 0 keeps the incoming image; 1 applies the full treatment.
new ShaderChain().wipeMask({ mode: 0, progress: 0.6, angle: 0.3 });

x and y choose the radial center; they do not reposition a linear wipe.

windowMask

Cut the image into a movable shape, keeping the inside by default.

.windowMask({ shape, x, y, width, height, angle, feather, cornerRadius, sides, invert, amount }) — Also available on arrays.

Parameter Default Meaning and range
shape 0 0 ellipse; 1 rounded rectangle; 2 diamond; 3 regular polygon. Use integer modes.
x 0.5 Center X as a fraction of canvas width: 0 left, 0.5 center, 1 right.
y 0.5 Center Y as a fraction of canvas height: 0 top, 0.5 center, 1 bottom.
width 0.65 Full shape width in shorter-edge fractions; 0 or less hides it.
height 0.5 Full shape height in shorter-edge fractions; 0 or less hides it.
angle 0 Radians. 0 is the initial orientation; π/2 is a quarter turn. Animate with a callback.
feather 0.02 Soft edge width in shorter-edge fractions; 0 gives a near-hard edge.
cornerRadius 0.03 Rounded-rectangle corner radius in shorter-edge fractions; capped at the smaller half-size.
sides 6 Polygon sides, floored and clamped 3–32; used only for shape 3.
invert 0 Clamped to 0–1. 0 keeps the mask; 1 reverses it; intermediate values blend the two.
amount 1 Wet/dry amount, clamped to 0–1: 0 keeps the incoming image; 1 applies the full treatment.
new ShaderChain().windowMask({ shape: 1, width: 0.8, height: 0.5, cornerRadius: 0.04 });

compoundMask

Combine two shape masks before applying them to the image.

.compoundMask({ first, firstShape, firstAngle, firstCorner, firstSides, second, secondShape, secondAngle, secondCorner, secondSides, mode, feather, invert, amount }) — Also available on arrays.

Parameter Default Meaning and range
first [0.42, 0.5, 0.6, 0.6] [centerX, centerY, width, height]. Position uses canvas fractions; sizes use shorter-edge fractions.
firstShape 0 0 ellipse; 1 rounded rectangle; 2 diamond; 3 regular polygon. Use integer modes.
firstAngle 0 Radians. 0 is the initial orientation; π/2 is a quarter turn. Animate with a callback.
firstCorner 0.03 First rounded-rectangle corner radius in shorter-edge fractions.
firstSides 6 First polygon side count, floored and clamped 3–32.
second [0.58, 0.5, 0.4, 0.4] [centerX, centerY, width, height] for the second shape, in the same units as first.
secondShape 0 0 ellipse; 1 rounded rectangle; 2 diamond; 3 regular polygon. Use integer modes.
secondAngle 0 Radians. 0 is the initial orientation; π/2 is a quarter turn. Animate with a callback.
secondCorner 0.03 Second rounded-rectangle corner radius in shorter-edge fractions.
secondSides 6 Second polygon side count, floored and clamped 3–32.
mode 2 0 union; 1 intersection; 2 first minus second; 3 exclusive-or.
feather 0.02 Shared shape edge width in shorter-edge fractions; minimum 0.00001.
invert 0 Clamped to 0–1. 0 keeps the mask; 1 reverses it; intermediate values blend the two.
amount 1 Wet/dry amount, clamped to 0–1: 0 keeps the incoming image; 1 applies the full treatment.
new ShaderChain().compoundMask({ mode: 2, first: [0.5, 0.5, 0.8, 0.8], second: [0.5, 0.5, 0.4, 0.4] });

The example cuts a circular hole out of a larger circular window.

noiseMask

Reveal the picture through a seeded noise field.

.noiseMask({ threshold, softness, frequency, roughness, seed, phase, invert, amount }) — Also available on arrays.

Parameter Default Meaning and range
threshold 0.5 Reveal threshold: 0 shows everything, 1 hides everything. Between them, higher values show less.
softness 0.1 Threshold transition width; absolute magnitude, minimum 0.00001.
frequency 6 Spatial noise frequency; absolute magnitude, minimum 0.001.
roughness 0.5 Fine-noise contribution, clamped 0–1.
seed 1 Repeatable numeric seed. Change it to choose a different noise or random arrangement.
phase 0 Noise travel coordinate. Use a callback for motion; fixed values hold still.
invert 0 Clamped to 0–1. 0 keeps the mask; 1 reverses it; intermediate values blend the two.
amount 1 Wet/dry amount, clamped to 0–1: 0 keeps the incoming image; 1 applies the full treatment.
new ShaderChain().noiseMask({ threshold: 0.5, phase: ({ time }) => time * 0.1 });

spatialFade

Fade transparency by distance or by position along a direction.

.spatialFade({ mode, x, y, radius, angle, falloff, invert, amount }) — Also available on arrays.

Parameter Default Meaning and range
mode 0 0 radial fade; 1 directional fade.
x 0.5 Center X as a fraction of canvas width: 0 left, 0.5 center, 1 right.
y 0.5 Center Y as a fraction of canvas height: 0 top, 0.5 center, 1 bottom.
radius 0.45 Radial outer edge or directional cutoff, in shorter-edge fractions.
angle 0 Fade direction in radians; used in directional mode.
falloff 0.25 Fade transition width in shorter-edge fractions; minimum 0.00001.
invert 0 Clamped to 0–1. 0 keeps the mask; 1 reverses it; intermediate values blend the two.
amount 1 Wet/dry amount, clamped to 0–1: 0 keeps the incoming image; 1 applies the full treatment.
new ShaderChain().spatialFade({ radius: 0.5, falloff: 0.25 });

tilePanels

Repeat the image inside separated panels, with optional mirroring and fit modes.

.tilePanels({ columns, rows, gap, offsetX, offsetY, stagger, mirrorX, mirrorY, fit }) — Also available on arrays.

Parameter Default Meaning and range
columns 3 Horizontal panel count, floored and clamped 1–32.
rows 2 Vertical panel count, floored and clamped 1–32.
gap 0.05 Gap as a fraction of each panel dimension, clamped 0–0.99.
offsetX 0 Horizontal source sampling offset in normalized image units.
offsetY 0 Vertical source sampling offset in normalized image units.
stagger 0 Shift of alternating rows in panel-width units.
mirrorX 0 Mirroring of alternating columns, clamped 0–1.
mirrorY 0 Mirroring of alternating rows, clamped 0–1.
fit 0 0 stretch; 1 contain with transparent margins; 2 cover with cropping.
new ShaderChain().tilePanels({ columns: 3, rows: 2, gap: 0.05, fit: 2 });

Color and palettes

posterize

Quantize each RGB channel into stepped tonal intervals.

.posterize(bins, gamma) — Also available on arrays.

Parameter Default Meaning and range
bins 3 Quantization interval count; absolute magnitude, minimum 1. Start around 3–12. A value exactly at white can occupy the top endpoint in addition to the intervals.
gamma 0.6 Exponent before quantization, inverted afterward. Absolute magnitude, minimum 0.00001; start around 0.6–1.5.
new ShaderChain().posterize(6, 1);

For an explicit number of output levels and optional dithering, use posterizeDither.

shift

Add the fractional part of each supplied value to its channel.

.shift(r, g, b, a) — On arrays, use colorShift.

Parameter Default Meaning and range
r 0.5 Fractional channel increment. 0 is neutral; 0.2 adds 0.2; an integer adds 0. Negative values wrap their fractional part, so −0.2 adds 0.8.
g 0 Fractional channel increment. 0 is neutral; 0.2 adds 0.2; an integer adds 0. Negative values wrap their fractional part, so −0.2 adds 0.8.
b 0 Fractional channel increment. 0 is neutral; 0.2 adds 0.2; an integer adds 0. Negative values wrap their fractional part, so −0.2 adds 0.8.
a 0 Fractional channel increment. 0 is neutral; 0.2 adds 0.2; an integer adds 0. Negative values wrap their fractional part, so −0.2 adds 0.8.
new ShaderChain().shift(0.1, 0, 0.2, 0);

On arrays, use .colorShift(...); native Array.shift() still removes the first item and is not a shader effect. Use brightness for a signed brightness adjustment.

invert

Blend RGB toward its photographic negative.

.invert(amount) — Also available on arrays.

Parameter Default Meaning and range
amount 1 0 unchanged, 1 fully inverted. Start within 0–1; the implementation also permits extrapolation.
new ShaderChain().invert(1);

contrast

Scale RGB around middle gray.

.contrast(amount) — Also available on arrays.

Parameter Default Meaning and range
amount 1.6 1 unchanged, 0 flat middle gray, values above 1 increase contrast. Start around 0.5–2; not clamped.
new ShaderChain().contrast(1.2);

brightness

Add an equal offset to RGB.

.brightness(amount) — Also available on arrays.

Parameter Default Meaning and range
amount 0.4 Signed RGB offset: 0 unchanged, positive brighter, negative darker. Start around −0.2–0.2; not clamped at this step.
new ShaderChain().brightness(0.08);

thresh

Convert RGB into a black-and-white luminance threshold, preserving alpha.

.thresh(threshold, tolerance) — Also available on arrays.

Parameter Default Meaning and range
threshold 0.5 Luminance cutoff; start within 0–1.
tolerance 0.04 Transition half-width, using absolute magnitude. Near 0 makes a sharp threshold; 0.1 produces a softer transition.
new ShaderChain().thresh(0.5, 0.02);

color

Multiply the four image channels by individual values. Negative multipliers use the inverted channel first.

.color(r, g, b, a) — Also available on arrays.

Parameter Default Meaning and range
r 1 Red multiplier. 1 unchanged, 0 removes the channel; start with 0–1.
g 1 Green multiplier. 1 unchanged, 0 removes the channel; start with 0–1.
b 1 Blue multiplier. 1 unchanged, 0 removes the channel; start with 0–1.
a 1 Alpha multiplier. 1 unchanged, 0 removes the channel; start with 0–1.
new ShaderChain().color(1, 0.6, 0.8, 0.75);

rgba

Multiply the four image channels by individual values.

.rgba(r, g, b, a) — Also available on arrays.

Parameter Default Meaning and range
r 1 Red multiplier. 1 unchanged, 0 removes the channel; start with 0–1.
g 1 Green multiplier. 1 unchanged, 0 removes the channel; start with 0–1.
b 1 Blue multiplier. 1 unchanged, 0 removes the channel; start with 0–1.
a 1 Alpha multiplier. 1 unchanged, 0 removes the channel; start with 0–1.
new ShaderChain().rgba(1, 0.6, 0.8, 0.75);

Use color for the special negative-value inversion behavior; rgba always performs direct multiplication.

saturate

Blend between grayscale and the original color.

.saturate(amount) — Also available on arrays.

Parameter Default Meaning and range
amount 2 0 grayscale, 1 unchanged, above 1 more saturated. Start around 0–2; not clamped.
new ShaderChain().saturate(0.5);

hue

Rotate hue while keeping HSV saturation and value.

.hue(amount) — Also available on arrays.

Parameter Default Meaning and range
amount 0.4 Hue turns: 0 unchanged, 0.5 half a cycle, 1 a full cycle. Values wrap; these are not radians.
new ShaderChain().hue(0.15);

colorama

Shift all three HSV components and wrap the resulting RGB for nonlinear color cycling.

.colorama(amount) — Also available on arrays.

Parameter Default Meaning and range
amount 0.005 Offset added to hue, saturation, and value. Try 0.005–0.1 for small changes or animate slowly. This is not a pure hue rotation.
new ShaderChain().colorama(({ time }) => time * 0.01);

sum

Make grayscale by summing weighted R, G, B, and alpha.

.sum(scale) — Also available on arrays.

Parameter Default Meaning and range
scale [1, 1, 1, 1] Four weights [r, g, b, a], or a callback returning them. The default includes alpha, so an opaque image can become white. Use [0.299, 0.587, 0.114, 0] for luminance.
new ShaderChain().sum([0.299, 0.587, 0.114, 0]);

The existing alpha is preserved. The alpha weight contributes to grayscale brightness; it does not set output alpha.

paletteMap

Map image luminance through a chosen sequence of colors.

.paletteMap({ palette, phase, repeat, steps, amount }) — Explicit ShaderChain only.

Parameter Default Meaning and range
palette [[0, 0, 0], [1, 1, 1]] Array of 1–8 RGB colors, each channel 0–1; extra colors are ignored. A callback may return the entire palette.
phase 0 Offset along the palette in palette-length units; try 0–1.
repeat 0 0 clamps at the palette ends; 1 wraps, allowing cyclic phase animation.
steps 0 0 or 1 keeps continuous interpolation; 2–64 quantizes the luminance position. Floored and clamped 0–64.
amount 1 Wet/dry amount, clamped to 0–1: 0 keeps the incoming image; 1 applies the full treatment.
new ShaderChain().paletteMap({ palette: [[0.02, 0.03, 0.1], [0.1, 0.8, 0.8], [1, 0.8, 0.2]], amount: 0.8 });

Palette RGB uses 0–1. Divide a 0–255 channel value by 255 before including it in the palette.

gradeColor

Apply exposure, white balance, contrast, saturation, gamma, and hue in one grading operation.

.gradeColor({ exposure, contrast, saturation, temperature, tint, brightness, gamma, hue, amount }) — Explicit ShaderChain only.

Parameter Default Meaning and range
exposure 0 Stops, clamped −8–8: +1 doubles brightness before later steps.
contrast 1 Multiplier around middle gray, clamped 0–4; 1 unchanged.
saturation 1 Clamped 0–4; 0 grayscale, 1 unchanged.
temperature 0 Clamped −1–1; positive warms red relative to blue, negative cools.
tint 0 Clamped −1–1; positive favors magenta, negative favors green.
brightness 0 Added RGB offset, clamped −1–1.
gamma 1 Clamped 0.1–8. 1 unchanged; above 1 lifts midtones because the exponent is 1/gamma.
hue 0 Hue offset in turns; 1 completes a cycle.
amount 1 Wet/dry amount, clamped to 0–1: 0 keeps the incoming image; 1 applies the full treatment.
new ShaderChain().gradeColor({ exposure: 0.2, contrast: 1.1, saturation: 0.9, temperature: 0.15 });

duotone

Map luminance between a dark color and a light color.

.duotone({ dark, light, midpoint, softness, amount }) — Explicit ShaderChain only.

Parameter Default Meaning and range
dark [0, 0, 0, 1] Normalized RGBA array for dark pixels; RGB is used, alpha is ignored.
light [1, 1, 1, 1] Normalized RGBA array for light pixels; RGB is used, alpha is ignored.
midpoint 0.5 Center of the luminance transition, clamped 0–1.
softness 1 Full luminance transition width, clamped 0–2; near 0 makes a hard two-color split.
amount 1 Wet/dry amount, clamped to 0–1: 0 keeps the incoming image; 1 applies the full treatment.
new ShaderChain().duotone({ dark: [0.05, 0.02, 0.1, 1], light: [1, 0.7, 0.3, 1] });

posterizeDither

Reduce RGB to a specified number of levels with optional ordered dithering.

.posterizeDither({ levels, gamma, dither, amount }) — Explicit ShaderChain only.

Parameter Default Meaning and range
levels 6 Output levels per channel, floored and clamped 2–64.
gamma 1 Quantization exponent, clamped 0.1–8, reversed after quantization.
dither 0 Ordered dither strength, clamped 0–1. 0 clean bands; 1 breaks up bands with a small repeating pattern.
amount 1 Wet/dry amount, clamped to 0–1: 0 keeps the incoming image; 1 applies the full treatment.
new ShaderChain().posterizeDither({ levels: 5, gamma: 1, dither: 0.6 });

Texture and surface

surfaceGrain

Add fine monochrome or colored grain without changing alpha.

.surfaceGrain({ strength, size, colored, seed, phase, amount }) — Explicit ShaderChain only.

Parameter Default Meaning and range
strength 0.12 Noise amplitude, clamped 0–1. Start around 0.03–0.15.
size 1 Grain cell size in pixels, clamped 1–64.
colored 0 Clamped 0–1: 0 monochrome, 1 independent noise per RGB channel.
seed 1 Repeatable numeric seed. Change it to choose a different noise or random arrangement.
phase 0 Noise evolution coordinate; fixed holds still. Animate or step it for moving grain.
amount 1 Wet/dry amount, clamped to 0–1: 0 keeps the incoming image; 1 applies the full treatment.
new ShaderChain().surfaceGrain({ strength: 0.08, phase: ({ time }) => Math.floor(time * 12) });

halftone

Render luminance as ink dots or lines on paper, retaining the incoming alpha.

.halftone({ size, angle, mode, softness, offsetX, offsetY, ink, paper, amount }) — Explicit ShaderChain only.

Parameter Default Meaning and range
size 12 Halftone cell size in pixels, clamped 2–128.
angle 0.7853981634 Grid rotation in radians.
mode 0 0 dots; 1 lines.
softness 0.75 Edge softness in pixels, clamped 0–4.
offsetX 0 Horizontal offset of the rotated grid in pixels.
offsetY 0 Vertical offset of the rotated grid in pixels.
ink [0.05, 0.07, 0.12, 1] Normalized RGBA array for the printed marks; RGB is used, alpha is ignored.
paper [1, 0.96, 0.86, 1] Normalized RGBA array for the background color; RGB is used, alpha is ignored.
amount 1 Wet/dry amount, clamped to 0–1: 0 keeps the incoming image; 1 applies the full treatment.
new ShaderChain().halftone({ size: 10, angle: Math.PI / 4, mode: 0 });

surfaceDither

Convert luminance to ink and paper with ordered or random thresholds.

.surfaceDither({ mode, size, spread, gamma, seed, phase, ink, paper, amount }) — Explicit ShaderChain only.

Parameter Default Meaning and range
mode 0 0 ordered 4×4 pattern; 1 random thresholds.
size 1 Dither cell size in pixels, clamped 1–32.
spread 1 Threshold variation, clamped 0–2: 0 a plain 0.5 cutoff; 1 normal distribution.
gamma 1 Luminance exponent, clamped 0.1–8. Above 1 darkens the decision signal.
seed 1 Repeatable numeric seed. Change it to choose a different noise or random arrangement. Used only by random mode.
phase 0 Noise coordinate for random mode; fixed holds still. Ordered mode ignores it.
ink [0, 0, 0, 1] Normalized RGBA array for dark pixels; RGB used, alpha ignored.
paper [1, 1, 1, 1] Normalized RGBA array for light pixels; RGB used, alpha ignored.
amount 1 Wet/dry amount, clamped to 0–1: 0 keeps the incoming image; 1 applies the full treatment.
new ShaderChain().surfaceDither({ mode: 0, size: 2, spread: 1 });

emboss

Turn directional luminance differences into a relief-like surface.

.emboss({ radius, angle, strength, bias, colorize, tint, amount }) — Explicit ShaderChain only.

Parameter Default Meaning and range
radius 2 Neighbor offset in pixels, clamped 0–32.
angle 2.35619449 Direction of the light/dark sample pair in radians.
strength 2 Relief contrast, clamped 0–8.
bias 0.5 Base brightness before relief, typically 0–1; final relief is clamped.
colorize 0.2 Clamped 0–1: 0 shaded tint, 1 source colors multiplied by the relief.
tint [1, 1, 1, 1] Normalized RGBA array; RGB tints the relief, alpha ignored.
amount 1 Wet/dry amount, clamped to 0–1: 0 keeps the incoming image; 1 applies the full treatment.
new ShaderChain().emboss({ radius: 2, strength: 2, colorize: 0.3 });

Video damage and feedback

videoNoise

Combine a low-resolution sample grid with stepped grain, scanlines, and occasional line dropouts.

.videoNoise({ amount, rate, width, height, seed }) — Also available on arrays.

Parameter Default Meaning and range
amount 0.35 Wet/dry amount, clamped to 0–1: 0 keeps the incoming image; 1 applies the full treatment.
rate 24 Noise updates per second; minimum 0.01. Setting 0 does not fully freeze it.
width 320 Horizontal sampling cell count; absolute magnitude, minimum 1.
height 240 Vertical sampling cell count; absolute magnitude, minimum 1.
seed 1 Repeatable numeric seed. Change it to choose a different noise or random arrangement.
new ShaderChain().videoNoise({ amount: 0.3, width: 320, height: 240, rate: 24 });

vhrDistortion

Simulate a curved video display with tearing bursts, occasional vertical rolls, scanlines, and edge shading.

.vhrDistortion({ amount, rate, bend, roll, seed }) — Also available on arrays.

Parameter Default Meaning and range
amount 0.45 Overall damage level, clamped 0–1; 0 unchanged.
rate 6 Random event updates per second; minimum 0.01.
bend 0.18 Screen curvature, clamped −0.5–0.5; negative reverses curvature.
roll 0.3 Vertical roll strength, clamped 0–1; rolls occur intermittently.
seed 1 Repeatable numeric seed. Change it to choose a different noise or random arrangement.
new ShaderChain().vhrDistortion({ amount: 0.4, bend: 0.12, roll: 0.2 });

vhsTape

Add tape drift, a traveling crease, head-switching near the bottom, color bleed, and brightness variation.

.vhsTape({ amount, tracking, bleed, rate, seed }) — Also available on arrays.

Parameter Default Meaning and range
amount 0.5 Wet/dry amount, clamped to 0–1: 0 keeps the incoming image; 1 applies the full treatment.
tracking 0.6 Tracking deformation strength, clamped 0–1.
bleed 0.45 Color-bloom strength, clamped 0–1.
rate 12 Animation rate relative to the default 12: 6 is half-speed, 24 double-speed. Minimum 0.01.
seed 1 Repeatable numeric seed. Change it to choose a different noise or random arrangement.
new ShaderChain().vhsTape({ amount: 0.35, tracking: 0.5, bleed: 0.3 });

The color-bleed implementation takes multiple texture samples, even when its contribution is low.

tvDistortion

Mix independently adjustable television interference treatments.

.tvDistortion({ amount, vertJerk, vertMovement, bottomStatic, scanlines, rgbOffset, horzFuzz, rate, seed }) — Explicit ShaderChain only.

Parameter Default Meaning and range
amount 1 Wet/dry amount, clamped to 0–1: 0 keeps the incoming image; 1 applies the full treatment.
vertJerk 1 Sudden vertical jumps, clamped 0–1.
vertMovement 1 Intermittent vertical motion, clamped 0–1.
bottomStatic 1 Static near the lower picture, clamped 0–1.
scanlines 1 Scanline strength, clamped 0–1.
rgbOffset 1 Horizontal RGB separation strength, clamped 0–1.
horzFuzz 1 Horizontal wave/fuzz strength, clamped 0–1.
rate 1 Time multiplier; 1 normal speed, minimum 0.01.
seed 1 Repeatable numeric seed. Change it to choose a different noise or random arrangement.
new ShaderChain().tvDistortion({ amount: 0.4, vertMovement: 0, bottomStatic: 0.2 });

glasses3D

Make a red/cyan anaglyph treatment from a single image.

.glasses3D({ amount, separation, depth }) — Explicit ShaderChain only.

Parameter Default Meaning and range
amount 1 Wet/dry amount, clamped to 0–1: 0 keeps the incoming image; 1 applies the full treatment.
separation 8 Horizontal offset for each eye sample in pixels; minimum 0. The two samples are twice this distance apart at depth 0.
depth 0 Brightness-based variation of the offset, clamped 0–1. Brighter regions separate more.
new ShaderChain().glasses3D({ separation: 5, depth: 0.4 });

This is a stylized image treatment, not stereo geometry or a true depth map.

glitch

Create intermittent stepped bursts of displacement, RGB separation, crushing, corrupted blocks, and analog interference.

.glitch({ amount, rate, bands, blockPixels, seed, trigger, displace, channels, crush, corrupt, analog }) — Also available on arrays.

Parameter Default Meaning and range
amount 0.35 Damage strength and random burst likelihood, clamped 0–1. 0 disables damage, even when trigger is high.
rate 6 Random pattern updates per second; minimum 0.01.
bands 24 Horizontal band count; absolute value is floored and clamped 2–512.
blockPixels 16 Corruption block size in pixels; absolute magnitude, minimum 2.
seed 1 Repeatable numeric seed. Change it to choose a different noise or random arrangement.
trigger 0 A value at least 0.5 forces a burst while held. 0 still allows random bursts when amount is nonzero.
displace 1 Band displacement contribution, clamped 0–1.
channels 0.7 RGB separation contribution, clamped 0–1.
crush 0.45 Color quantization contribution, clamped 0–1.
corrupt 0.6 Block corruption contribution, clamped 0–1.
analog 0.25 Vertical drift, scanline, and edge-static contribution, clamped 0–1.
new ShaderChain().glitch({ amount: ({ audio }) => audio.bass * 0.6, rate: 8 });

For only deliberate beat hits, drive amount to 0 between hits. The trigger alone is not an on/off gate.

datamosh

Replace selected blocks with displaced pixels from the previous chain output.

.datamosh({ amount, rate, blockPixels, seed, trigger, motion }) — Also available on arrays.

Parameter Default Meaning and range
amount 0.55 Temporal replacement strength and burst likelihood, clamped 0–1. Below 0.0001 leaves the current picture.
rate 5 Random pattern updates per second; minimum 0.01.
blockPixels 24 Block size in pixels; absolute magnitude, minimum 2.
seed 1 Repeatable numeric seed. Change it to choose a different noise or random arrangement.
trigger 0 At least 0.5 forces a burst while held; 0 still allows random bursts.
motion 1 Previous-frame sampling drift in block-size units, clamped 0–4. 0 uses stationary held blocks.
new ShaderChain().datamosh({ amount: 0.4, blockPixels: 24, motion: 1 });

Needs previous-frame history; a fresh binding starts without old imagery. This is a visual simulation, not actual codec corruption.

feedback

Blend toward the brighter of the current picture and the previous chain output.

.feedback(amount, decay, zoom) — Also available on arrays.

Parameter Default Meaning and range
amount 0.55 Previous-frame influence, clamped 0–1. 0 shows the current picture only.
decay 0.96 Multiplier applied to previous-frame RGB, not alpha. Start around 0.85–0.99; 1 retains brightness, above 1 can brighten repeatedly.
zoom 1.005 Previous-frame magnification around center; absolute magnitude, minimum 0.0001. 1 no zoom; try 1.002–1.02 for outward trails.
new ShaderChain().feedback(0.6, 0.96, 1.005);

This is a brightening/max feedback blend, not a conventional crossfade or a frame delay. It retains one previous output texture; parameters act every frame, so perceived decay depends on frame rate.

Combining two images

modulate

Use the red and green channels of a private image to displace the current one.

.modulate(image, amount) — Also available on arrays.

Parameter Default Meaning and range
image Required Required array of patches, including nested layers. R controls X, G controls Y, 0.5 is neutral; alpha scales displacement.
amount 0.1 Normalized displacement strength; 0 unchanged. With 0.1, each axis moves by at most 5% of its dimension. Negative reverses it.
new ShaderChain().modulate([displacementMap], 0.05);

The input is rendered privately and is not displayed as another parent layer. Transparent input is neutral. Sample coordinates wrap. Positive red/green displacement makes features appear left/up.

add

Add the second image’s RGB as light.

.add(image, amount) — Also available on arrays.

Parameter Default Meaning and range
image Required Required private array of patches. Its alpha determines where it contributes.
amount 1 Blend strength, clamped 0–1. 0 unchanged, 1 full contribution.
new ShaderChain().add([secondImage], 0.75);

multiply

Multiply the two images’ RGB. White is neutral; black darkens.

.multiply(image, amount) — Also available on arrays.

Parameter Default Meaning and range
image Required Required private array of patches. Its alpha determines where it contributes.
amount 1 Blend strength, clamped 0–1. 0 unchanged, 1 full contribution.
new ShaderChain().multiply([secondImage], 0.75);

screen

Screen the second image over the first, usually brightening. Black is neutral.

.screen(image, amount) — Also available on arrays.

Parameter Default Meaning and range
image Required Required private array of patches. Its alpha determines where it contributes.
amount 1 Blend strength, clamped 0–1. 0 unchanged, 1 full contribution.
new ShaderChain().screen([secondImage], 0.75);

overlay

Apply an overlay blend: dark base pixels multiply, light base pixels screen.

.overlay(image, amount) — Also available on arrays.

Parameter Default Meaning and range
image Required Required private array of patches. Its alpha determines where it contributes.
amount 1 Blend strength, clamped 0–1. 0 unchanged, 1 full contribution.
new ShaderChain().overlay([secondImage], 0.75);

difference

Use the absolute RGB difference. Identical colors become black.

.difference(image, amount) — Also available on arrays.

Parameter Default Meaning and range
image Required Required private array of patches. Its alpha determines where it contributes.
amount 1 Blend strength, clamped 0–1. 0 unchanged, 1 full contribution.
new ShaderChain().difference([secondImage], 0.75);

subtract

Subtract the second image’s RGB from the first.

.subtract(image, amount) — Also available on arrays.

Parameter Default Meaning and range
image Required Required private array of patches. Its alpha determines where it contributes.
amount 1 Blend strength, clamped 0–1. 0 unchanged, 1 full contribution.
new ShaderChain().subtract([secondImage], 0.75);

lighten

Keep the lighter value of each channel.

.lighten(image, amount) — Also available on arrays.

Parameter Default Meaning and range
image Required Required private array of patches. Its alpha determines where it contributes.
amount 1 Blend strength, clamped 0–1. 0 unchanged, 1 full contribution.
new ShaderChain().lighten([secondImage], 0.75);

darken

Keep the darker value of each channel.

.darken(image, amount) — Also available on arrays.

Parameter Default Meaning and range
image Required Required private array of patches. Its alpha determines where it contributes.
amount 1 Blend strength, clamped 0–1. 0 unchanged, 1 full contribution.
new ShaderChain().darken([secondImage], 0.75);

mask

Multiply the first image’s alpha by the second image’s luminance times alpha.

.mask(image, amount) — Also available on arrays.

Parameter Default Meaning and range
image Required Required private array of patches. Its alpha determines where it contributes.
amount 1 Blend strength, clamped 0–1. 0 unchanged, 1 full contribution.
new ShaderChain().mask([secondImage], 0.75);

A transparent or empty mask hides the source at amount 1.

alphaMask

Multiply the first image’s alpha by the second image’s alpha, ignoring color.

.alphaMask(image, amount) — Also available on arrays.

Parameter Default Meaning and range
image Required Required private array of patches. Its alpha determines where it contributes.
amount 1 Blend strength, clamped 0–1. 0 unchanged, 1 full contribution.
new ShaderChain().alphaMask([secondImage], 0.75);

A transparent or empty mask hides the source at amount 1.

composite

Composite a private image over preceding pixels with a named mode.

.composite(image, mode, amount) — Also available on arrays.

Parameter Default Meaning and range
image Required Required private array of patches.
mode "alpha" alpha, add, multiply, screen, overlay, difference, subtract, lighten, or darken. Aliases: normal → alpha; mult → multiply.
amount 1 Input contribution, clamped 0–1.
new ShaderChain().composite([secondImage], 'alpha', 0.75);

Procedural sources

metaballField

Generate shaded blobs that merge when their fields overlap, composited over the incoming picture.

.metaballField({ count, radius, threshold, softness, phase, seed, x, y, scale, angle, spreadX, spreadY, variation, red, green, blue, shadowRed, shadowGreen, shadowBlue, opacity, lightAngle, lightStrength, gloss, rim }) — Also available on arrays.

Parameter Default Meaning and range
count 5 Blob count, floored and clamped 0–16. 0 hides the source.
radius 0.12 Base blob radius in shorter-edge fractions. 0 or less hides it.
threshold 1.25 Field cutoff, minimum 0.01. Higher values shrink/separate blobs; lower values merge them.
softness 0.08 Field-threshold transition half-width, minimum 0.002.
phase 0 Motion coordinate for blob trajectories. Fixed holds still; use a callback to animate.
seed 3 Repeatable numeric seed. Change it to choose a different noise or random arrangement.
x 0.5 Center X as a fraction of canvas width: 0 left, 0.5 center, 1 right.
y 0.5 Center Y as a fraction of canvas height: 0 top, 0.5 center, 1 bottom.
scale 1 Overall field magnification. Positive values enlarge; 0 or less hides the field.
angle 0 Radians. 0 is the initial orientation; π/2 is a quarter turn. Animate with a callback.
spreadX 0.26 Horizontal distribution extent in shorter-edge fractions, before overall scale.
spreadY 0.22 Vertical distribution extent in shorter-edge fractions, before overall scale.
variation 0.3 Radius variation among blobs, clamped 0–0.9.
red 0.25 Base red channel, normalized 0–1.
green 0.85 Base green channel, normalized 0–1.
blue 0.75 Base blue channel, normalized 0–1.
shadowRed 0.06 Shadow red channel, normalized 0–1.
shadowGreen 0.2 Shadow green channel, normalized 0–1.
shadowBlue 0.3 Shadow blue channel, normalized 0–1.
opacity 0.95 Source opacity, clamped 0–1.
lightAngle -0.8 Lighting direction in radians.
lightStrength 0.65 Blend from flat color to shaded color, clamped 0–1.
gloss 0.4 Specular highlight strength, clamped 0–1.
rim 0.2 Edge highlight strength, clamped 0–1.
new ShaderChain().metaballField({ count: 6, phase: ({ time }) => time * 0.4 });

More blobs add field calculations per pixel. Keep phase fixed for a still frame rather than rebuilding the chain.

atmosphere

Generate layered noise as clouds, fog, texture, or wisps, composited over the incoming picture.

.atmosphere({ mode, frequency, octaves, roughness, lacunarity, seed, phase, offsetX, offsetY, stretch, direction, warp, billow, coverage, softness, density, contrast, lightAngle, lightStrength, horizon, heightFalloff, breakup, tint, shadow, opacity }) — Also available on arrays.

Parameter Default Meaning and range
mode 0 0 lit clouds; 1 fog with vertical falloff; 2 noise texture; 3 wisps.
frequency 3 Noise scale, absolute magnitude clamped 0.05–64. Higher makes finer structures.
octaves 5 Noise layers, floored and clamped 1–6. More layers add detail and computation.
roughness 0.55 Amplitude retained per finer octave, clamped 0–1.
lacunarity 2 Frequency multiplier per octave, clamped 1.01–4.
seed 1 Repeatable numeric seed. Change it to choose a different noise or random arrangement. Reduced modulo 4096 internally.
phase 0 Noise evolution coordinate. Fixed holds still; try ({ time }) => time * 0.08.
offsetX 0 Horizontal offset of the field in shorter-edge coordinate units.
offsetY 0 Vertical offset of the field in shorter-edge coordinate units.
stretch 1 Field stretch along its local X axis; absolute magnitude, minimum 0.05.
direction 0 Orientation of the field/stretch axis in radians.
warp 0.5 Noise-flow distortion, clamped −4–4. 0 skips the extra warp-noise calculations.
billow 0 Clamped 0–1. 0 ordinary noise; 1 folded, billowy noise.
coverage 0.55 Clamped 0–1. 0 no coverage, 1 complete threshold coverage; larger values fill more area.
softness 0.2 Coverage transition width, clamped 0–1.
density 1 Optical density, clamped 0–8. 0 transparent; larger values thicken the source.
contrast 1 Noise-field exponent, clamped 0.1–8. Larger values favor stronger peaks.
lightAngle -0.8 Cloud lighting direction in radians; used in mode 0.
lightStrength 0.7 Cloud shading strength, clamped 0–1. 0 skips the neighboring light-field sample.
horizon 0.25 Vertical fog-falloff start as a fraction of canvas height; used in mode 1.
heightFalloff 0 Vertical ramp width in canvas-height fractions; 0 disables the fog gradient. With a positive value, density increases below horizon.
breakup 0 Large-scale fragmentation strength, clamped 0–1; 0 skips the extra breakup-noise calculation.
tint [0.96, 0.97, 1, 1] Normalized RGBA highlight color. RGB colors the source; alpha multiplies opacity.
shadow [0.32, 0.4, 0.53, 1] Normalized RGBA shadow color. RGB is used; alpha is ignored.
opacity 0.8 Overall source opacity, clamped 0–1.
new ShaderChain().atmosphere({ mode: 0, coverage: 0.55, phase: ({ time }) => time * 0.08 });

These modes share one operator. Animate phase, offsetX, and offsetY with callbacks for moving clouds, fog, noise textures, or wisps. Noise octave count, warp, lighting, and breakup affect work per pixel.

Chain controls and inspection

These belong to an explicit new ShaderChain(), rather than an array layer.

Method or property Default and behavior
new ShaderChain(operations = []) Start empty. Advanced callers may supply internal { name, args } records; normal authoring uses the named methods above.
.mix(amount = 1) Wet/dry blend against the chain's original input, clamped 0–1 when rendered. Accepts a number or context callback. 0 dry, 1 wet. A zero mix does not remove the planned shader work.
.blend(mode = 'alpha') Blend the completed effect against its original input. Modes: alpha, add, multiply, screen, overlay, difference, subtract, lighten, darken. Aliases: normal, mult, diff. Takes a string, not a callback.
.bypass(enabled = true) Skip the chain's drawing while bypassed; .bypass(false) resumes. Takes a boolean, not a callback. Private image-input occurrences can still run.
.clear() Remove all operations. Rebuild the desired chain during code evaluation.
.clone() New chain with copied operations and chain settings, with separate GPU resources. Referenced image arrays and callback functions remain references.
.operations Copied operator records for inspection. Changing the returned list does not edit the chain.
.bypassed Read the current bypass state.
.passCount Planned pass count, not measured rendering time; even an empty chain reports 1. Use bypass when you want to skip the chain draw.
.imageInputs Private texture dependencies as { uniform, source } records.
.withImageInputs(map) Advanced host-recovery helper that clones with replacement sources keyed by uniform name. Use ordinary input arrays when authoring.
.draw(context) / .dispose() Host lifecycle methods. The instrument calls these to render and release resources; do not call them from a parameter callback.

.blend('screen') combines the completed effect with its original input. .screen([anotherPatch]) combines the current image with a separate private image input. .mix() applies once to the whole chain; an individual operator's amount affects that operator.

Array-only helpers include .translate(x = 0, y = 0), .opacity(amount = 1), .append(...patches), .fx(...effects), and .mute(enabled = true). See Layer composition for their scene behavior. Use .colorShift() on arrays; .shift() remains JavaScript's native array operation.

Order scope and transparency

Start with a short chain and add one effect at a time. .pixelate(24, 14).blur(3) softens the pixel blocks; .blur(3).pixelate(24, 14) gives hard blocks sampled from a softened picture. A key or mask changes alpha, so it reveals a lower layer only if that lower layer is outside the processed group.

const keyedLook = new ShaderChain()
  .chromaKey(0, 1, 0, 0.18, 0.08)
  .alphaGlow({ radius: 16, strength: 0.6 });
const scene = [replacementBackground, [greenScreenSource, keyedLook]];
scene.activate();

Here replacementBackground and greenScreenSource stand for your own patches. A black background is opaque: it hides layers underneath. For a halo around a subject, keep its group transparent and draw the background outside that group.

Private image arguments must be arrays, such as .screen([secondImage], 0.5). Their patches receive the frame context but render into their own target. Changing the input structure requires reevaluating the consumer scene. Do not create cycles between image inputs. Use distinct bindings for effects that should have independent temporal histories.

Performance while playing

Compatible operations can share a shader pass, but neighborhood filters and image-input operations introduce boundaries when earlier operations need to be materialized. A one-pass effect can still be expensive if it evaluates many noise octaves, copies, or texture taps. Count, depth, samples, and octaves are useful quality controls as well as visual parameters.

At show resolution, check a cold launch and several warmed returns through your actual scenes. First use may compile shaders; a warmed return can reuse resources. Test with Push pad launches when available, and watch occasional stalls as well as typical latency. Headless browser results do not measure physical pad-to-display latency.

Keep chains and image inputs stable while playing; animate numbers through callbacks. Reduce resolution or simplify expensive chains if sustained frame rate drops. A smaller amount often changes appearance without removing the sampling work. Bypass can skip the chain draw; private inputs have their own lifecycle. Feedback and datamosh need a previous-frame texture; more independent bindings mean more retained history while active. The host bounds reusable resources and clears old feedback imagery when resources change owners.

Troubleshooting

Symptom Check
Nothing changes Place the chain after visible drawing in the same group; check mix, bypass, and each effect's neutral value.
Everything disappears Inspect masks, opacity, window dimensions, crop bounds, and missing private inputs.
A glow fills a rectangle The input alpha is probably opaque across the whole layer; move its background outside the glow group.
A parameter seems far too large Check pixels versus fractions, radians versus degrees, and normalized 0–1 color channels.
Glitches appear between intended hits The trigger does not disable random bursts; set amount to 0 between hits.
A callback fails Confirm its control/modulation exists and that it returns a finite number or the required array length.
The analyzer line returns to the center Auto scale fits recent values. Select Scale → 1× to keep a control's full range fixed; the held control value has not necessarily changed.
A method is unavailable on an array Use an explicit ShaderChain for entries marked chain-only.

Imported and custom shaders

An imported ISF file defines its own inputs, so its names and defaults cannot be inferred from a built-in effect with a similar name. Inspect the imported patch's settings and ISF metadata. Thunk Machine supports the documented subset described in ISFSource; use that section for input types, limits, and file format.

For a custom WebGL patch, allocate graphics resources once, reuse them, resize when necessary, and release them in dispose(). The manual's shader chapter covers custom shader interfaces and adaptation. The defaults in this reference describe the built-in methods. For an imported shader, use the defaults in its own source.

For an optional Library patch, open its card’s Guide → Parameters or its source cell for that patch’s own settings. Patch names and units may differ from the built-in methods used inside them.

Implementation references