Math
Low-level color math: HSL quantization to a 720-cell grid, color space conversions, and perceptual distance in OKLab.
Pick a color to quantize
Original
HSL(25, 67%, 31%)
#84461a
OKLab L=0.463 a=0.062 b=0.080
Quantized"bright dark orange"
HSL(30, 75%, 30%)
#864d13
OKLab L=0.478 a=0.051 b=0.089
The quantization grid has 24 hues × 6 saturations × 5 lightnesses = 720 cells.
Hue snaps to: [0, 15, 30, 45, 60, ...345]
Saturation snaps to: [0, 15, 30, 50, 75, 100]
Lightness snaps to: [10, 30, 50, 70, 90]
quantizeHsl({ h: 25, s: 67, l: 31 });
// → { hue: 30, saturation: 75, lightness: 30 }import { quantizeHsl, rgbToOklab, oklabDistance, HUE_VALUES } from "colorscope/math";Quantization
quantizeHsl
Snap an HSL color to the nearest cell on the quantization grid (24 hues × 6 saturations × 5 lightnesses = 720 cells). When saturation snaps to 0, hue normalizes to 0.
function quantizeHsl(hsl: HSL): QuantizedHSLquantizeHsl({ h: 25, s: 67, l: 31 });
// → { hue: 30, saturation: 75, lightness: 30 }
quantizeHsl({ h: 180, s: 3, l: 50 });
// → { hue: 0, saturation: 0, lightness: 50 } — gray normalizationColor Space Conversions
hslToRgb
function hslToRgb(h: number, s: number, l: number): { r: number; g: number; b: number }Convert HSL (0-360 hue, 0-100 sat/light) to sRGB (0-255).
rgbToHsl
function rgbToHsl(r: number, g: number, b: number): HSLConvert sRGB (0-255) to HSL. Values are integer-rounded.
hslToHex
function hslToHex(h: number, s: number, l: number): stringConvert HSL to hex string (e.g., "#ff8040").
srgbToLinear
function srgbToLinear(value: number): numberConvert a single sRGB channel (0-255) to linear RGB (0-1). Applies inverse gamma correction.
linearRgbToOklab
function linearRgbToOklab(r: number, g: number, b: number): OKLabConvert linear RGB (0-1) to OKLab. The core matrix transform from Björn Ottosson's specification.
rgbToOklab
function rgbToOklab(r: number, g: number, b: number): OKLabConvert sRGB (0-255) to OKLab. Convenience function that applies gamma linearization first.
hslToOklab
function hslToOklab(h: number, s: number, l: number): OKLabConvert HSL to OKLab (via RGB intermediate).
hexToOklab
function hexToOklab(hex: string): OKLab | nullParse hex (#RGB or #RRGGBB) and convert to OKLab. Returns null for invalid formats.
getOklabLightness
function getOklabLightness(h: number, s: number, l: number): numberGet perceptual lightness (0-1) from HSL. Useful for perceptually-accurate sorting.
Distance Metrics
oklabDistance
function oklabDistance(a: OKLab, b: OKLab): numberEuclidean distance between two OKLab colors. Perceptually uniform — equal distances look equal to human eyes.
Typical thresholds: <0.02 imperceptible, <0.05 similar, <0.1 related.
const leather1 = rgbToOklab(139, 69, 19); // saddle brown
const leather2 = rgbToOklab(160, 82, 45); // sienna
oklabDistance(leather1, leather2);
// → 0.052 — similar, these look closeoklabDistanceSq
function oklabDistanceSq(a: OKLab, b: OKLab): numberSquared Euclidean distance. Faster than oklabDistance — use for sorting and filtering where you only need relative ordering.
Constants
HUE_VALUES
const HUE_VALUES: number[] // [0, 15, 30, ... 345] — 24 valuesSATURATION_VALUES
const SATURATION_VALUES: readonly [0, 15, 30, 50, 75, 100]LIGHTNESS_VALUES
const LIGHTNESS_VALUES: readonly [10, 30, 50, 70, 90]Types
QuantizedHSL
interface QuantizedHSL {
hue: number; // 0, 15, 30, ... 345
saturation: number; // 0, 15, 30, 50, 75, 100
lightness: number; // 10, 30, 50, 70, 90
}