shader-templates/registry.ts

PURPOSE

Single source of truth for every tile-able background fragment shader the Backgrounds Workbench can bake. Maps a string TileTemplateId to a { fragGlsl } record so BackgroundDef.layers[].shader can be resolved at bake time. Fails fast on unknown ids so typos in a BackgroundDef crash at compile, not at render.

OWNS

  • TileTemplateId — string-union type of all 15 valid template ids.
  • TILE_TEMPLATESRecord<TileTemplateId, { fragGlsl: string }> keyed lookup, populated from the per-template .frag modules at module load.
  • getTileTemplate(id: string): { fragGlsl: string } — runtime resolver. Throws with the full available-id list in the message when an id is missing.

READS FROM

  • ./tile_fbm_warp.frag — domain-warped fbm.
  • ./tile_ridged_streaks.frag — sharp-crested fbm streaks.
  • ./tile_sparkle.frag — twinkle / star points.
  • ./tile_gradient.frag — directional gradient (does not tile vertically without a fold).
  • ./tile_solid.frag — flat color fill; always tiles cleanly.
  • ./tile_voronoi.frag — periodic voronoi cells.
  • ./tile_lines.frag — banded lines.
  • ./tile_hex.frag — hex grid.
  • ./tile_radial_burst.frag — radial spokes from a center.
  • ./tile_dots.frag — periodic dot field.
  • ./tile_curl_flow.frag — curl-noise flow streaks.
  • ./tile_cracks.frag — crack / fracture network.
  • ./tile_threshold_blobs.frag — thresholded fbm blobs.
  • ./tile_polar_swirl.frag — polar-coordinate swirl.
  • ./tile_kaleidoscope.frag — kaleidoscopic fold.

Each imported module is a default-exported GLSL #version 300 es fragment string. The shared primitives library (tile-noise.glsl.ts, TILE_NOISE_LIB) is prepended elsewhere by the baker — not by this registry.

PUSHES TO

  • Bake pipeline (bake-background.ts and downstream) consumes getTileTemplate(id).fragGlsl to produce the final compiled program per layer.
  • BackgroundDef.layers[].shader (schema) — every string used there must match a key here.

DOES NOT

  • Compile, link, or otherwise touch WebGL state.
  • Prepend TILE_NOISE_LIB or any other GLSL preamble — that is the baker’s job.
  • Validate uniforms, palette length, or layer parameters.
  • Cache or memoize — the templates are static strings already resident in module scope.
  • Provide vertex shaders. Templates are fragment-only; the baker pairs them with a shared screen-quad vert.

Signals

  • Thrown Error('Unknown tile template '<id>'. Available: <list>') is the only failure mode. Listing all available ids in the message is intentional so a bad BackgroundDef surfaces the fix immediately.

Entry points

  • getTileTemplate(id) — sole runtime API. Callers must pass a string; the function narrows internally.
  • TILE_TEMPLATES — exported for static iteration (e.g., dev tools that want to render every template into a thumbnail strip).
  • TileTemplateId — exported type for BackgroundDef authoring and type-safe content tables.

Pattern notes

  • Contract per template. Every entry must be toroidally tileable (uv ∈ [0,1] wraps) and temporally loop-closed (frame at u_time=1 equals frame at u_time=0). The registry does not enforce this — it is a contract on the shader source. Breaking it produces seams or pops in the baked tile.
  • Two waves. Five originals (tile_fbm_warp, tile_ridged_streaks, tile_sparkle, tile_gradient, tile_solid) plus 10 v2 templates added in v5.186.0 (tile_voronoi through tile_kaleidoscope). Both waves share the same record shape; there is no version field.
  • tile_solid vs tile_gradient. tile_solid is the recommended base layer when the layer must wrap top↔bottom cleanly — a vertical gradient cannot tile vertically without a sinusoidal fold.
  • String-union, not enum. TileTemplateId is a literal union so JSON content (BackgroundDef) can be authored without imports; the resolver does the unsafe cast and lets the runtime throw on miss.
  • Counts. 15 templates total, despite the task brief mentioning 16 — verify before adding a new one that the count is what you think.

EXTRACT-CANDIDATE

  • The “validate any background-def shader id against this registry at content load” check exists implicitly (it throws at bake) but could be hoisted into a content-validation pass alongside palette / layer-param checks, so a malformed BackgroundDef fails at startup rather than first-bake.
  • A thumbnail-strip dev tool that iterates Object.keys(TILE_TEMPLATES) and renders each at default uniforms would be a small, high-value Workbench addition; the registry is already shaped for it.
  • The “every template must tile + loop-close” contract is currently doc-only. A vitest suite that bakes each template at u_time=0 and u_time=1 and pixel-diffs the seams would catch contract regressions on new templates.