Back to articles index
Format comparisons

RGB vs HSL vs OKLCH — which color space should you author in?

Compare three color spaces used in CSS, design tools, and image processing by perceptual uniformity, interpolation quality, and implementation availability. Why CSS Color Module 4 favours OKLCH.

What actually separates the three — perception, interpolation, intent

Picking a colour space is rarely a matter of “newest wins.” Four axes carry most of the weight. Perceptual uniformity asks whether numeric distance matches the difference your eye perceives. RGB and HSL fall short here; OKLCH is close to uniform. Interpolation quality governs gradients and color-mix() results, and tracks perceptual uniformity directly. Intuition is whether a designer or engineer can guess the colour from the number — almost nobody reads #ff8800 as orange, but hsl(30 100% 50%) reads as “vivid orange near red.” Gamut decides whether you stay inside sRGB or can take advantage of Display P3 and other wide-gamut targets, which matters for brand colours and photography.

CSS Color Module Level 4 brought OKLCH into the platform, and design systems at Tailwind v4, Linear, Vercel, and GitHub have moved their tokens onto OKLCH since 2024. Existing design tools, brand books, and older CMSs still revolve around HEX and RGB, so in practice teams maintain values in all three formats at once.

Side-by-side comparison

PropertyRGB (sRGB)HSLOKLCH
AxesR / G / BHue / Saturation / LightnessLightness / Chroma / Hue
Meaning of the numbersDevice output channelsDesigner-friendly but non-linearLinear with respect to perception
Perceptual uniformityPoor (lightness drifts unpredictably)Mixed (cross-hue interpolation muddies)Good (derived from Oklab)
Interpolation qualityOften passes through greyClean within a hue, muddy across huesSmooth, holds chroma
GamutsRGB onlysRGB only (CSS hsl is sRGB)Can exceed sRGB into Display P3 etc.
StandardCSS 1 (1996)CSS3 Color (drafted 2003)CSS Color Module 4 (2022)
Browser supportUniversalNear-universalChrome 111+, Safari 15.4+, Firefox 113+
Notation example#ff8800 / rgb(255 136 0)hsl(32 100% 50%)oklch(0.75 0.18 50)
Typical useToken compatibility, direct-from-code valuesHand-tuned UI palettesGenerated palettes, gradients, color-mix()

OKLCH’s advantage is easy to demonstrate with numbers. Interpolating from hsl(0 100% 50%) (red) to hsl(60 100% 50%) (yellow) in HSL passes through hsl(30 100% 50%), but the human eye sees the midpoint as oddly muted. The same interpolation in OKLCH rotates only the hue while holding lightness and chroma roughly constant, so the midpoint looks like a natural orange. RGB interpolation is worst: the midpoint between rgb(255 0 0) and rgb(0 255 0) is rgb(127 127 0), a muddy olive that produces a visible grey band in the middle of a gradient. The reason color-mix(in oklch, red, yellow) looks better than in srgb is exactly this linearity gap.

Pick-by-use-case

Match existing design tokens or brand guidelines: HEX / RGB. If your team already ships #1A73E8 everywhere, there is no need to rewrite it as OKLCH — both render identically inside the sRGB gamut. Keep the values you have.

Build a “same lightness, different hue” palette: OKLCH. Fix L, rotate H in equal steps, and you get a colour wheel whose entries look the same brightness. Doing this in HSL leaves blues and purples looking dark while yellows and greens look washed out. Tailwind v4 defines --color-blue-500, --color-red-500, --color-green-500 and friends in OKLCH precisely because aligning lightness across hues by eye is impossible in HSL.

Design gradients: OKLCH. Writing linear-gradient(in oklch, red, blue) keeps midpoints saturated, and the family of in oklab / in lab / in hsl longer hue switches lets you pick the interpolation path that fits the design.

Display P3 / wide gamut: OKLCH. Values like oklch(0.7 0.3 30) can describe colours outside sRGB, and capable displays show them at their actual chroma instead of clipping. This pairs well with color(display-p3 ...) when targeting Apple hardware.

WCAG contrast calculations: RGB. WCAG 2.x defines luminance from sRGB-linear channels, so RGB is the natural starting point for contrast math. Palettes that look balanced by lightness in OKLCH still drift slightly in WCAG terms, so verify final pairs with a contrast checker before committing.

Converting and designing in the browser

The format you want is rarely the format you have. color-converter translates between HEX, RGB, HSL, HSV, OKLCH, and Lab — paste any value and the other notations appear immediately. It handles the common chores: translating #ff8800 to oklch(0.74 0.17 50), exporting an HSL-built palette as OKLCH for a new design system, or comparing how a colour quantises across spaces. Pair it with color-contrast-checker for WCAG verification, or color-blend when you need a tint between two named colours. Together they cover most palette-design work without leaving the browser.

One subtlety worth flagging: OKLCH values can look different across displays. A vivid oklch(0.7 0.3 30) on a Display P3 MacBook screen may render as a flatter colour on an older sRGB monitor because the chroma is clipped at the gamut boundary. Pick values with the final delivery target in mind, and if the difference matters, verify on representative hardware. All conversions run through the browser’s Canvas and CSS colour parsers; nothing is uploaded. The source is open on GitHub, and the DevTools Network tab confirms that no requests are made while you experiment.