Learn · Advanced

Color spaces and perceived lightness

Gamma, perceptual uniformity and why OKLCH is popular

Two colors that are "10 units apart" numerically are not always 10 units apart visually. This mismatch is why some gradients look muddy, why steps in a scale feel uneven, and why modern systems lean on perceptual models like OKLCH. This page looks at gamma, perceptual uniformity, and how that ties into newer frameworks and tools.

1.

Gamma and non‑linear brightness

Most digital color encodings are gamma‑corrected:

  • Human vision is more sensitive to differences in dark tones than in bright ones.
  • To use storage efficiently, we curve the mapping between numeric values and actual light (luminance).

In sRGB:

  • The channel value 0.5 (on a 0–1 scale) does not mean "half as bright" as 1.0.
  • It's brighter than the midpoint in terms of physical light.

As a result:

  • Linear interpolation in sRGB ((c1 + c2) / 2) doesn't produce a perceptual halfway color.
  • Simple averages of RGB values often look too dark or desaturated in the middle.
2.

Why RGB gradients sometimes look "dirty"

When you create a gradient like:

background: linear-gradient(to right, #ff0000, #00ff00);

the browser:

  1. Converts the hex to sRGB
  2. Interpolates R, G and B channels linearly in that space
  3. Applies gamma when drawing to the screen

The mid‑point between bright red and bright green is a yellowish color, but the non‑linear encoding plus the mixing of channels can produce muddy, low‑contrast regions — gradients between complementary hues especially tend to look desaturated in the centre.

In UI, this shows up as:

  • gradients that flatten in the middle
  • tints/shades that don't feel evenly spaced in lightness
  • difficult to predict contrast steps using raw RGB math
3.

Perceptual uniformity

A color space is perceptually uniform (or close to it) if equal numeric steps correspond roughly to equal visual steps.

Not perceptually uniform

  • Classic sRGB, HSL and HSV — stepping through these numerically produces uneven perceived differences.

Spaces designed with perceptual uniformity in mind include:

CIE Lab / LCHOlder but widely supported perceptual model.
OKLab / OKLCHNewer model with better uniformity and gamut behaviour.

Here, stepping lightness by 0.05 looks like a similar change regardless of hue.

4.

Lab and LCH

CIE Lab represents color as:

L*Lightness
a*, b*Two opponent color axes (green–red, blue–yellow)

LCH is a cylindrical rewrite:

L*Lightness
C*Chroma (colorfulness)
H*Hue angle

Benefits:

  • Better behaved gradients
  • More predictable tonal scales
  • Useful for measuring color differences (ΔE)

Limitations

  • Derived from older viewing conditions and reference data.
  • Not perfectly uniform, especially at extremes.
  • Coordinates are less intuitive for everyday UI work.
5.

OKLab and OKLCH

OKLab and OKLCH are newer models that build on similar ideas but aim for:

  • better uniformity across the entire gamut
  • good behaviour across different color spaces (sRGB, P3…)
  • stability for both light and dark themes
oklch(L C H)
LLightness (0–1)
CChroma
HHue in degrees

Practical advantages for UI:

  • Stepping L gives predictable changes in perceived lightness.
  • You can reduce C in dark mode to avoid neon/glow without wrecking contrast.
  • Gradients with constant L and changing H/C look smooth and vivid.
6.

How this relates to Tailwind and other modern frameworks

Several modern tools and frameworks are moving towards perceptual models:

  • Newer Tailwind releases and community palettes are designed in OKLCH (or LCH) first, then converted to hex.
  • Systems like Radix Colors and some Material‑inspired palettes are tuned with perceptual uniformity in mind, even if they expose only hex codes.
  • Design‑token pipelines increasingly do math in Lab/LCH/OKLCH before emitting RGB.

Benefits for teams

  • Scales (50–900, 1–12) feel smoother and more consistent.
  • It's easier to guarantee contrast ratios across a palette.
  • Light/dark pairs can be managed with systematic adjustments of L and C instead of manual eyeballing.
7.

Practical takeaways

1. Do math in a perceptual space when possible

If you're generating scales programmatically, operate in Lab/LCH/OKLCH, not raw RGB.

2. Use pre‑tuned palettes where it makes sense

Tailwind, Radix, Open Color and similar systems have already done this work for you.

3. Avoid naive averaging in RGB

For gradients and midpoints, convert to a perceptual space, interpolate there, then convert back.

4. Think in terms of lightness first

For accessible design, control lightness explicitly and use chroma as a secondary dial.

On ColorR

  • ColorR shows traditional and perceptual coordinates (Lab, LCH, OKLCH) for each named color, making it easier to reason about which colors have similar lightness, how far apart two hues really are, and how to build even scales across different frameworks.