Skip to article
Görkem Yıldız, home
Available for October

HSL vs Helmlab: The Difference Every Designer Should Know

Why do HSL blue and yellow at L=50 look completely different in brightness? Discover perceptual uniformity and how Helmlab fixes design.

· 7 min readEnglishTürkçe oku

You are building a design system. You create a blue color scale using HSL, incrementing L (lightness) in equal steps from dark to light. Mathematically, every step is identical. But when you look at the result, something is off: some steps are nearly indistinguishable, while others jump dramatically. The problem is not your eyes. The problem is HSL.

In this post, we will break down why HSL deceives designers, what "perceptual uniformity" actually means, and how Helmlab solves this with a color space trained on human vision data.

The HSL Deception: Same L, Different Brightness

In the HSL (Hue, Saturation, Lightness) color model, L=50 means "medium brightness." But this is a purely mathematical definition that has nothing to do with how the human eye perceives light. The proof is trivial:

HSL(240, 100%, 50%) — Blue HSL(60, 100%, 50%) — Yellow L = 50 L = 50

Both colors have L=50 in HSL. But your eyes tell a completely different story: the yellow appears far brighter, the blue far darker. This is not an illusion — the human visual system has different sensitivities across wavelengths. Our eyes are most sensitive in the yellow-green region and least sensitive in the blue-violet region. HSL completely ignores this reality.

Why does this matter? Because in a design system, "blue-500" and "yellow-500" should have the same perceived brightness. A blue button and a yellow button at the same scale position should carry the same visual weight. HSL cannot guarantee this.

What "Perceptual Uniformity" Actually Means

Perceptual uniformity is the most important property a color space can have. The definition is simple:

Equal mathematical distance in the color space = equal perceived difference to the human eye.

If the distance between colors A and B is 10 units, and the distance between C and D is also 10 units, those two pairs should look equally "different" to your eyes. Sounds obvious, but HSL fails this test catastrophically.

Think of it like a map. If 1 cm represents the same real-world distance in every direction, the map uses a "uniform" projection. HSL is like the Mercator projection — 1 cm at the equator and 1 cm near the poles represent vastly different real distances. In color perception, the same thing happens: in some HSL regions, a small change in L creates a huge perceptual shift, while in others, a large L change is barely noticeable.

The HSL Palette: "Equal" Steps That Don't Look Equal

Let's create a blue color scale with HSL. H=220 (blue), S=80%, L from 10% to 90% in equal steps:

HSL: L = 10%, 19%, 28%, 37%, 46%, 54%, 63%, 72%, 81%, 90% Each step is mathematically equal (~8.9% increment) — but not perceptually equal

Look carefully: the first few steps on the dark end (left) are nearly identical — it is hard to tell them apart. But the last few steps on the light end (right) have much more visible differences. Some steps feel "skipped," others feel cramped together.

When you use this scale in your design system, the problem becomes concrete: the difference between blue-100 and blue-200 does not look the same as the difference between blue-700 and blue-800. Every step should feel equal, but they don't.

The Helmlab Palette: True Perceptual Equal Spacing

Helmlab is a perceptual color space trained on over 64,000 human color-difference observations. With 72 parameters and a 13-stage transform pipeline, it models how the human eye actually perceives color. In Helmlab, L=50 genuinely means "medium brightness" — regardless of hue.

When you create a 10-step scale in the same blue hue with Helmlab, every step truly looks equal to your eye:

Helmlab: Perceptual L distributed in equal intervals The perceived difference between each step is equal — your eyes confirm it

Can you see the difference? In the Helmlab scale, the brightness jump between each step is homogeneous. The steps in the dark tones and the steps in the light tones look equally "different." No step feels skipped or cramped.

Side-by-Side Comparison

Same blue hue, same start and end points, but different distribution logic. Top row is HSL, bottom row is Helmlab:

HSL Helmlab 1 2 3 4 5 6 7 8 9 10 HSL: steps 1-2 almost identical HSL: steps 9-10 exaggerated Helmlab: Every step has equal perceptual difference = Uneven step (HSL) = Even step (Helmlab)

In the HSL row, the first 2-3 tiles on the dark end are nearly indistinguishable. The last 2-3 tiles on the light end have a noticeable gap. In the Helmlab row, every transition is equally visible.

Practical Impact on Design Systems

This is not just an academic difference. It has concrete consequences in real design systems:

1. Consistent Button States

You are creating normal, hover, and active states for a button. In HSL, "darken L by 10" produces different perceived changes for a blue button versus a green button. In Helmlab, the same L shift guarantees the same perceived change for every hue.

2. Accessibility (a11y)

WCAG contrast checking is luminance-based, but if you build palettes with HSL, you need to individually test contrast for every color — because L=50 passes contrast for some hues but fails for others. In Helmlab, colors at the same L level exhibit similar contrast behavior.

3. Multi-Hue Palettes

You are designing a dashboard that needs 6 different colors. Even if you set all of them to L=40 in HSL, some will appear more prominent than others. In Helmlab, 6 colors at L=0.4 genuinely carry the same visual weight.

4. Dark Mode Transitions

When switching from light mode to dark mode, you need to transform your colors. If you "invert L" in HSL, some colors end up too dark while others end up too bright. In Helmlab, inverting the L axis produces consistent results because L truly represents perceived brightness.

Code Example: Perceptual Scales with Helmlab

The Helmlab npm package is 10KB with zero dependencies. Creating a perceptual blue scale takes two lines:

Code
import { Helmlab } from 'helmlab';

const hl = new Helmlab();

// Tailwind CSS-style 50-950 scale with perceptual equal spacing
const scale = hl.semanticScale('#3B82F6');
// {
//   '50':  '#b3c7ff',  // lightest
//   '100': '#aabdff',
//   '200': '#96aaff',
//   '300': '#809aff',
//   '400': '#628eff',
//   '500': '#3b82f6',  // base color
//   '600': '#0263ca',
//   '700': '#0048a2',
//   '800': '#003179',
//   '900': '#00184b',
//   '950': '#000b2f',  // darkest
// }

You can convert any color to Helmlab Lab coordinates and see its true perceptual lightness:

Code
// HSL L=50 blue vs yellow — see the perceptual brightness difference
const blue = hl.fromSRGB(0, 0, 1);       // HSL(240, 100%, 50%)
const yellow = hl.fromSRGB(1, 1, 0);     // HSL(60, 100%, 50%)

console.log('Blue L:', blue.L.toFixed(3));   // ~0.35 — dark!
console.log('Yellow L:', yellow.L.toFixed(3)); // ~0.96 — very bright!

// HSL calls both of these L=50, but Helmlab reveals the truth:
// Yellow's perceptual brightness is nearly 3x that of blue

Measuring the perceptual distance between two colors is a single call:

Code
// deltaE: perceptual color difference (0 = identical, >0.5 = noticeable)
const lab1 = hl.fromSRGB(0.23, 0.51, 0.96);  // Tailwind blue-500
const lab2 = hl.fromSRGB(0.22, 0.48, 0.91);  // Slightly modified blue

const distance = hl.deltaE(lab1, lab2);
console.log('Perceptual difference:', distance.toFixed(4));
// A small difference — but HSL cannot even measure this correctly

When HSL Still Works

HSL is not entirely useless. For quick prototyping, color picker interfaces, and moments where you just want to say "roughly this hue," HSL is perfectly fine. The problem arises when you try to use HSL as a design system standard.

The rule is simple: use HSL to pick colors, use Helmlab to build color systems.

The Numbers

STRESS measures how well a color space's distances match human perception. Lower is better:

  • HSL — No perceptual uniformity claim; its distance metric is meaningless
  • CIE Lab — STRESS: ~30.3
  • CIEDE2000 — STRESS: 29.2 (industry standard)
  • Oklab — STRESS: ~27.5 (CSS standard)
  • Helmlab — STRESS: 23.2 (best)

Helmlab beats the industry standard CIEDE2000 by 20%. This difference translates to visibly better palette generation and more accurate contrast calculations.

Conclusion

HSL is a convenient tool for defining colors in CSS. But when it says "L=50," it cannot guarantee that you will get a color of medium perceptual brightness. For building design systems — palette generation, contrast calculation, dark mode transitions — HSL falls short wherever perceptual accuracy matters.

Helmlab is a perceptual color space with 72 parameters, trained on over 64,000 human observations. When it says "equal step," it truly is an equal step. When you set blue and yellow to the same lightness, they genuinely appear equally bright.

Terminal
npm install helmlab

Source code: GitHub | Package: npm

This is post 2 in the Helmlab blog series. In the next post, we will compare CIEDE2000 with Helmlab.

Görkem Yıldız

Helmlab, a family of perceptual color spaces for UI design systems (arXiv:2602.23010)

I'm Görkem, a UI/UX designer and engineer based in Ankara, leading the design team at [PushBrands](https://pushbrands.app/en), a Saudi UGC marketplace. I've spent over eight years designing and building digital products, and lately the systems underneath them: Helmlab, a color space trained on 64,000 human color judgments that now ships in Color.js; open-source tools for icons, sound and the terminal; and, as Orbit Design, Figma plugins with more than 100,000 users.

Book a call

Talk about color, design systems or Helmlab.

Saturdays 19:00–21:00 Istanbul time, on Google Meet.

Book a call