Color Interpolation
How colors blend between stops in gradients, animations and color-mix() — and why the interpolation space makes a visible difference.
color-mix() function. The result depends entirely on which color space is used — sRGB, HSL, OKLab or OKLCH will each produce visibly different middle colors.The interpolation problem
Given two colors, what color lies halfway between them? The answer is not obvious. "Halfway" means different things depending on how you describe each color numerically. If you write the colors in sRGB, halfway is the arithmetic midpoint of three channel values. If you write them in OKLab, halfway is the perceptual midpoint. These are usually not the same color.
This is why the same two stops can produce very different gradients in different tools — and why color-mix() requires you to specify the interpolation space.
How different spaces interpolate
| Space | Behavior | Result |
|---|---|---|
| sRGB | Linear midpoints of R, G and B channels | Fast; muddy mid-tones between distant hues |
| Linear sRGB | Linear midpoints after gamma decoding | Physically correct; still muddy for distant hues |
| HSL | Hue rotates, S and L interpolate | Bright middle for analogous hues, wrong direction for complementary |
| OKLab | Perceptually uniform linear interpolation | Smooth and consistent for all hue pairs |
| OKLCH | Hue rotates, C and L interpolate | Smooth and predictable — best for gradients with vivid mid-tones |
Why sRGB gradients look muddy
Consider a gradient from blue to yellow in sRGB. At the midpoint, the RGB values average to a mid-gray — because blue is (0, 0, 255) and yellow is (255, 255, 0). The average is (127, 127, 127). That is a muted gray, not a color that feels halfway between blue and yellow.
Interpolate the same pair in OKLCH and the result passes through a rich green-cyan. This is because OKLCH interpolates hue, chroma and lightness as separate dimensions, so the hue rotates from blue to yellow without losing saturation or lightness.
Hue interpolation — shortest path
When interpolating in any polar space (HSL, LCH, OKLCH), the hue is an angle. Going from 10° to 350° could be a 20° counter-clockwise rotation or a 340° clockwise one. Browsers and libraries choose the shortest path by default — the 20° option — which produces a natural transition.
From 10° to 350°
direct difference: 350 − 10 = 340°
shortest: 340 − 360 = −20° (i.e. rotate counter-clockwise)
Most libraries clamp Δh to [−180°, +180°] to force the shortest path.
CSS: color-mix() and gradient hints
Modern CSS lets you specify the interpolation space directly.
/* color-mix() with explicit interpolation space */
background: color-mix(in oklab, blue, yellow);
color: color-mix(in srgb, red 40%, white);
border-color: color-mix(in oklch, #4f46e5, black 20%);
/* Gradient interpolation hint */
background: linear-gradient(in oklch, blue, yellow);
background: radial-gradient(in oklab, red, green, blue);
/* Long interpolation across the hue wheel */
background: linear-gradient(in oklch longer hue, red, magenta);
Practical examples
Blue to yellow
sRGB — muddy gray midpoint
OKLCH — smooth green-cyan midpoint
Red to green
sRGB — muddy brown midpoint
OKLCH — vivid yellow midpoint
Which space should I use?
| Task | Recommended space |
|---|---|
| Gradients between distant hues | OKLab or OKLCH |
| Gradients between similar hues | sRGB is fine |
| UI state transitions | OKLab or OKLCH for consistent perceived lightness |
| Icon and brand system blending | OKLCH for hue control |
| Print production | Lab (industry standard for color science) |
| Matching a legacy tool | sRGB to match the browser default |
Continue reading
Frequently asked questions
What is color interpolation?
Color interpolation is the process of producing intermediate colors between two or more stops. It happens in gradients, animations, opacity fades and the CSS color-mix() function. The colors you get in between depend entirely on which color space is used.
Why do gradients look muddy in sRGB?
Because sRGB is not perceptually uniform. Interpolating complementary or distant hues in sRGB passes through darker, less saturated values, producing a muddy or grayish middle. Interpolating in OKLab or OKLCH avoids this.
What is the best space for interpolation?
OKLab is generally the best choice — it is perceptually uniform and its Cartesian form avoids hue-rotation issues. OKLCH is a good second choice if you want to control hue and chroma separately. sRGB remains the browser default and is fine for closely-related colors.
How do I specify interpolation in CSS?
Use the color-mix() function with an explicit space, such as color-mix(in oklab, blue, red). For gradients, use the interpolation hint: linear-gradient(in oklch, red, blue).