@vshaders/color

OKLab and OKLCH conversions, perceptual color mixing, and procedural palettes as pure WGSL modules. Why OKLab: naive RGB interpolation dips through gray in the middle of a gradient; interpolating in OKLab keeps perceived lightness and vividness even. It is the same reasoning behind CSS oklch(), in your shader.

All conversions operate on linear-light sRGB. Cross into and out of encoded sRGB with srgbToLinear3 / linearToSrgb3 from @vgpu/wgsl-std/color: this package composes with the standard library rather than duplicating it. @vgpu/wgsl-std ships with vgpu, so the snippets below need no extra install.

npm install @vshaders/color

@vshaders/color/oklab

SignatureDescription
linearSrgbToOklab(color: vec3f) -> vec3fLinear-light sRGB to OKLab.
oklabToLinearSrgb(lab: vec3f) -> vec3fOKLab back to linear-light sRGB. Out-of-gamut results are not clamped; clamp or gamut-map explicitly.
oklabToOklch(lab: vec3f) -> vec3fReturns (lightness, chroma, hueRadians). The hue of an achromatic color (chroma == 0) is indeterminate, as atan2(0, 0) is unspecified in WGSL.
oklchToOklab(lch: vec3f) -> vec3fThe inverse of the above.
mixOklab(colorA: vec3f, colorB: vec3f, t: f32) -> vec3fPerceptually even blend between two linear-light sRGB colors; unclamped in t.
gradient.wgsl
import { mixOklab } from "@vshaders/color/oklab";
import { linearToSrgb3 } from "@vgpu/wgsl-std/color";

@fragment fn main(@builtin(position) position: vec4f) -> @location(0) vec4f {
  let t = position.x / 512.0;
  let gradient = mixOklab(vec3f(0.05, 0.03, 0.35), vec3f(0.9, 0.18, 0.12), t);
  return vec4f(linearToSrgb3(gradient), 1.0);
}

@vshaders/color/palette

SignatureDescription
cosinePalette(t: f32, bias: vec3f, amplitude: vec3f, frequency: vec3f, phase: vec3f) -> vec3fThe cosine gradient bias + amplitude * cos(tau * (frequency * t + phase)). t is unbounded; with frequency = vec3f(1.0) the palette repeats with period 1. Output is in whatever light space bias and amplitude are given in.
palette.wgsl
import { cosinePalette } from "@vshaders/color/palette";
import { linearToSrgb3 } from "@vgpu/wgsl-std/color";

@fragment fn main(@builtin(position) position: vec4f) -> @location(0) vec4f {
  let t = position.x / 512.0;
  let color = cosinePalette(t, vec3f(0.5), vec3f(0.5), vec3f(1.0), vec3f(0.0, 0.33, 0.67));
  return vec4f(linearToSrgb3(max(color, vec3f(0.0))), 1.0);
}

Provenance

The OKLab conversion matrices are Björn Ottosson's published reference implementation (public domain), transcribed to WGSL with a sign-safe cube root so slightly out-of-gamut inputs don't produce NaN. The cosine palette is a direct expression of the formula popularized by Inigo Quilez. MIT licensed.