Shaders¶
Noisemaker’s GPU rendering engine. Dual WebGL2 and WebGPU backends, 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 internal texture in
textures: { _temp: { ... } }Pass 1 writes to
_tempPass 2 reads from
_temp, 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: The manifest must be regenerated whenever you add or remove effect files, or modify 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.