Perceptual Color Spaces: Lab, LCH, Oklab, OKLCH
When you design a set of color swatches at equal perceptual intervals — shades that look evenly spaced to the human eye — you quickly discover that HSL and RGB are the wrong tools. Incrementing HSL lightness by 10 points produces dramatically different visual jumps depending on the hue. Blue gets much darker much faster than yellow. Green and yellow are clustered at the bright end, blue and purple at the dim end, yet they share identical lightness numbers.
The solution is a perceptually uniform color space: one where equal numerical differences in any direction correspond to equal perceived differences in color. This tutorial traces the evolution of perceptually uniform color spaces — from CIE Lab in 1976 to OKLCH in 2020 — explaining how each works and what distinguishes them.
Why Perceptual Uniformity Matters
Human color vision is a biological system built for survival, not for linear measurement. The three types of cone cells in our retinas respond to overlapping ranges of light wavelengths, with peak sensitivity in the green-yellow region. We are far more sensitive to variations in green light than in blue. We also perceive brightness non-linearly: a 10% increase in physical light output looks like a much smaller perceptual increase than a 10% increase starting from near-darkness.
These biological facts mean that the numerical coordinates of any color — its R, G, B values, or its H, S, L values — are a very poor guide to how it looks. Two colors at the same numerical distance in sRGB or HSL may look barely distinguishable or dramatically different, depending on where they sit in the color space.
A perceptually uniform color space attempts to correct for this. Its mathematical structure is calibrated against human perception data, gathered through experiments where observers compare colors and report which pairs look "equally different." The goal: a 10-unit move in any direction in the color space should look like the same size change regardless of starting point.
Practical applications that benefit from perceptual uniformity:
- Shade generation: building a 10-step tonal scale that looks evenly spaced
- Color interpolation: gradients that transition smoothly without luminance dips
- Contrast evaluation: knowing whether two colors are "far enough apart" perceptually
- Color picking: moving sliders that have predictable visual effects
- Data visualization: mapping numerical data to color without creating misleading brightness variations
CIE Lab (1976)
CIE Lab (also written as L*a*b* or CIELAB) was standardized by the International Commission on Illumination in 1976. It remains the international standard for color measurement in manufacturing, printing, and textile industries.
Structure
Lab uses three axes:
- L* — Lightness, from 0 (black) to 100 (white)
- a* — Green-to-red axis. Negative values are green, positive are red/magenta.
- b* — Blue-to-yellow axis. Negative values are blue, positive are yellow.
The a* and b* axes are opponent-color axes, reflecting the way neurons in the visual system encode color information as red-vs-green and blue-vs-yellow signals.
From XYZ to Lab
Converting to Lab requires going through CIE XYZ, a linear color space derived from the color-matching functions of a "standard observer":
L* = 116 × f(Y/Yn) − 16
a* = 500 × [f(X/Xn) − f(Y/Yn)]
b* = 200 × [f(Y/Yn) − f(Z/Zn)]
Where Xn, Yn, Zn are the XYZ values of a reference white (usually D65, the standard daylight illuminant), and f is a cube-root-based function that encodes the compressive nature of human lightness perception.
The Color Converter converts any HEX or RGB value to Lab coordinates alongside other formats.
Lab's Strengths and Limitations
Lab does a much better job than sRGB or HSL at predicting when two colors will look equally different. Its lightness axis L* is approximately perceptually uniform — a move from L*=30 to L*=40 looks about the same size as a move from L*=60 to L*=70, for most observers under standard viewing conditions.
However, CIE Lab has a known flaw: its chroma-lightness interaction. Vivid, highly saturated colors (particularly blues and greens) look slightly uneven in Lab space. Some perceptual "circles" in Lab space look more like ellipses or irregular blobs to actual human observers. The Munsell system, based on extensive empirical data, does not map cleanly to Lab.
Lab is also defined in terms of rectangular coordinates, which makes it less intuitive for designers who think in terms of hue and saturation.
CSS Syntax
/* CIE Lab in CSS */
color: lab(55% -20 40);
/* L*=55 (mid-tone), a*=-20 (slightly green), b*=40 (yellow) */
color: lab(75% 0 0);
/* Neutral gray at L*=75 */
LCH: Cylindrical Lab
LCH is a cylindrical transformation of CIE Lab — the same color space expressed in polar coordinates. Instead of the rectangular a*/b* grid, LCH uses:
- L — Lightness (same as Lab L*)
- C — Chroma (the distance from the neutral gray axis: C = √(a² + b²))
- H — Hue angle in degrees (H = atan2(b, a), range 0–360)
Why LCH is More Intuitive Than Lab
Chroma is analogous to "saturation" but more meaningful: it measures the colorfulness of a color relative to a neutral gray of equal lightness. Hue is the familiar angle on the color wheel. This makes LCH much more accessible to designers than Lab's abstract a*/b* coordinates.
Crucially, LCH retains the perceptual uniformity properties of Lab. Equal C increments look like equal increases in colorfulness. Equal L increments look like equal steps in brightness. And because the L axis is the same as Lab's L*, equal L steps look perceptually even across all hues — unlike HSL, where yellow is dramatically brighter than blue at the same L.
CSS Syntax
/* LCH in CSS */
color: lch(55% 50 150);
/* L=55 (mid-tone), C=50 (moderate chroma), H=150 (green) */
/* Building a tonal scale with consistent perceived lightness */
:root {
--teal-100: lch(95% 10 190);
--teal-300: lch(75% 25 190);
--teal-500: lch(55% 40 190);
--teal-700: lch(35% 35 190);
--teal-900: lch(18% 20 190);
}
LCH's Limitations
LCH inherits Lab's underlying chroma-lightness interaction problem. The hue lines in LCH are not perfectly "straight" when projected onto human perception — particularly in the blue range (H ≈ 270–300). This means that as you increase chroma at a fixed hue in LCH, the apparent hue can shift slightly. Blues tend to shift toward purple as chroma increases. This is called "hue non-linearity" and it is the primary motivation for the next generation of color spaces.
Oklab (2020)
Oklab was published by Björn Ottosson in 2020. It is a direct response to the known deficiencies of CIE Lab — specifically, the hue non-linearity that makes Lab and LCH less perceptually uniform than their designers intended.
Design Goals
Ottosson's explicit goal was a color space where:
- Lightness is perceptually uniform across all hues
- Equal numerical differences correspond to equal perceptual differences in all directions
- Hue lines are approximately straight (consistent hue as chroma changes)
- The math is simple and fast to compute
To achieve this, Ottosson took a different approach: instead of deriving the space from the CIE XYZ color-matching functions directly, he fitted a new transformation matrix to empirical data from human color-discrimination experiments (specifically, the MacAdam ellipse data and the Luo-Rigg dataset). The result is a space that better fits the actual shape of human color perception.
Structure
Oklab uses the same L/a/b naming as CIE Lab but with different axes:
- L — Lightness, 0 to 1 (note: normalized to 1, not 100)
- a — Green-to-red opponent axis (similar to CIE Lab a* but with better hue linearity)
- b — Blue-to-yellow opponent axis
The conversion from sRGB involves two matrix multiplications and a cube-root step, making it efficient for real-time computation.
Oklab vs. CIE Lab: Key Improvements
| Property | CIE Lab | Oklab |
|---|---|---|
| Hue linearity | Moderate (blue/green issues) | Much better |
| Perceptual uniformity | Good but imperfect | Closer to uniform |
| Lightness uniformity | Approximate | Better calibration |
| Standard | ISO/CIE (1976) | Open (2020) |
| Browser support | Yes (CSS Color Level 4) | Yes (CSS Color Level 4) |
The improvement in hue linearity is most visible in the blue-to-violet range, where CIE Lab/LCH distorts. In Oklab, changing chroma at a fixed hue produces less hue shift.
CSS Syntax
/* Oklab in CSS */
color: oklab(0.55 -0.05 0.12);
/* L=0.55 (mid-tone), a=-0.05 (slightly green), b=0.12 (yellow) */
OKLCH: Cylindrical Oklab
OKLCH is to Oklab as LCH is to Lab — the same space, expressed in cylindrical polar coordinates. It is the format that has gained the widest adoption in modern CSS and design tooling.
- L — Lightness, 0 to 1
- C — Chroma (distance from neutral axis), typically 0 to ~0.4 for sRGB colors
- H — Hue angle, 0–360 degrees
/* OKLCH in CSS */
color: oklch(0.55 0.15 160);
/* L=0.55, C=0.15, H=160° (green-cyan) */
OKLCH Over LCH: The Practical Difference
For most design work, OKLCH and LCH produce similar results. The difference is most apparent when:
- Generating shade scales through blues: LCH scales in the blue range can drift slightly toward purple at high chroma. OKLCH scales stay on-hue.
- Interpolating across hues: OKLCH gradients through the blue-violet range stay more vibrant and hue-stable than LCH gradients.
- Chroma gamut mapping: When a color exceeds the sRGB gamut and must be mapped back in, OKLCH's better hue linearity results in more predictable gamut mapping.
Building a Shade Scale in OKLCH
Because OKLCH's L axis is perceptually calibrated, you can generate a 10-step scale with equal L increments and get approximately equal visual steps:
Starting from a brand purple #7C3AED — approximately oklch(0.52 0.23 295):
:root {
--purple-50: oklch(0.97 0.04 295);
--purple-100: oklch(0.93 0.07 295);
--purple-200: oklch(0.86 0.11 295);
--purple-300: oklch(0.78 0.15 295);
--purple-400: oklch(0.68 0.19 295);
--purple-500: oklch(0.58 0.22 295);
--purple-600: oklch(0.52 0.23 295); /* brand color */
--purple-700: oklch(0.44 0.22 295);
--purple-800: oklch(0.35 0.18 295);
--purple-900: oklch(0.27 0.13 295);
--purple-950: oklch(0.20 0.09 295);
}
Each step represents a consistent perceptual decrease in lightness. Note that chroma is also adjusted as we approach the dark end — very dark colors cannot support high chroma in any perceptual color space. Use the Color Converter to find the OKLCH coordinates of your brand color and then adjust L values systematically.
OKLCH Beyond sRGB: Wide Gamut Colors
One major advantage of OKLCH over Lab/LCH for web development is that OKLCH can address colors outside the sRGB gamut. Modern displays that support Display P3 or Rec. 2020 can render more vivid colors than sRGB allows. OKLCH coordinates can describe these wide-gamut colors:
/* This vivid green may be outside sRGB on some hues/chroma combinations */
color: oklch(0.80 0.30 140);
/* Browsers will render the closest sRGB equivalent on standard displays */
/* and the full vivid color on wide-gamut displays */
Colors with C > ~0.25 are likely to exceed sRGB for some hue angles. Check whether a specific OKLCH value is in-gamut using the Color Converter.
Choosing the Right Space for Your Task
| Task | Recommended Space | Reason |
|---|---|---|
| CSS color definitions | OKLCH | Best browser support, intuitive, wide gamut |
| Color interpolation (gradients) | OKLCH or Oklab | Both are perceptually uniform; OKLCH is cylindrical (hue-aware) |
| Shade scale generation | OKLCH | L axis is most uniform; cylindrical (hue-aware) |
| Color distance calculation | Oklab or CIE Lab | ΔE calculations work in Cartesian form |
| Print/manufacturing | CIE Lab | Industry standard, ICC profile integration |
| Automated contrast evaluation | Oklab (for APCA) | Oklab-derived lightness underpins APCA |
A Note on CSS Support
All four spaces — Lab, LCH, Oklab, and OKLCH — are supported in CSS Color Level 4 and available in all major modern browsers since 2022–2023:
color: lab(55% -20 40); /* CIE Lab */
color: lch(55% 50 150); /* LCH */
color: oklab(0.55 -0.05 0.12); /* Oklab */
color: oklch(0.55 0.15 160); /* OKLCH */
For most web design work, OKLCH is the default choice: it is the most intuitive cylindrical format, it has the best hue linearity, and it accesses the widest gamut. Use Oklab when you need to do Cartesian color math (distance, averaging). Use CIE Lab/LCH only if you need compatibility with an existing design system or standard built around them.
Key Takeaways
- Perceptual uniformity means equal numerical distances correspond to equal perceived color differences. sRGB and HSL are not perceptually uniform; Lab, LCH, Oklab, and OKLCH are designed to be.
- CIE Lab (1976) was the first widely adopted perceptually uniform space. Its L* axis is well-calibrated for lightness, but its chroma-lightness interaction causes hue shifts, especially in blues.
- LCH is CIE Lab in cylindrical (hue, chroma) form — more intuitive for designers than Lab's a*/b* axes, but inherits Lab's hue-linearity issues.
- Oklab (2020) was designed by Björn Ottosson to fix Lab's hue non-linearity, using empirical human-perception data. It achieves better hue consistency across the chroma range.
- OKLCH is Oklab in cylindrical form — the most practical format for CSS and design work. It combines perceptual uniformity, intuitive hue-chroma-lightness coordinates, and access to wide-gamut colors.
- Use the Color Converter to translate any HEX or RGB color to OKLCH and begin building perceptually calibrated color systems today.