PURPOSE
Pure deterministic function that turns three numeric parameters (temperature, brightness, saturation) plus an optional hue_bias and an id string into a fully resolved 7-slot Palette. All math runs in OkLCh perceptual color space so equal lightness steps read as equal brightness across hues, then converts to sRGB hex at the end. Same inputs always produce the same 7 hexes — no randomness, no state.
OWNS
generatePalette(id, params)— the sole exported function; returns aPalettewith the input params echoed plus seven hex strings.SLOT_RECIPE— the fixed internal shape of every palette: per-slotlOff(lightness offset from brightness-driven center),hueRot(degrees rotated from base hue), andcMul(chroma multiplier against the saturation parameter). Defines the contract that all three background slots rotate +180° to the complementary hue while terrain and the shadow/midtone bridge stay at the core hue.MAX_CHROMA = 0.18— the OkLCh chroma ceiling at saturation=1, chosen because higher chroma starts falling out of sRGB gamut and clips.HUE_STOPS = [220, 270, 320, 20]— the cyan→violet→magenta→red-orange arc thattemperaturewalks. The desaturated green band is intentionally skipped.hueCenter(temperature, hueBias)— resolves the base hue. IfhueBiasis set, normalizes it to 0..360 and returns it directly. Otherwise interpolates alongHUE_STOPSusing shortest-arc lerp so 320→20 crosses through red, not backward through cyan.oklchToHex(L, C, hDeg)— full OkLCh → OkLab → LMS → linear sRGB → gamma-encoded sRGB hex pipeline using Björn Ottosson’s 2020 constants.linearToSrgb(x)— clamped sRGB gamma encode (piecewise: linear below 0.0031308, power 1/2.4 above).clamp(x, lo, hi)— local utility.
READS FROM
PaletteParamsandPalettetype definitions from./palette-types.- The four input fields on
PaletteParams:temperature,brightness,saturation,hue_bias. - The
idstring passed alongside the params.
No global state, no config files, no other modules. Fully pure.
PUSHES TO
Nothing. Returns a Palette value to its caller and exits. Does not mutate inputs, does not register anywhere, does not log.
DOES NOT
- Does not apply preset overrides — that is the caller’s job (the palette system layers
PalettePreset.overrideson top of generator output). - Does not pick which palette is active or which preset to use.
- Does not cache. Every call recomputes from scratch; callers that want memoization wrap it themselves.
- Does not validate
iduniqueness, does not enforce any naming convention on it. - Does not clamp
temperatureorsaturationinputs hard — onlybrightness(clamped -1..1 inside) andsaturation(clamped 0..1 inside) and the per-slot L (clamped 0.04..0.96) get clamped. Out-of-rangetemperaturewalks off the end of the hue arc. - Does not handle the green hue band — by design, the cool→violet→magenta→red arc routes around it; reaching green requires
hue_bias. - Does not gamut-map. Slots that would exceed sRGB get clamped channel-wise in
linearToSrgb, which can flatten color near gamut edges.
Signals
None. No event emission, no telemetry, no console output, no callback registration.
Entry points
generatePalette(id: string, params: PaletteParams): Palette— the only export. Called by the palette system when resolving a preset’s parameters into final slot hexes.
Pattern notes
- Pure-function module: zero imports beyond a type-only import, zero side effects, no class wrapper. Trivial to test by comparing returned hex strings.
- Color math kept in OkLCh deliberately — comment block at top justifies the choice against HSL (HSL equal-L hues are not equal-brightness, which would wreck contrast budgets between slots).
- The 7-slot
SLOT_RECIPEis declaredas constand treated as the immutable shape of every palette. The slot loop builds each output by feeding{ lOff, hueRot, cMul }through a closure that readslCenter,c, andhfrom the outer scope. - Contrast contract is enforced via the recipe, not via runtime checks: terrain slots stay at the core hue, all three
bg_*slots rotate +180°, and the L offsets guarantee a ≥0.18 L gap betweenterrain_baseandbg_haze.shadowandmidtonesit at the core hue at low chroma to act as a tonal bridge between the terrain and its complementary backdrop. - Brightness maps linearly into a ±0.25 window around L=0.5, so the brightness parameter shifts the whole L window rather than stretching it — slot-to-slot contrast stays constant regardless of overall brightness.
- The
hue_biasescape hatch lets presets pin a specific hue (e.g. green, yellow) outside the arc; when used,temperatureis documented as biasing complementary accents, but in the current implementation hue_bias overrides hue entirely and temperature has no further effect on hue. - Hex output is lowercased and zero-padded via
padStart(2, '0'); round-trip stable.