Skip to main content
colorscope

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

Snap distance (OKLab):0.0209barely visible

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): QuantizedHSL
quantizeHsl({ 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 normalization

Color 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): HSL

Convert sRGB (0-255) to HSL. Values are integer-rounded.

hslToHex

function hslToHex(h: number, s: number, l: number): string

Convert HSL to hex string (e.g., "#ff8040").

srgbToLinear

function srgbToLinear(value: number): number

Convert a single sRGB channel (0-255) to linear RGB (0-1). Applies inverse gamma correction.

linearRgbToOklab

function linearRgbToOklab(r: number, g: number, b: number): OKLab

Convert 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): OKLab

Convert sRGB (0-255) to OKLab. Convenience function that applies gamma linearization first.

hslToOklab

function hslToOklab(h: number, s: number, l: number): OKLab

Convert HSL to OKLab (via RGB intermediate).

hexToOklab

function hexToOklab(hex: string): OKLab | null

Parse hex (#RGB or #RRGGBB) and convert to OKLab. Returns null for invalid formats.

getOklabLightness

function getOklabLightness(h: number, s: number, l: number): number

Get perceptual lightness (0-1) from HSL. Useful for perceptually-accurate sorting.

Distance Metrics

oklabDistance

function oklabDistance(a: OKLab, b: OKLab): number

Euclidean 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 close

oklabDistanceSq

function oklabDistanceSq(a: OKLab, b: OKLab): number

Squared 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 values

SATURATION_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
}