@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
| Signature | Description |
|---|---|
linearSrgbToOklab(color: vec3f) -> vec3f | Linear-light sRGB to OKLab. |
oklabToLinearSrgb(lab: vec3f) -> vec3f | OKLab back to linear-light sRGB. Out-of-gamut results are not clamped; clamp or gamut-map explicitly. |
oklabToOklch(lab: vec3f) -> vec3f | Returns (lightness, chroma, hueRadians). The hue of an achromatic color (chroma == 0) is indeterminate, as atan2(0, 0) is unspecified in WGSL. |
oklchToOklab(lch: vec3f) -> vec3f | The inverse of the above. |
mixOklab(colorA: vec3f, colorB: vec3f, t: f32) -> vec3f | Perceptually even blend between two linear-light sRGB colors; unclamped in t. |
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
| Signature | Description |
|---|---|
cosinePalette(t: f32, bias: vec3f, amplitude: vec3f, frequency: vec3f, phase: vec3f) -> vec3f | The 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. |
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.