palette.ts
PURPOSE
Iñigo Quílez-style cosine palette primitives for the VFX workbench. Provides the 4-vec3 palette type (Palette4), evaluation at parameter t, flatten-for-uniform helper, and hex←>vec3 round-trip conversion. Pure functions, no state, no DOM.
OWNS
Vec3type —[number, number, number], each channel 0..1.Palette4type — tuple of fourVec3rows (a, b, c, d) matching the cosine-palette formulacolor(t) = a + b * cos(2π * (c*t + d)).DEFAULT_PALETTE— Quílez canonical neutral grey-to-rainbow palette: a=(0.5, 0.5, 0.5), b=(0.5, 0.5, 0.5), c=(1, 1, 1), d=(0, 0.33, 0.67).
READS FROM
- Nothing. Module is dependency-free.
PUSHES TO
- Nothing. Consumers pull the helpers.
DOES NOT
- Does not bind a WebGL uniform —
flattenPaletteonly produces the 12-number array; the caller uploads it. - Does not validate
Palette4shape; trusts the type. - Does not clamp inputs to
evalPalette(output may exceed 0..1 for extremeb). - Does not handle 3-digit shorthand hex, alpha channel, or named CSS colors —
hexToVec3requires exactly 6 hex digits after stripping#and throws otherwise. - Does not normalize parameter
t(caller decides if it wraps, loops, or extrapolates).
Signals
- Throws
Error('invalid hex: <hex>')whenhexToVec3receives a string whose post-#length is not 6. vec3ToHexsilently clamps each channel to 0..1 before rounding to 0..255 — out-of-range input is folded, not flagged.
Entry points
flattenPalette(p: Palette4): number[]— spreads the 4 rows into a flat 12-number array forgl.uniform3fv/ shader upload.evalPalette(p: Palette4, t: number): Vec3— sample the palette at scalart; applies the cosine-palette formula per channel.vec3ToHex(v: Vec3): string—[0..1, 0..1, 0..1]→#rrggbb(clamps, rounds, pads to 2 hex digits).hexToVec3(hex: string): Vec3—#rrggbborrrggbb→[0..1, 0..1, 0..1].DEFAULT_PALETTE— exported constant, safe to use as a seed value.
Pattern notes
- Cosine palette math:
color(t) = a + b * cos(2π * (c*t + d)).ais base offset,bis amplitude,cis frequency,dis per-channel phase. The default values produce a smooth rainbow astsweeps 0..1. flattenPaletteorder is row-major (a.xyz, b.xyz, c.xyz, d.xyz) — matches the layout a shader expects when readinguniform vec3 palette[4].vec3ToHexclamps before rounding so callers don’t have to pre-clamp;evalPalettedoes not, so the two are not symmetric round-trip unless inputs stay in range.- Hex round-trip is lossy: a vec3 holds floats, hex holds 8-bit channels.
hexToVec3(vec3ToHex(v))only equalsvup to 1/255 per channel.
EXTRACT-CANDIDATE
Vec3type is duplicated across many engine modules (rendering, vfx, particles). A sharedengine/types/vec.tswould deduplicate, but the[number, number, number]alias is so trivial that local declaration is fine.vec3ToHex/hexToVec3are general-purpose color conversion utilities that could live in a sharedengine/color/module if any other system grows hex-conversion needs. Today they are only used by the workbench palette UI.- The cosine-palette formula (
a + b * cos(2π * (c*t + d))) is the Iñigo Quílez canonical form — well-known enough that re-implementations elsewhere would also follow this shape and could shareevalPalette.