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_TEMPLATES—Record<TileTemplateId, { fragGlsl: string }>keyed lookup, populated from the per-template.fragmodules 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.tsand downstream) consumesgetTileTemplate(id).fragGlslto 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_LIBor 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 badBackgroundDefsurfaces the fix immediately.
Entry points
getTileTemplate(id)— sole runtime API. Callers must pass astring; 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 forBackgroundDefauthoring 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=1equals frame atu_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 inv5.186.0(tile_voronoithroughtile_kaleidoscope). Both waves share the same record shape; there is no version field. tile_solidvstile_gradient.tile_solidis 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.
TileTemplateIdis 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
BackgroundDeffails 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=0andu_time=1and pixel-diffs the seams would catch contract regressions on new templates.