OKLCH and Modern Color Spaces on the Web

Why HSL keeps lying to you about lightness, what a perceptually uniform space actually buys, and where the approximation runs out.

Short answer: oklch() describes a color by perceptual lightness, chroma and hue angle, and it is defined in the CSS Color Module Level 4 specification alongside oklab(). Its practical value is that the three numbers are close to independent: change the hue and the color does not jump in apparent lightness, change the lightness and the hue holds. HSL does not behave that way, because its lightness is arithmetic on the sRGB components rather than anything about vision. OKLCH is an approximation, not a solved problem, but it is a much better approximation than the one most stylesheets are still using.

The demonstration that settles it

Take two colors that HSL claims are equally light: hsl(60 100% 50%), pure yellow, and hsl(240 100% 50%), pure blue. Both are declared 50 percent lightness. Now measure them.

ColorHSL lightnessLight emitted, relative to whiteCIE lightness (L star)
Yellow50 percent92.8 percent97
Cyan50 percent78.7 percent91
Red50 percent21.3 percent53
Blue50 percent7.2 percent32

Yellow and blue are given the same number by HSL and differ by 65 points of perceptual lightness. Anyone who has built a palette by rotating the hue slider at constant HSL lightness has met this: the yellows glare, the blues disappear, and the set has no visual rhythm. The problem is not taste. The model is measuring something that has no perceptual meaning.

The underlying reason is in gamma and perceptual uniformity: HSL operates on gamma-encoded sRGB values and takes their midrange as lightness, which ignores both the encoding nonlinearity and the fact that the three channels contribute very unequally to luminance.

What "perceptually uniform" is trying to mean

A perceptually uniform color space is one in which equal geometric distances correspond to equal perceived differences. That is the ideal. Nothing achieves it, for a reason that goes back to the just-noticeable difference work: discrimination thresholds form ellipses whose size and orientation change across color space, so no single smooth deformation of the space can flatten them all at once.

CIELAB, adopted in 1976, was the first widely used attempt and remains the backbone of industrial color difference. Its weaknesses are well documented. The most quoted one is hue linearity in the blue region: measurements of constant-hue loci, made by asking observers to find colors of the same hue at different lightness and chroma, showed that CIELAB's straight hue lines do not match the perceptual ones there. In practice that means lightening a blue while holding CIELAB hue constant drags it towards purple, which is exactly the operation an interface designer performs a hundred times a day.

The lineage, briefly

SpaceYearWhat it addedCost
CIELAB1976A workable perceptual space and a difference metricUneven uniformity; blue hue lines curve
CAM02-UCS2006A uniform space derived from a color appearance model, accounting for viewing conditionsHeavier; needs viewing-condition parameters
CAM16-UCS2017A revised appearance model fixing structural problems in its predecessor, with matching uniform spaceStill heavy for interactive use
Jzazbz2017Designed for high dynamic range and wide gamut image signalsAimed at imaging pipelines, not stylesheets
Oklab and OKLCH2020A light, well-behaved approximation aimed at image and interface work, now in the CSS specificationAn approximation, and it does not model viewing conditions at all

The pattern is worth noticing. The appearance-model family got more accurate and more expensive, because it predicts how a color looks under stated viewing conditions, which is what you need for color measurement and cross-media matching. Oklab went the other way: it drops viewing conditions entirely and optimises for being cheap, smooth and good enough for the operations that interfaces and image editors actually perform. Different jobs, not a ranking.

What the CSS specification actually gives you

CSS Color Module Level 4 defines oklab() and oklch() as first-class color functions, alongside lab() and lch() for the CIE spaces, and the color() function for addressing predefined spaces such as display-p3, rec2020, prophoto-rgb and the CIE XYZ spaces directly. Two consequences follow that are easy to miss.

First, the coordinates are not clamped to a device gamut. You can write a chroma that no sRGB monitor can produce, which is useful when you want the same declaration to reach further on a wide-gamut display, and dangerous if you never check what the gamut mapping does on ordinary hardware.

Second, interpolation has to pick a space. Gradients and animations between two colors give visibly different results depending on where the interpolation happens, and the classic failure, a desaturated grey sagging in the middle of a blue-to-yellow gradient, is what interpolating in a perceptual space avoids.

What it is good for, concretely

Tonal scales. Fix hue and chroma, step the lightness evenly, and you get a ramp whose steps feel evenly spaced. Do the same in HSL and the steps bunch.

Palettes with a consistent weight. Fix lightness and chroma, rotate the hue, and the resulting set reads as a family rather than as a fight. This is the operation HSL fails at most obviously.

Programmatic lightening and darkening. Adding to L does what you meant. Adding to HSL lightness shifts apparent hue, particularly through the blues.

Interpolation. Midpoints stay saturated instead of collapsing towards grey. This is the same problem, in a different costume, as the one Huetween makes you solve by eye.

Where it stops helping

It is not a contrast checker. Equal Oklab lightness lands you near equal luminance, so contrast becomes more predictable, but accessibility contrast is defined on relative luminance and still has to be computed. Build the scale in OKLCH, then verify with the ratio, as color contrast and accessibility describes.

It does not know about your viewing conditions. Oklab has no parameter for the surround, the adapting luminance or the white point of the room. Everything on our color constancy page still applies, and no notation fixes it.

Uniformity degrades at high chroma. The approximation is best near the middle of the space. Very saturated colors are where every uniform space struggles, and where you should trust your eyes over the numbers.

It is not a color-difference formula. Euclidean distance in Oklab is a reasonable rough guide, but industrial color difference still runs on the CIE recommendation, described in color difference and Delta-E.

The connection to the games

Huewheel deals in hue angles: complements, analogous sets and triads are all defined by rotations. Which wheel you rotate on matters, because a 180 degree turn in one space and in another do not land on the same color, a point our page on color wheel relationships takes up.

Huetween is the interpolation question made playable. There is no space-free answer to "what is halfway", and playing it is a fast way to discover that your eye's answer is not the arithmetic mean of two hex codes.

Huesort asks for an even gradient, and evenness is defined relative to a space too. Understanding that is what turns a frustrating arrangement into an interesting one.

Hue angles, by eye. Huewheel asks you to find complements, analogous sets and triads without a color picker. It is the fastest way to feel where a wheel's geometry matches your perception and where it does not.

Play Huewheel →

Frequently asked questions

What is OKLCH?

A CSS color notation with three components: L for perceptual lightness, C for chroma, and H for hue angle in degrees. It is the polar form of the Oklab color space, and both are defined in the CSS Color Module Level 4 specification, so browsers implement them from a written standard rather than from a library.

How is it different from HSL?

HSL's lightness is a simple algebraic function of the sRGB components, not a perceptual quantity. Pure yellow and pure blue are both given a lightness of 50 percent in HSL, yet their CIE lightness values are about 97 and about 32. In OKLCH, two colors with the same L really do look about equally light.

Does changing the hue in OKLCH keep the lightness?

Approximately, which is the whole point and also the honest caveat. Rotating hue at fixed L and C keeps apparent lightness far more stable than the same operation in HSL, but no space is perfectly uniform, and at high chroma the approximation gets looser.

Can OKLCH describe colors my screen cannot show?

Yes, and that is a feature and a trap. The coordinates are not bounded by any device gamut, so you can write a chroma that no sRGB display can produce. The browser then has to gamut-map it, and the result depends on the mapping, so check high-chroma values on real hardware.

Does using OKLCH make my palette accessible?

It helps and it does not settle it. Equal Oklab lightness gets you close to equal luminance, so contrast becomes more predictable, but the accessibility contrast ratio is defined on relative luminance and still has to be computed. Use OKLCH to build the scale, then check the ratios.

What is CAM16-UCS, and do I need it?

It is a uniform color space derived from a full color appearance model, published in 2017, that predicts appearance under specified viewing conditions. It is more accurate and considerably heavier than Oklab. For interface and image work Oklab is usually the better trade; for color measurement and appearance prediction, the appearance-model family is the right tool.

Why did CIELAB need replacing?

It did not get replaced so much as supplemented. Its known weaknesses include hue lines that curve in the blue region, so that lightening a blue at constant CIELAB hue drifts towards purple. Measurements of constant-hue loci made this concrete decades ago, and every later space has tried to straighten it out.

Should I convert my whole codebase?

There is no need to convert existing colors that already work. The payoff comes when you are generating colors: building a tonal scale, interpolating between two colors, or programmatically lightening and darkening. Those are the operations where the space you work in changes the answer.

Keep reading

Sources: W3C, CSS Color Module Level 4, Oklab and Oklch; W3C, CSS Color Module Level 4, predefined color spaces; Li, C., Li, Z., Wang, Z., Xu, Y., Luo, M. R., Cui, G., Melgosa, M., Brill, M. H. and Pointer, M. (2017), Comprehensive color solutions: CAM16, CAT16, and CAM16-UCS, Color Research & Application 42(6), 703; Luo, M. R., Cui, G. and Li, C. (2006), Uniform colour spaces based on CIECAM02 colour appearance model, Color Research & Application 31(4), 320; Safdar, M., Cui, G., Kim, Y. J. and Luo, M. R. (2017), Perceptually uniform color space for image signals including high dynamic range and wide gamut, Optics Express 25(13), 15131; Hung, P.-C. and Berns, R. S. (1995), Determination of constant hue loci for a CRT gamut and their predictions using color appearance spaces, Color Research & Application 20(5), 285.

Understand it, now train it. The daily games stay free forever. A Personal Pass adds progress tracking (trends + export); Teacher / School tools let you assign it and follow a whole class. For teachers & self-learners →

Updated · By Lajos Toldi — educator and researcher in adaptive intelligent tutoring systems at AI24EduLabs (part of AI24Labs). Scientific claims are checked against authoritative sources; see our editorial policy.
Privacy · Cookies · Terms · About · Contact