A hands-on explainer for developers. Three demos that show exactly why OKLCH produces more consistent, predictable colors than HSL — and when it matters.
Developed by itsmeprakash.com
Every swatch below has identical L=50% in HSL. They should look equally bright — but your eye clearly sees different brightness levels. Yellow looks near-white. Blue looks near-black. HSL's lightness is mathematical, not perceptual.
HSL — hsl(H, 90%, 50%)
OKLCH — oklch(0.65, 0.20, H)
The OKLCH row uses L=0.65 for all — they look genuinely similar in brightness because OKLCH is calibrated to human vision.
Left half is HSL, right half is OKLCH. Both use the same lightness value. As you move through hues, notice that HSL brightness jumps while OKLCH stays stable. This is the perceptual uniformity difference in real time.
Both strips go from dark to light in 9 equal mathematical steps. In HSL the jumps look uneven — the middle steps cluster. In OKLCH each step looks like the same visual distance. Pick a hue to try it on.
HSL — L steps: 10%, 20%, 30%... 90%
OKLCH — L steps: 0.10, 0.20, 0.30... 0.90
Interpolating from blue to yellow in HSL passes through a desaturated, gray-ish zone. In OKLCH the path stays vivid the whole way. This directly affects CSS gradients and color transitions.
OKLCH reads like HSL — three parameters, same mental model — but the numbers actually match what your eyes see.
| Parameter | In HSL | In OKLCH | What it controls |
|---|---|---|---|
| Lightness L | 0%–100% (not perceptual) | 0–1 or 0%–100% (perceptual) | How bright the color looks to a human eye |
| Saturation / Chroma C | 0%–100% | 0–0.37 approx | How vivid vs gray — OKLCH C can exceed sRGB into P3 gamut |
| Hue H | 0°–360° | 0°–360° | The color angle — blue ~260°, red ~25°, green ~145° |