Shaders¶
Noisemaker’s GPU rendering engine provides WebGL2 and WebGPU backends. It includes a composable DSL for building effect chains and a growing library of shader effects.
Project Structure¶
Shader development lives under shaders/:
shaders/
├── src/ # Runtime, compiler, and backend code
├── effects/ # Effect definitions
│ ├── synth/ # 2D generators
│ ├── filter/ # Image processors
│ ├── mixer/ # Blend/composite effects
│ ├── render/ # Rendering utilities (pointsEmit, pointsRender)
│ ├── points/ # Particle/agent simulations
│ ├── synth3d/ # 3D volumetric generators
│ ├── filter3d/ # 3D volumetric processors
│ ├── classicNoisedeck/ # Ported complex shaders
│ └── manifest.json # Auto-generated effect registry
└── tests/ # Test suites
demo/
└── shaders/ # Interactive development UI
Each effect is a directory:
effects/filter/blur/
├── definition.js # Effect definition
├── glsl/ # WebGL 2 shaders
├── wgsl/ # WebGPU shaders
└── help.md # Optional documentation
Double Buffering¶
Global surfaces (o0-o7, geo0-geo7) are double-buffered. Each has a read buffer and write buffer that swap at frame end.
Display surfaces (o0-o7) swap normally—previous frame’s write becomes current frame’s read.
State surfaces (textures with names containing xyz, vel, rgba, trail, or state) persist their bindings for simulation continuity.
See shaders/src/runtime/pipeline.js, specifically swapBuffers() and the surface creation code around line 376.
Multi-Pass Effects¶
Use internal textures (prefixed with _) to chain passes.
Example: filter/blur uses _blurTemp as intermediate storage between horizontal and vertical blur passes.
Pattern:
Define an internal texture in
textures: { _temp: { ... } }Pass 1 writes to
_tempPass 2 reads from
_tempand writes tooutputTex
Feedback Effects¶
Read from previous output by copying to an internal texture each frame.
Example: filter/feedback uses _selfTex:
Main pass reads
inputTex+_selfTex, writes tooutputTexCopy pass copies
outputTexto_selfTexfor next frame
Iteration¶
Use repeat: "uniformName" to run a pass multiple times per frame.
Example: synth/rd uses repeat: "iterations" on its simulate pass. The pipeline handles buffer swapping when a pass reads and writes the same texture.
Agent-Based Effects¶
Shared global textures pass state between effects in a chain.
Naming: Textures prefixed with global_ are shared across effects.
Example chain: pointsEmit().physarum().pointsRender()
pointsEmitcreatesglobal_xyz,global_vel,global_rgbaphysarumreads and updates these texturespointsRenderusesglobal_xyzto scatter points toglobal_points_trail
MRT (Multiple Render Targets): Use drawBuffers: N for passes writing multiple textures.
Points rendering: Use drawMode: "points" and blend: true for scatter operations.
See: render/pointsEmit, render/pointsRender, points/physarum
Running Tests¶
npm run test:shaders # All shader tests
npm run test:shaders:render # Both backends
# Test harness for specific effects
node shaders/tests/test-harness.js --effects synth/noise --backend webgl2
node shaders/tests/test-harness.js --effects "synth/*" --backend webgpu
Development Workflow¶
npx http-server -p 8000
open http://localhost:8000/demo/shaders/
Regenerate manifest after adding/removing effects or changing texture definitions:
python shaders/scripts/generate_shader_manifest.py
When to regenerate: Regenerate the manifest after any of these changes:
Adding effect files
Removing effect files
Changing texture definitions in
definition.js
The manifest tracks all effects and their texture requirements for the runtime.
Bundle for distribution:
npm run bundle:shaders
Downstream Integration¶
Consumer projects vendor the built bundles from dist/.
Effect bugs and new effects belong in this repository. UI customization belongs downstream.