Grid programs
A grid simulation is a fixed-size field of cells (a displacement per cell, two chemical
concentrations, a sort offset) that changes a little every frame. gridSim runs it: you
describe the frame as an ordered list of stages built with op.*, and the engine runs that
list every frame, owning the clock, the frame-delta clamp, the ready gate, the settle gate and
the outputs the layer samples. A stage is bookkeeping (op.host, op.values, op.readyWhen,
op.settle) or work (op.pass, op.cache, op.seedOnce, op.iterate, op.sortPass), and
every frame ends with op.publish, which writes the state into the texture the fragment reads.
The build callback runs once per instance of the layer and returns the outputs, the clamp and
the stages; anything it creates (buffers, passes, trackers) belongs to that instance. A settled
field costs nothing: once op.settle sees nothing driving it, frames are skipped and the last
published picture stays on screen.
Two shortcuts sit on top. The pointer fields in effects.pointerFields are whole grid programs
you spread as ...gridSim(springLatticeField({...})) and read with a warp map. And for a small
field whose arithmetic reads better as JavaScript loops, hostGridProgram runs the same named
steps on the CPU and hostFieldTexture uploads the cells as a texture. When a wave field and a
displacement are all you need, declare them with simulate.grid instead.
import {sim} from 'shaders/std'
Reach for it when
| When you need | Use |
|---|---|
| liquid or fabric that ripples after the cursor pushes it | gridSim with springLatticeField and a warp map |
| a rule that must run N times per frame (reaction-diffusion) | op.iterate after op.values, then op.publish |
| a sort that settles a little more every frame | op.sortPass, with op.publish reading its side |
| the state must be reset on the first frame or a seed change | op.seedOnce |
| a pass that needs the child's picture, which arrives late | op.readyWhen and op.cache |
| the simulation should sleep once nothing drives it | op.settle |
| this frame's values written before the passes read them | op.values |
| the canvas size or aspect inside a stage | trackedViewport |
| a small field stepped in plain JavaScript | hostGridProgram with hostStep, published by hostFieldTexture |
Example
import {defineShader, p, sim, effects, warps, transformEdges} from 'shaders/std'
const {gridSim} = sim.grids
const {springLatticeField} = effects.pointerFields
// Liquify: a spring lattice the cursor pushes, published as a displacement field the warp map reads.
// The pointer field packages the whole stage list (pointer tracking, values, the step passes, the
// settle gate, publish); this definition only names the props and the output.
export const Liquify = defineShader({
name: 'Liquify',
usesPointer: true,
props: {
intensity: {default: 10},
stiffness: {default: 3},
damping: {default: 3},
radius: {default: 1},
edges: {default: 'stretch', transform: transformEdges, compileTime: true},
},
...gridSim(springLatticeField({
stiffness: p('stiffness'), damping: p('damping'), radius: p('radius'),
output: 'displacement',
})),
// Bend the incoming coordinate by the field, scaled by intensity, then handle the edges.
map: warps.liquidDisplace({intensity: p('intensity'), output: 'displacement'}),
// Before the field exists (no GPU yet), the warp passes the layer through untouched.
uvRemapIdentityWhen: ({computeOutputs}) => !computeOutputs?.displacement,
})
Types
| Type | What it is |
|---|---|
GridFrame | One frame, as every stage sees it: the clamped delta, the clock, and your props. |
GridFrameParams | What the renderer reports each frame: the pointer in uv, the frame delta in seconds, the canvas size in pixels. |
GridRoot | The GPU device handle the build callback receives, for creating the buffers and passes your stages use. |
GridSimConfig | What a GPU grid simulation declares: its outputs, its frame-delta clamp, and its stages. |
GridStage | One stage of the frame. |
HostFrame | One frame of a JavaScript-stepped grid: the clamped delta, the clock and the pointer in uv. |
HostStep | One named JavaScript step. |
SortPassStage | A sort chain's stage, which also reports which of its two buffers holds the current result. |
Words
- gridSimA field of cells advanced every frame by an ordered list of stages.
- opThe stages a grid frame is built from.
- op.valuesA named step that writes this frame's values for the passes after it.
- op.passOne GPU pass, the same every frame.
- op.publishThe last stage: write the state into the texture the layer samples.
- op.iterateRun a step
counttimes this frame. - op.seedOnceA one-shot reset run before the rest of the frame whenever
staleWhensays the state is stale: on the first frame, or when a seed prop changes. - op.sortPassA compare-and-swap sort that converges a little more every frame:
passesswaps per frame, alternating odd and even pairs and the two buffers. - op.cacheA GPU pass chosen per frame, for a pass whose input is rebound after the frame starts.
- op.readyWhenRun nothing until
fnreturns true, typically once the child's picture has arrived throughbindInputs. - op.settleSleep the simulation once nothing has driven it for
settleMsof simulated time. - op.hostA named step that runs before any gate: pointer smoothing, activity flags, anything a later
settleneeds to know. - trackedViewportThe canvas size in pixels, kept current across resizes, in the two forms a stage wants.
- hostGridProgramA frame program for a small grid stepped in plain JavaScript: the steps run in order every frame until one returns
'skip'. - hostStepOne named step of a JavaScript-stepped grid.
- hostFieldTextureThe texture a JavaScript-stepped grid is read through.