30

Oklab and Oklch: Modern Perceptual Color Spaces

HSL is intuitive but lies about brightness. CIELAB is honest but clumsy in blue and needs elaborate patches. In 2020 a single, elegant space arrived that is both intuitive and perceptually well-behaved - and modern CSS adopted it almost overnight. This is the complete, interactive tour of Oklab and its polar twin Oklch: why they exist, the math that makes them work, and the everyday problems they finally solve.

Colorimetry · 22 6 Live Demos ~45 min read Oklab · Oklch · CSS
2020
Published by B. Ottosson
L C h
Perceptual polar axes
oklch()
Native in CSS Color 4
2 × 3×3
Matrices, one cube root
00

Advanced Oklch production lab

Oklch is more than a nicer color picker. In production it becomes a decision surface: which chroma can survive the target gamut, how much lightness can move before contrast breaks, whether a gradient should travel through Oklab or Oklch, and how stable a palette remains when you ask for many steps. This lab turns those hidden decisions into live data.

Interactive 00 - Advanced Oklab / Oklch production lab

Analyze gamut mapping, interpolation, tonal ramps, contrast, and perceptual distance

Pick a base color, then edit the Oklch coordinates and production context. The canvas plots the a/b plane, gamut boundary search, tonal ramp, interpolation path, and contrast risk. The meters explain whether the current request is safe for CSS tokens, gradients, dark mode, or brand palettes.

#5B7CFF
Design token ramp
sRGB
Oklab straight line
64%
0.170
264 deg
+72 deg
0.70
9 steps
50%
4.5:1
Active color Calculating...
Oklab vector Calculating...
Gamut map Calculating...
Ramp behavior Calculating...
Contrast decision Calculating...
Recommended use Calculating...
Gamut safety0%
Ramp evenness0%
Gradient quality0%
Contrast safety0%
Model warning Oklch is perceptual, but gamut and contrast still need explicit decisions.
01

The problem with "intuitive" spaces

There are two families of color space, and until recently you had to pick a side. The intuitive family - HSL, HSV - lets a human reason in plain words but is just sRGB twisted into a cylinder, so its numbers do not match perception. The perceptual family - CIELAB and friends - is built on real vision science but is awkward to author in and has well-known weak spots, especially a hue that drifts in the blues.

Three failures show up again and again when the wrong tool is used. First, lightness lies: a blue at HSL L = 50% looks far darker than a yellow at the same number. Second, lightening shifts hue: push a blue lighter in sRGB or HSL and it slides toward purple. Third, gradients misbehave: blend blue to yellow in sRGB and the midpoint goes a muddy gray. Each of these is a perceptual-uniformity problem, and each is exactly what Oklab was built to fix.

Perceptual uniformity
Equal numeric distances in the space correspond to roughly equal perceived color differences. The property HSL/HSV lack and CIELAB approximates.
Hue linearity
Changing only lightness or only chroma should not change the perceived hue. CIELAB famously fails this in blue; Oklab was tuned to hold it.
Authoring ergonomics
A space humans can actually type and reason about. Oklch gives the HSL-style lightness/chroma/hue trio, but on perceptual axes.
The pitch in one line: Oklab is the first widely-adopted space that is perceptual enough for honest math and simple enough to author by hand - which is why CSS, design tools, and palette generators reached for it almost as soon as it appeared.
02

What Oklab is

Oklab was published in December 2020 by Björn Ottosson, who set out to build "a better Lab" optimized specifically for the kinds of operations software performs on color: blending, lightening, generating palettes. Rather than starting from the 1931 CIE machinery, he started from an approximation of the cone responses (the L, M, S signals the eye actually produces), applied a cube-root nonlinearity to model the compressive response of vision, and then fit a final linear transform so that hue lines stayed straight and lightness matched perception.

The result keeps the shape of CIELAB - an opponent space with a lightness axis and two color axes - but corrects CIELAB's biggest practical flaw: it does not bend hue as colors get lighter or more saturated. And because the whole transform is two small matrix multiplies with a cube root in between, it is cheap enough to run per pixel, in shaders, and live in a browser.

"I wanted a model that is simple to use and understand, while still being good enough for most practical applications - so that picking and blending colors just works." Björn Ottosson · paraphrased from the 2020 Oklab introduction
03

The Oklab axes

Oklab describes a color with three Cartesian numbers, mirroring CIELAB's structure but on its own, better-tuned scale:

L — lightness
Perceived lightness from 0 (black) to 1 (the brightest white). Tuned so that a step in L looks like a step in brightness across every hue - the thing HSL gets wrong.
a — green ↔ red
The first opponent axis. Negative is green, positive is red. Roughly bounded to about ±0.4 inside the sRGB gamut.
b — blue ↔ yellow
The second opponent axis. Negative is blue, positive is yellow. Together a and b place the color off the neutral gray axis.

Neutral grays sit at a = b = 0 for every lightness, exactly as in an opponent model of vision. The further (a, b) gets from the origin, the more colorful the result. That distance has a name - chroma - and turning the Cartesian (a, b) into a radius and angle is precisely what gives us Oklch.

04

Oklch: the designer-friendly form

Oklch is Oklab in polar coordinates. The lightness L is unchanged; the two color axes become a radius and an angle:

C = √(a² + b²)       h = atan2(b, a) Chroma C is distance from the neutral axis; hue h is the angle in degrees, 0–360.

This is the same trick that turns CIELAB into LCh, or that turns the RGB cube into HSL - but here the axes are perceptual, so the three dials finally mean what you expect. Raising L makes it lighter without changing the color. Raising C makes it more colorful without changing the hue. Spinning h rotates through hues that feel evenly spaced. It is the ergonomics of HSL with the integrity of a perceptual space.

Interactive 01 · Oklch studio

Three perceptual dials - and a gamut warning

Set lightness, chroma, and hue. The swatch is the nearest sRGB color and the badge tells you whether your request actually fits inside sRGB. Push chroma high at a mid lightness and watch many hues fall out of gamut - Oklch can describe colors your screen cannot show, which is a feature, not a bug.

CSS: oklch(62% 0.150 264) HEX: #5782e0 RGB: 87, 130, 224 in sRGB gamut
05

Hue constancy: the headline win

The single most quoted reason to use Oklch is hue constancy. Take any color and make a row of lighter and darker versions. In HSL - and in naive sRGB - the hue wanders: blues drift toward purple as they lighten, and the steps look uneven, bunching up at the dark end. In Oklch, the hue stays put and the steps feel evenly spaced. That is the difference between a tonal ramp that looks designed and one that looks broken.

Interactive 02 · Lighten a hue, two ways

The same hue, ramped in HSL versus Oklch

Both rows hold one hue and step from dark to light. The HSL row fixes saturation and walks lightness; the Oklch row fixes chroma and walks perceptual lightness. Watch the HSL row twist toward purple and crowd at the dark end, while the Oklch row stays the same color and steps evenly.

HSL — fixed hue & saturation, walking lightness
Oklch — fixed hue & chroma, walking perceptual lightness
Why it matters. Button states, hover shades, chart series, and dark-mode variants all rely on lightening or darkening a brand color without changing its identity. Oklch makes that a one-axis move you can trust; HSL makes it a gamble.
06

The conversion pipeline

Getting from an sRGB byte triple to Oklab is a fixed four-step pipeline. Nothing in it is tuned per color - it is the same constants every time.

1 · Linearize
Undo the sRGB transfer function so the channels are linear-light, in 0–1.
2 · To LMS (M₁)
Multiply by a 3×3 matrix to approximate cone responses (the l, m, s cone signals).
3 · Cube root
Apply ∛ to each LMS value - the nonlinearity that models compressive perception.
4 · To Lab (M₂)
A second 3×3 matrix produces the final L, a, b. For Oklch, convert a, b to C and h.
Step 1 — sRGB ↔ linear (per channel, c in 0–1)linear = (c ≤ 0.04045) ? c / 12.92 : ((c + 0.055) / 1.055) ^ 2.4 sRGB = (c ≤ 0.0031308) ? 12.92 · c : 1.055 · c ^ (1/2.4) − 0.055
Step 2 — linear sRGB → LMS (M₁)l = 0.4122214708·r + 0.5363325363·g + 0.0514459929·b m = 0.2119034982·r + 0.6806995451·g + 0.1073969566·b s = 0.0883024619·r + 0.2817188376·g + 0.6299787005·b
Step 3 + 4 — cube root, then LMS′ → Oklab (M₂)l′ = ∛l m′ = ∛m s′ = ∛s L = 0.2104542553·l′ + 0.7936177850·m′ − 0.0040720468·s′ a = 1.9779984951·l′ − 2.4285922050·m′ + 0.4505937099·s′ b = 0.0259040371·l′ + 0.7827717662·m′ − 0.8086757660·s′

The inverse simply runs backward: Oklab → cube (the inverse of the cube root) → inverse matrices → delinearize. Because every step is invertible, the round trip is exact up to floating-point and the final 8-bit rounding.

Gamut is the catch. Oklab/Oklch can name colors outside sRGB. After the inverse transform you may get a linear channel below 0 or above 1 - that color is out of gamut. You must then gamut-map it (usually by reducing chroma at fixed L and h until it fits) rather than naively clipping each channel, which would shift the hue and lightness. CSS does this for you; hand-rolled code must not forget it.
07

Gradients done right

Interpolation is where the space you choose becomes visible to everyone, not just color nerds. Blend two colors by walking a straight line between them - the question is which space the line lives in. sRGB interpolation dips dark and muddy through the middle. HSL interpolation swings through unwanted hues. Oklab interpolation keeps lightness and colorfulness sensible the whole way across.

Interactive 03 · Three ways to blend

The same two endpoints, interpolated in three spaces

Pick two colors. The three bars blend them in sRGB, in HSL (shortest hue arc), and in Oklab. The classic test is blue → yellow: sRGB grays out in the middle, HSL detours through green, and Oklab stays bright and even. Try saturated complementary pairs to see the gap most clearly.

sRGB interpolation
HSL interpolation (shortest hue arc)
Oklab interpolation

This is exactly why CSS Color 4 lets you name the interpolation space: linear-gradient(in oklab, …) and color-mix(in oklch, …) produce the clean blends by default in modern browsers.

08

Perceptual lightness and palettes

Because Oklab's L tracks perceived lightness, it is the natural engine for a tonal scale - the 50-to-900 ramp every design system needs. Fix the hue and chroma, step lightness in even increments, and you get a scale whose steps look even and whose endpoints behave. It also makes accessible contrast far more predictable: two colors with a sensible gap in Oklab lightness will usually have a sensible contrast ratio, because both are anchored in perception.

Interactive 04 · Tonal scale generator

Build a 50–900 ramp from one hue

Choose a hue and a target chroma. The generator steps perceptual lightness from light to dark, reducing chroma automatically where a step would fall out of the sRGB gamut. The result is the kind of even, hue-stable scale you would ship as design tokens.

09

Oklch in CSS Color 4

Oklab and Oklch are first-class CSS color functions, shipping in every major browser since 2023. The syntax mirrors the model directly:

CSS Color 4 — Oklab / Oklch syntaxcolor: oklch(62% 0.15 264); /* L C H */ color: oklab(62% -0.02 -0.16); /* L a b */ color: oklch(70% 0.12 200 / 0.5); /* with 50% alpha */ /* perceptual interpolation */ background: linear-gradient(in oklch, #f6c500, #1f4fd8); --mix: color-mix(in oklab, white 30%, var(--brand)); /* relative color: lighten the brand color, keep its hue */ --hover: oklch(from var(--brand) calc(l + 0.08) c h);

That last line - relative color syntax - is the payoff. You can derive a hover or disabled variant by nudging only L, confident the hue and chroma stay put. The whole "lighten by 8%" intent becomes a single, honest expression that would have been a hue-shifting mess in HSL.

L in CSS
Accepts a percentage (0–100%) or a 0–1 number. Both mean the same perceptual lightness.
C in CSS
A number from 0 upward; values past roughly 0.37 are mostly out of sRGB but valid for wide-gamut displays. none and percentages are allowed too.
H in CSS
An angle in degrees. Browsers gamut-map automatically for the target display, so you rarely clip by hand.
10

Oklab vs CIELAB vs HSL

Each space is the right answer to a different question. The converter below shows one color in all of them at once; the table sums up when to reach for which.

Interactive 05 · One color, every model

See sRGB, HSL, Oklab, and Oklch side by side

Pick or type a color, or tap a preset. The panel reports it as RGB, HSL, Oklab (L a b), and Oklch (L C h) simultaneously, with a gamut flag. It is the fastest way to build intuition for how Oklch lightness and chroma relate to the HSL numbers you already know.

RGB
42, 157, 143
HSL
173°, 58%, 39%
Oklab
0.630, -0.101, -0.005
Oklch
63.0%, 0.101, 183°
oklch(63.0% 0.101 183) in sRGB gamut
Need Best space Why
Quick casual tweak HSL / HSV Fastest to reason about; perceptual error does not matter for a one-off nudge.
Even tonal ramps & theming Oklch Perceptual lightness, hue constancy, native in CSS - the modern default.
Smooth gradients / blends Oklab Straight-line interpolation stays bright and even, no gray midpoint.
Industry color difference (ΔE) CIELAB / CIEDE2000 The standardized, contract-grade reference the whole industry already uses.
Absolute measurement & profiles CIE XYZ / Lab Tied to the standard observer and reference whites; the metrological ground truth.
11

Limits and caveats

Not a measurement standard
Oklab is young and not enshrined in ISO/ICC contracts. For certified ΔE tolerances, print, or paint matching, CIEDE2000 in CIELAB is still the expected reference.
It describes out-of-gamut colors
A valid Oklch triple can sit outside sRGB. You must gamut-map, not channel-clip, or you will shift hue and lightness.
No appearance model
It has no surround, adaptation, or viewing-condition terms. For cross-illuminant appearance prediction you still need CIECAM-style models.
Tuned, not derived from CIE data alone
Its constants were fit pragmatically. It is excellent for design operations, but it is an engineering approximation, not a law of vision.
Lightness ≠ contrast ratio
Oklab L is a much better guide than HSL, but WCAG contrast and APCA use their own formulas. Verify with the real metric, don't assume.
Reference white is D65
Oklab assumes a D65 white point, matching sRGB. Mixing it with D50 print workflows needs an explicit chromatic adaptation step.
"Oklab will not replace CIELAB at the lab bench, and it does not try to. It replaces HSL at the keyboard - the place where most color decisions are actually made." Editorial summary · Oklab in production
12

Test your understanding

Six questions on Oklab and Oklch. Instant feedback, no scores recorded - a wrong answer comes with a short explanation pointing you back to the right section.

Quick check

Loading…
 
Question 1 of 6
13

Continue your journey

These articles sit directly around Oklab - the spaces it improves on and the ones it works alongside. The numbers reflect each article's position in the editorial roadmap of 113.