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

13-Stage Transform: Inside the Helmlab Pipeline

How does Helmlab get from XYZ to Lab in 13 stages? We break down each stage, explain why it exists, and why 72 parameters work.

· 12 min readEnglishTürkçe oku

Most color spaces finish their work with a single matrix multiplication. Oklab: one 3x3 matrix, cube root, another 3x3 matrix -- 18 parameters total. CIE Lab: a cube root function -- zero free parameters. Helmlab uses a 13-stage pipeline with 72 optimized parameters. This might look "unnecessarily complex." But these 13 stages respond to the roughly 150-million-year evolutionary complexity of the human visual system. Every stage has a reason. In this post, we examine those reasons.

Why a Pipeline? Is a Single Matrix Not Enough?

Human color perception is nonlinear. A matrix multiplication expresses linear transforms: it can rotate axes, scale, and shift. But perception does more than that:

  • Power compression -- The retina responds to light intensity in a roughly logarithmic manner. Doubling the physical light does not double the perceived brightness.
  • Hue-dependent corrections -- Our eyes do not perceive every hue with equal sensitivity. We are more sensitive in the blue-green transition, less so in the red-orange range.
  • Brightness-saturation interaction -- Saturated colors appear brighter than grays at the same luminance (the Helmholtz-Kohlrausch effect, covered in Post 6).
  • Dark region behavior -- Perceptual differences scale differently for very dark colors.
  • Chrominance-lightness coupling -- Chroma perception changes at very light and very dark levels.

A single matrix cannot capture any of these behaviors. Two matrices and a power function (Oklab's approach) can capture a few. But modeling all of them requires a sequence of stages, each targeting a specific perceptual phenomenon. Helmlab's pipeline is that sequence.

The 13 Stages: Full Pipeline Map

Below is a flowchart of the complete pipeline, followed by a detailed explanation of each stage.

Helmlab Pipeline: XYZ → Lab (13 Stages, 72 Parameters) 1. XYZ → M1 (3x3 matrix) 9 params -- cone response transform 2. Power Compression (gamma) 3 params -- [0.395, 0.421, 0.434] 3. M2 → Lab (3x3 matrix) 9 params -- opponent color channels 4. Hue Correction (4-harmonic Fourier) 8 params -- perceptual hue circle alignment 5. Helmholtz-Kohlrausch Embedding 3 params -- saturation → brightness 6. Hue-Dependent Lightness (Fourier) 8 params -- fine-tune L by hue 7. Cubic L Correction 2 params -- dark/light extremes 8. Dark L Compression (hue-dependent) 4 params -- special dark color treatment 9. Chroma Scaling (4-harmonic Fourier) 8 params -- hue-dependent chroma calibration 10. Chroma Power Function 1 param -- high saturation compression 11. L-Dependent Chroma Scaling 2 params -- chroma varies with lightness 12. Neutral Correction (NC) LUT -- guarantees a=b=0 for grays 13. Rigid Rotation (-28.2°) 0 params -- hue angle alignment (STRESS-invariant) Helmlab Lab (L, a, b) Matrix transform Power function Fourier series L correction Structural fix Total: 72 optimized parameters | Trained on 64,000+ human observations

Stage 1: XYZ to M1 (9 parameters)

The entry point is CIE XYZ coordinates -- the standard representation of physical light measurements. M1 is a 3x3 matrix multiplication that transforms XYZ into "cone-like" responses. Why not use LMS (cone responses) directly? Because classical LMS matrices (Hunt-Pointer-Estevez, Bradford) are derived from physiological measurements, not perceptual experiments. Every element of M1 is optimized against perceptual distance data to arrive at perceptually accurate cone responses.

Stage 2: Power Compression (3 parameters)

Each channel undergoes an independent x → x^gamma transform. The gammas are [0.395, 0.421, 0.434]. These are close to the cube root (0.333) but differ across channels -- because each cone type has a different compression profile. These independent gammas are a critical design decision in v19: a shared gamma (tried in v18) loses 4 degrees of freedom and degrades performance.

Stage 3: M2 to Lab (9 parameters)

The second 3x3 matrix separates the compressed cone responses into opponent color channels: one lightness axis (L) and two chrominance axes (a, b). This is the analytical counterpart of what the visual cortex does after the retina. Oklab stops here -- Helmlab is just getting started.

Stage 4: Hue Correction (8 parameters)

The first three stages do not guarantee a correct hue circle. Physically equal wavelength intervals may not map to perceptually equal hue steps. This stage uses a 4-harmonic Fourier series to correct hue angles in the ab plane:

Code
// Hue angle correction: h' = h + f(h)
// f(h) = sum(a_k * cos(k*h) + b_k * sin(k*h)), k=1..4
// 8 parameters: 4 cosine, 4 sine coefficients

The result: a hue circle where perceptually equal hue steps are genuinely represented by equal angles.

Stage 5: Helmholtz-Kohlrausch Embedding (3 parameters)

Saturated colors appear brighter than grays at the same luminance (covered in detail in Post 6). This stage adds a chroma-dependent L shift: L += w * C^p * hueFactor(h). The parameter count is 3: weight, exponent, and hue modulation. The hue modulation leverages the Fourier series from stage 4. This closes the biggest blind spot of Oklab and CIE Lab.

Stage 6: Hue-Dependent Lightness (8 parameters)

Beyond the H-K effect, some hues are perceived as inherently "heavier" or "lighter." Yellow appears brighter than other hues -- even when its luminance value is lower. This stage applies a hue-dependent fine-tuning to L using a 4-harmonic Fourier series.

Stage 7: Cubic L Correction (2 parameters)

Power functions work well in the midrange but at the extremes -- very dark and very light -- perceptual differences scale differently. A cubic polynomial corrects the L axis in these endpoint regions. Just 2 parameters deliver a substantial improvement.

Stage 8: Dark L Compression (4 parameters)

The dark region demands special attention: our eyes are more sensitive to small luminance differences in darkness (the Weber-Fechner law). This stage applies a hue-dependent compression to fine-tune the L values of dark colors. Dark blue and dark green, for instance, have different compression profiles.

Stage 9: Chroma Scaling (8 parameters)

Each hue has a different chroma range. Red spans a wide chroma gamut while yellow saturates within a narrower range. A 4-harmonic Fourier series scales chroma by hue to balance these differences.

Stage 10: Chroma Power Function (1 parameter)

At high saturation, perceptual chroma grows more slowly than physical chroma. A single exponent parameter applies the C → C^p transform to model this compression.

Stage 11: L-Dependent Chroma Scaling (2 parameters)

Chroma perception depends on lightness. At very light and very dark levels, our capacity to perceive chroma diminishes. Two parameters scale chroma as a function of L.

Stage 12: Neutral Correction -- NC (LUT)

This is v19's most critical innovation. A gray (R=G=B) passing through 11 stages should theoretically land at a=b=0. However, the combination of 3 independent gammas and 2 independent matrices causes grays to drift to small but nonzero a, b values. NC uses a 256-point LUT (lookup table) to compute and subtract this deviation:

Code
// NC: gray correction at pipeline end
// a_err(L), b_err(L): gray deviations (PCHIP interpolation)
// Correction: a -= a_err(L), b -= b_err(L)
// Result: a=b=0 GUARANTEE for all grays

Why at the pipeline end? Applying NC earlier (for example, right after M2) is catastrophic. An early correction shifts the hue angle, causing all subsequent stages (H-K, chroma scaling, dark_L) to follow an entirely different trajectory. For extreme colors, L can diverge from 0.94 to 7.27. Applying it at the pipeline end ensures that earlier stages see the original hue and only the final output is corrected.

Stage 13: Rigid Rotation -- -28.2 degrees

The final stage is a fixed-angle rotation in the ab plane. This stage has zero free parameters -- the angle was determined post-optimization by a minimax criterion. Why does it not affect STRESS? Because the da^2 + db^2 term in the distance formula is invariant under rotation (isometry). This is a free improvement:

  • Hue RMS error: 25.9° → 16.1°
  • Hue maximum error: 48.4° → 20.2°
  • COMBVD (STRESS): unchanged -- literally 0.0000000000 difference

Invertibility: Every Stage Can Be Reversed

For a color space to be practically useful, the Lab to XYZ direction (inverse transform) is at least as important as the forward direction. You cannot leave the question "What physical light produces these L, a, b values?" unanswered. Every one of Helmlab's 13 stages is exactly invertible:

  • Matrix stages (1, 3) -- Multiply by the matrix inverse: M1_inv * x.
  • Power functions (2, 10) -- Inverse exponent: x^(1/gamma).
  • Addition/subtraction stages (5, 6, 7, 8, 11, 12) -- Subtract the value added in the forward pass.
  • Hue correction (4) -- This is where Newton iteration is needed. In the equation h' = h + f(h), finding h given h' requires: h_{n+1} = h_n - (h_n + f(h_n) - h') / (1 + f'(h_n)). Typically converges to machine precision in 3-5 iterations.
  • Chroma scaling (9) -- Simple division once the hue-dependent multiplier is known.
  • Rigid rotation (13) -- Rotate back by +28.2°.

The round-trip error (forward + inverse transform) is on the order of 1e-6 -- near machine precision. This guarantees that every Helmlab coordinate maps to a unique physical color.

72 Parameters: Too Many?

72 parameters might look intimidating at first glance. But this number needs context:

Parameter Count Comparison Model Parameters COMBVD STRESS CIE Lab 0 (zero free) ~30.3 Oklab 18 ~27.5 CIEDE2000 5 (patches) 29.18 Helmlab 72 23.22

What matters is not the raw parameter count but value per parameter and overfitting risk. Let us examine three key points:

Value per parameter

Helmlab reduces STRESS from 30.3 to 23.22 with 72 parameters -- a 7.1-point improvement. Oklab reduces it from 30.3 to roughly 27.5 with 18 parameters -- a 2.8-point improvement. STRESS improvement per parameter: 0.099 for Helmlab, 0.156 for Oklab. Oklab is more efficient per parameter -- but absolute performance is far better with Helmlab. CIEDE2000's 5 "patch" parameters lower it from 30.3 to 29.18 -- 0.22 per parameter.

Analytical form: a natural regularizer

If 72 parameters belonged to a neural network, overfitting would be a serious risk. But every Helmlab parameter is tied to a specific mathematical function: a matrix element, a Fourier coefficient, an exponent value. This analytical form constrains the parameter space. It cannot fit arbitrary curves -- it can only express physically meaningful transforms. Our experimental data contains 3,813 color pairs (64,000+ observations), giving a parameter-to-data ratio of 1:53. For a typical neural network, even a 1:10 ratio would be considered generous.

Cross-validation

Helmlab is trained with an 80/20 train/validation split (seed=42). The validation gap is only +1.11 STRESS points -- demonstrating that the model generalizes well to unseen data. In the case of overfitting, this gap would be much larger.

The Distance Formula: Minkowski + SL/SC

A color space does not only compute coordinates -- it must also measure the perceptual difference (deltaE) between two colors. Helmlab's distance formula includes corrections beyond simple Euclidean distance:

Code
// Helmlab distance formula
// Input: Lab coordinates of two colors

const dL = L2 - L1;
const da = a2 - a1;
const db = b2 - b1;

// Pair-dependent weights (SL, SC)
const L_avg = (L1 + L2) / 2;
const C_avg = (C1 + C2) / 2;
const SL = 1 + sl * (L_avg - 0.5) ** 2;  // dark/light pairs
const SC = 1 + sc * C_avg;                // chromatic pairs

// Minkowski distance (p ~= 2)
const DE = ((dL / SL) ** 2
          + wC * ((da / SC) ** 2 + (db / SC) ** 2))
          ** (p / 2);

// Final corrections
const result = (DE / (1 + c * DE)) ** q;

The key components of this formula:

  • SL: An L-average-dependent weight. The perceptual importance of lightness differences changes for dark and light pairs. SL captures this variation.
  • SC: A chroma-average-dependent weight. Chrominance differences are less noticeable in high-chroma pairs. SC models this.
  • wC: The chrominance/lightness balance. Is human vision more sensitive to L differences or to color differences? wC adjusts this ratio.
  • p: The Minkowski exponent. p=2 gives Euclidean distance; p<2 amplifies small differences. The optimized value is approximately 1.8.
  • q: A final power correction (1.1). Models the perceptual "saturation" of large differences.

The SL and SC parameters are inspired by similar mechanisms in CIEDE2000 -- but in Helmlab they operate on Helmlab Lab coordinates, not CIE Lab. This subtle difference matters: since Helmlab's Lab already incorporates the H-K effect, hue correction, and the other 10 stages, the distance formula operates on a cleaner foundation.

In Practice: Forward and Inverse Transform

Code
import { Helmlab } from 'helmlab';

const hl = new Helmlab();

// Forward transform: sRGB -> Helmlab Lab
const lab = hl.fromSRGB(0.2, 0.6, 0.9);
console.log('L:', lab.L.toFixed(4));
console.log('a:', lab.a.toFixed(4));
console.log('b:', lab.b.toFixed(4));

// Inverse transform: Helmlab Lab -> sRGB
const rgb = hl.toSRGB(lab.L, lab.a, lab.b);
console.log('R:', rgb.r.toFixed(6));
console.log('G:', rgb.g.toFixed(6));
console.log('B:', rgb.b.toFixed(6));
// -> (0.200000, 0.600000, 0.900000)
// Round-trip error: ~1e-6

// Distance measurement
const dE = hl.deltaE(
  { r: 0.2, g: 0.6, b: 0.9 },
  { r: 0.3, g: 0.5, b: 0.8 }
);
console.log('deltaE:', dE.toFixed(4));

The 13 stages are completely transparent to the user. The fromSRGB and toSRGB calls apply all 13 stages sequentially (or their inverses). API simplicity hides internal complexity -- as it should.

Conclusion: Why Complexity Matters

Helmlab's 13 stages are not arbitrary complexity. Each stage models a specific property of the human visual system:

  • Stages 1-3: Fundamental physics-to-perception transform (matrix + power)
  • Stages 4-8: Lightness and hue refinements (Fourier + polynomial)
  • Stages 9-11: Chroma calibration (Fourier + power + L-coupling)
  • Stages 12-13: Structural guarantees (NC + rotation)

Each stage can be removed, and the loss from removing it can be measured. None are decorative. The 72 parameters are optimized against 64,000+ human observations, cross-validated, and protected against overfitting by the analytical form.

The result: STRESS 23.22 -- well below CIEDE2000's 29.18 and Oklab's roughly 27.5. Pipeline complexity is the price paid for perceptual accuracy. And it ships in a 10.3 KB npm package:

Terminal
npm install helmlab

Source code: GitHub | Package: npm

This is post 7 of 16 in the Helmlab series. Next up: Gamut Mapping -- how to safely handle colors outside the sRGB boundary.

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