Motion GPU supports post-processing through a post-scene render graph that sequences render passes over ping-pong buffers and optional named render targets. The same passes array also accepts compute passes and fragment-feedback passes, but those pre-scene passes are extracted before slot routing and run before the base material shader.
For named render targets, see Render Targets. For detailed compute shader documentation, see Compute Shaders. For the full pass API, see Passes API.
Pass constructors are framework-agnostic and can be imported from root/core entrypoints.
Render graph model
When you add passes to FragCanvas, the renderer builds an execution plan with three public execution groups plus final presentation:
- All enabled pre-scene passes run first.
ComputePass/PingPongComputePassdispatch compute work, andPingPongShaderPassrenders fullscreen feedback iterations into renderer-owned A/B textures. Their declaration order relative to each other is preserved. - The base shader renders your material’s
fn frag(...). If post-process render passes or a final color pipeline are enabled, it renders into an internal surface; otherwise it renders directly tocanvas. - Enabled render passes execute after the base shader, in declaration order. Each render pass reads from an input slot, processes the image, and writes to an output slot.
- The renderer presents the resolved final surface to the physical canvas. With
color.toneMapping,color.dynamicRange, orcolor.workingFormat, this final step is a private presentation pass that applies color transforms after all render passes.
Without render passes and without a color presentation pipeline, the base shader renders directly to the canvas. Compute-only and feedback-only pipelines keep that direct path. Enabling Khronos PBR Neutral, HDR presentation, or a custom workingFormat allocates an internal presentation surface so the final color transform can remain last.
Slot semantics
canvas is output-only (cannot be used as a pass input).
When a final color pipeline is active, render passes should still write output: 'canvas' for their final result. The renderer maps that logical slot to an internal rgba16float target when needed, then runs the private presentation pass to the physical swapchain. Custom passes should use context.output or context.beginRenderPass() instead of manually writing to the physical canvas.
Default ping-pong flow
By default, passes read from source, write to target, and then swap the two:
- Base shader →
source - Pass A: reads
source, writestarget→ swap → A’s output is now insource - Pass B: reads
source, writestarget→ swap → B’s output is now insource - Final:
sourceis blitted tocanvas
Non-swapping passes
If needsSwap: false, the pass writes to its output slot without swapping. This is useful for the final pass that writes directly to canvas:
You can also route through named targets:
Validation rules
planRenderGraph(...) validates render-pass slot usage before execution. Compute and fragment-feedback passes are kept in the plan for diagnostics and pre-scene ordering, but they do not make slots available and cannot satisfy render-pass read dependencies.
Built-in render passes
BlitPass
Fullscreen texture sample pass. Copies input to output using a fragment shader with configurable filter mode.
CopyPass
Optimized texture copy with a fullscreen-blit fallback. Attempts copyTextureToTexture when possible — a GPU-side copy that avoids shader execution entirely.
Direct copy conditions (all must be true):
clear === falsepreserve === true- Source and target are different textures
- Neither is the canvas texture
- Same width, height, and format
When any condition fails, CopyPass falls back to an internal BlitPass.
Options are identical to BlitPass.
ShaderPass
Programmable post-process pass with custom WGSL fragment shader:
Fragment contract
The fragment must declare:
Where inputColor is the sampled result from the previous pass (or the base shader).
Hot-swapping shaders
You can change the shader at runtime:
This invalidates the pipeline cache and recompiles the shader module on next render.
Options
Same as BlitPass, plus:
Pass lifecycle
The renderer tracks each pass by object identity:
Multi-pass example
RenderPassContext
When implementing custom passes, the render(context) method receives:
Fragment feedback passes
PingPongShaderPass runs fullscreen fragment shaders in the pre-scene phase. It is useful for feedback effects where every pixel of a state texture is updated from the previous state: trails, reaction-diffusion, simple fluid buffers, temporal masks, and iterative displacement fields.
The target is a normal fragment-visible sampled texture declared in defineMaterial({ textures }). Do not declare it with storage: true; storage textures are for compute workflows. During feedback execution the target texture is excluded from the feedback shader’s regular material texture bind group, and the previous state is exposed through:
PingPongShaderPass has no input, output, or needsSwap options. It does not read from the post-process source slot and does not write render-graph slots; after its iterations finish, the renderer attaches the latest A/B texture view to the material target so the base scene can sample it.
Compute passes
Compute passes run GPU compute workloads from the same passes prop, but they execute in the pre-scene phase. They do not participate in slot routing and operate on storage buffers and storage textures instead.
ComputePass
Single-dispatch compute pass:
PingPongComputePass
Iterative compute pass for multi-step simulations:
Compute in the frame
Compute, fragment-feedback, and render passes coexist in the passes array:
All enabled compute and fragment-feedback passes execute before the scene render, regardless of where render passes appear in the array. Their relative order against other pre-scene passes is preserved. Enabled render passes then execute after the scene as post-processing steps, preserving their relative order against other render passes. All passes share the same command encoder and one queue submit.
For full compute shader documentation, see Compute Shaders and Storage Buffers.