Skip to main content

The Muddy Middle Problem

When interpolating between two vibrant colors in sRGB, the intermediate values often pass through a desaturated, grayish region. This is the “muddy middle” problem that makes sRGB gradients look unprofessional.
┌───────────────────────────────────────────────────────────────────────────────┐
│                    COLOR SPACE GEOMETRY COMPARISON                             │
├───────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│   sRGB (Gamma-Corrected RGB)                                                  │
│   ==========================                                                  │
│                                                                               │
│   Structure: 3D Cube (R, G, B axes)                                          │
│                                                                               │
│        G (Green)                                                              │
│        ^                                                                      │
│        │      ┌────────┐                                                      │
│        │     /│       /│                                                      │
│        │    / │      / │                                                      │
│        │   ┌────────┐  │                                                      │
│        │   │  │     │  │                                                      │
│        │   │  └─────│──┘ ────→ R (Red)                                      │
│        │   │ /      │ /                                                       │
│        │   │/       │/                                                        │
│        │   └────────┘                                                         │
│        │  /                                                                   │
│        │ /                                                                    │
│        │/                                                                     │
│        └──────────────────────→ B (Blue)                                      │
│                                                                               │
│   Problem: Straight line in RGB != Perceptually straight                     │
│                                                                               │
├───────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│   OKLAB (Perceptually Uniform)                                               │
│   ============================                                               │
│                                                                               │
│   Structure: Cylindrical (L, a, b axes)                                      │
│                                                                               │
│        L (Lightness)                                                          │
│        ^                                                                      │
│        │                                                                      │
│        │      . . .                                                           │
│        │   .         .                                                        │
│        │  .           .                                                       │
│        │ .      +      .  <── Neutral axis (a=0, b=0)                        │
│        │  .           .                                                       │
│        │   .         .                                                        │
│        │      . . .                                                           │
│        │         │                                                            │
│        └─────────│────────→ a (green-red axis)                              │
│                 /                                                             │
│                /                                                              │
│               /                                                               │
│              v b (blue-yellow axis)                                           │
│                                                                               │
│   Advantage: Straight line in OKLAB = Perceptually straight                  │
│                                                                               │
└───────────────────────────────────────────────────────────────────────────────┘

Blue to Yellow: The Classic Example

The blue-to-yellow gradient demonstrates sRGB’s fundamental flaw most clearly.
┌───────────────────────────────────────────────────────────────────────────────┐
│                    BLUE TO YELLOW INTERPOLATION                                │
├───────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│   sRGB Interpolation Path (Through Gray Zone):                               │
│   =============================================                               │
│                                                                               │
│                      Lightness                                                │
│                         ^                                                     │
│                         │                                                     │
│                    1.0  ┼                    * Yellow (1.0, 1.0, 0.0)        │
│                         │                  /                                  │
│                         │                /                                    │
│                    0.7  ┼              *  <── Muddy brown/gray               │
│                         │            /                                        │
│                         │          /                                          │
│                    0.5  ┼        *  <── DESATURATED ZONE                     │
│                         │      /                                              │
│                         │    /                                                │
│                    0.3  ┼  *  <── Muddy gray                                 │
│                         │/                                                    │
│                    0.0  * Blue (0.0, 0.0, 1.0)                               │
│                         ┼─────┼─────┼─────┼─────┼─────┼────→ Chroma        │
│                         0    0.1   0.2   0.3   0.4   0.5                     │
│                                                                               │
│   Notice: Path curves INWARD toward neutral axis (low chroma)                │
│                                                                               │
├───────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│   OKLAB Interpolation Path (Direct):                                         │
│   ===================================                                         │
│                                                                               │
│                      Lightness                                                │
│                         ^                                                     │
│                         │                                                     │
│                    1.0  ┼                    * Yellow                         │
│                         │                   /│                                │
│                         │                  / │                                │
│                    0.7  ┼                 *  │  <── Cyan-ish (clean)         │
│                         │                /   │                                │
│                         │               /    │                                │
│                    0.5  ┼              *     │  <── Vibrant green-cyan       │
│                         │             /      │                                │
│                         │            /       │                                │
│                    0.3  ┼           *        │  <── Clean transition         │
│                         │          /         │                                │
│                    0.0  ┼         * Blue     │                                │
│                         ┼─────┼─────┼─────┼─────┼─────┼────→ Chroma        │
│                         0    0.1   0.2   0.3   0.4   0.5                     │
│                                                                               │
│   Notice: Path maintains CONSTANT CHROMA (saturation preserved)              │
│                                                                               │
└───────────────────────────────────────────────────────────────────────────────┘

Mathematical Definition

Linear interpolation (lerp) produces dramatically different results depending on color space.
┌───────────────────────────────────────────────────────────────────────────────┐
│                    LINEAR INTERPOLATION FORMULA                                │
├───────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│   Generic Formula:                                                            │
│   ================                                                            │
│                                                                               │
│   result = A * (1 - t) + B * t                                               │
│                                                                               │
│   Where:                                                                      │
│   - A = start color                                                           │
│   - B = end color                                                             │
│   - t = interpolation parameter [0, 1]                                       │
│   - t=0 gives A, t=1 gives B, t=0.5 gives midpoint                          │
│                                                                               │
├───────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│   In sRGB:                                                                    │
│   =========                                                                   │
│                                                                               │
│   R_result = R_A * (1 - t) + R_B * t                                        │
│   G_result = G_A * (1 - t) + G_B * t                                        │
│   B_result = B_A * (1 - t) + B_B * t                                        │
│                                                                               │
│   Example: Blue (0, 0, 255) to Yellow (255, 255, 0) at t=0.5                │
│                                                                               │
│   R = 0   * 0.5 + 255 * 0.5 = 127.5                                         │
│   G = 0   * 0.5 + 255 * 0.5 = 127.5                                         │
│   B = 255 * 0.5 + 0   * 0.5 = 127.5                                         │
│                                                                               │
│   Result: rgb(127.5, 127.5, 127.5) = GRAY!                                   │
│                                                                               │
├───────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│   In OKLAB:                                                                   │
│   ==========                                                                  │
│                                                                               │
│   L_result = L_A * (1 - t) + L_B * t                                        │
│   a_result = a_A * (1 - t) + a_B * t                                        │
│   b_result = b_A * (1 - t) + b_B * t                                        │
│                                                                               │
│   Blue in OKLAB:   L=0.45, a=-0.03, b=-0.31                                 │
│   Yellow in OKLAB: L=0.97, a=-0.07, b=0.20                                  │
│                                                                               │
│   At t=0.5:                                                                   │
│   L = 0.45 * 0.5 + 0.97 * 0.5 = 0.71                                        │
│   a = -0.03 * 0.5 + -0.07 * 0.5 = -0.05                                     │
│   b = -0.31 * 0.5 + 0.20 * 0.5 = -0.055                                     │
│                                                                               │
│   Result: A vibrant cyan-ish color (NOT gray!)                               │
│                                                                               │
└───────────────────────────────────────────────────────────────────────────────┘

OKLAB vs OKLCH: Cartesian vs Polar

OKLAB and OKLCH represent the same perceptually uniform color space in different coordinate systems.
┌───────────────────────────────────────────────────────────────────────────────┐
│                    OKLAB vs OKLCH COORDINATE SYSTEMS                           │
├───────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│   OKLAB (Cartesian):                                                          │
│   ==================                                                          │
│                                                                               │
│   Coordinates: L (Lightness), a (green-red), b (blue-yellow)                 │
│                                                                               │
│                    a+                                                         │
│                    │                                                          │
│                    │     P(a, b)                                              │
│                    │    /                                                     │
│                    │   /                                                      │
│                    │  /                                                       │
│                    │ /                                                        │
│        b─ ────────┼┼────────  b+                                             │
│                    │                                                          │
│                    │                                                          │
│                    a─                                                         │
│                                                                               │
│   Best for: INTERPOLATION (straight line = perceptual straight line)         │
│                                                                               │
├───────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│   OKLCH (Polar/Cylindrical):                                                  │
│   ==========================                                                  │
│                                                                               │
│   Coordinates: L (Lightness), C (Chroma), H (Hue angle)                      │
│                                                                               │
│                    0deg (Red)                                                 │
│                    │                                                          │
│                    │     P(C, H)                                              │
│                    │    /                                                     │
│                    │   / C (radius)                                           │
│                    │  /                                                       │
│                    │ / H (angle)                                              │
│     270deg ────────┼──────── 90deg                                           │
│     (Blue)         │         (Yellow)                                         │
│                    │                                                          │
│                    180deg (Green)                                             │
│                                                                               │
│   Best for: DEFINING colors (human-intuitive: hue, saturation, brightness)   │
│                                                                               │
├───────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│   Conversion Between:                                                         │
│   ===================                                                         │
│                                                                               │
│   OKLAB to OKLCH:                                                            │
│   C = sqrt(a^2 + b^2)                                                        │
│   H = atan2(b, a)                                                            │
│                                                                               │
│   OKLCH to OKLAB:                                                            │
│   a = C * cos(H)                                                             │
│   b = C * sin(H)                                                             │
│                                                                               │
└───────────────────────────────────────────────────────────────────────────────┘

Side-by-Side Comparison

┌───────────────────────────────────────────────────────────────────────────────┐
│                    sRGB vs OKLAB: SIDE-BY-SIDE COMPARISON                      │
├───────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│   Red (H=25) to Blue (H=230):                                                │
│                                                                               │
│   sRGB Path:                                                                  │
│   ==========                                                                  │
│                                                                               │
│   RED ──── muddy ──── brown ──── purple-ish ──── gray ──── BLUE              │
│    │         │          │           │            │          │                │
│   t=0      t=0.2      t=0.4       t=0.6        t=0.8      t=1.0              │
│                                                                               │
│   The path is UNPREDICTABLE. Intermediate colors feel "dirty".               │
│                                                                               │
├───────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│   OKLAB Path:                                                                 │
│   ===========                                                                 │
│                                                                               │
│   RED ──── pink ──── magenta ──── violet ──── purple ──── BLUE               │
│    │         │          │           │            │          │                │
│   t=0      t=0.2      t=0.4       t=0.6        t=0.8      t=1.0              │
│                                                                               │
│   The path is PREDICTABLE. Intermediate colors are vibrant and logical.      │
│                                                                               │
├───────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│   Green (H=130) to Yellow (H=85):                                            │
│                                                                               │
│   sRGB Path:                                                                  │
│   ==========                                                                  │
│                                                                               │
│   GREEN ──── muddy ──── olive ──── brown-ish ──── YELLOW                     │
│     │          │          │            │            │                        │
│   t=0       t=0.25     t=0.5        t=0.75       t=1.0                       │
│                                                                               │
│   Even nearby colors have uneven transitions!                                 │
│                                                                               │
├───────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│   OKLAB Path:                                                                 │
│   ===========                                                                 │
│                                                                               │
│   GREEN ──── lime ──── chartreuse ──── yellow-green ──── YELLOW              │
│     │          │            │               │              │                 │
│   t=0       t=0.25       t=0.5          t=0.75          t=1.0                │
│                                                                               │
│   Smooth, predictable, visually even steps.                                   │
│                                                                               │
└───────────────────────────────────────────────────────────────────────────────┘

Core-to-Core Flow Pattern

When interpolating between two Core Colors (both defined in OKLCH), the system converts to OKLAB for the actual interpolation.
┌───────────────────────────────────────────────────────────────────────────────┐
│                    CORE-TO-CORE FLOW INTERPOLATION                             │
├───────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│   Scenario: Git branch merge line between two branches                        │
│                                                                               │
│   Core A (main branch):     oklch(55% 0.15 230)  -- Blue                     │
│   Core B (feature branch):  oklch(55% 0.15 50)   -- Orange                   │
│                                                                               │
│   Method: color-mix(in oklab, coreA, coreB 50%)                              │
│                                                                               │
├───────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│   Conversion Process:                                                         │
│   ===================                                                         │
│                                                                               │
│   1. Core A (OKLCH) --> Convert --> OKLAB                                    │
│      oklch(55% 0.15 230)                                                     │
│      L=55, C=0.15, H=230deg                                                  │
│         │                                                                     │
│         v                                                                     │
│      L=0.55, a=-0.10, b=-0.11                                                │
│                                                                               │
│   2. Core B (OKLCH) --> Convert --> OKLAB                                    │
│      oklch(55% 0.15 50)                                                      │
│      L=55, C=0.15, H=50deg                                                   │
│         │                                                                     │
│         v                                                                     │
│      L=0.55, a=0.10, b=0.11                                                  │
│                                                                               │
│   3. Interpolate in OKLAB:                                                    │
│      L = 0.55 * 0.5 + 0.55 * 0.5 = 0.55                                     │
│      a = -0.10 * 0.5 + 0.10 * 0.5 = 0.00                                    │
│      b = -0.11 * 0.5 + 0.11 * 0.5 = 0.00                                    │
│                                                                               │
│   4. Result: L=0.55, a=0, b=0 (NEUTRAL GRAY at 55% lightness)               │
│                                                                               │
├───────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│   Important Clarification:                                                    │
│   ========================                                                    │
│                                                                               │
│   Blue and Orange are OPPOSITE on the color wheel (180 apart).               │
│   Their true midpoint IS neutral. This is mathematically correct.            │
│                                                                               │
│   The DIFFERENCE from sRGB:                                                   │
│   - sRGB midpoint: Often muddy brown (uncontrolled desaturation)            │
│   - OKLAB midpoint: Clean neutral gray (predictable, intentional)            │
│                                                                               │
│   For non-opposite colors, OKLAB maintains chroma:                           │
│   Core A: oklch(55% 0.15 230)  -- Blue                                       │
│   Core C: oklch(55% 0.15 160)  -- Cyan-Green                                 │
│   Midpoint: oklch(55% ~0.15 195)  -- Maintains chroma, hue averages!        │
│                                                                               │
└───────────────────────────────────────────────────────────────────────────────┘

Perceptual Distance

In a perceptually uniform color space, equal numerical distances correspond to equal perceived differences.
┌───────────────────────────────────────────────────────────────────────────────┐
│                    PERCEPTUAL DISTANCE FORMULA                                 │
├───────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│   Delta E (Euclidean distance in OKLAB):                                      │
│                                                                               │
│   dE = sqrt( (L2-L1)^2 + (a2-a1)^2 + (b2-b1)^2 )                            │
│                                                                               │
│   Where:                                                                      │
│   - L1, L2 = Lightness values                                                │
│   - a1, a2 = Green-Red axis values                                           │
│   - b1, b2 = Blue-Yellow axis values                                         │
│                                                                               │
├───────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│   Property: If dE values are equal, perceived differences are equal.          │
│                                                                               │
│   This means:                                                                 │
│   - Interpolating produces evenly spaced visual steps                        │
│   - No "faster" or "slower" regions in color transitions                     │
│   - The gradient feels smooth and natural                                    │
│                                                                               │
├───────────────────────────────────────────────────────────────────────────────┤
│                                                                               │
│   In sRGB, this property does NOT hold:                                       │
│                                                                               │
│   - Moving from dark to mid-gray feels different than mid-gray to light      │
│   - Yellow appears much brighter than blue at same sRGB value                │
│   - Saturation changes unpredictably across hues                             │
│                                                                               │
└───────────────────────────────────────────────────────────────────────────────┘

THE CENTER

Why Clean Interpolation Matters for Information Flow

In Human-AI collaboration, color gradients communicate relationships between data points. When a merge line connects two branches, the gradient color should intuitively show “this belongs to both.” Muddy sRGB interpolation obscures this relationship, making the user’s cognitive load heavier.
Connection to Core-Flow:
├── Core colors define identity (OKLCH)
├── Flow colors show relationships (OKLAB interpolation)
├── Clean gradients = clear communication of connections
└── Muddy gradients = confusing, broken visual language
The key advantage of OKLAB is predictability. Designers can reason about color transitions mathematically, knowing that the intermediate colors will be clean and logical.

CSS Gradients

Using linear-gradient with OKLAB interpolation