wandres.dev
GRADIENTES · Interpolación, ángulos y cónicos

Elegir el espacio de interpolación de un degradado

Los dos defectos distintos que ensucian un degradado de azul a amarillo, cuál arregla cada espacio, los cuatro métodos de tono, y el efecto secundario de interpolar en polar.

⏱ 21 min

El degradado de azul a amarillo que pasa por un gris muerto es el ejemplo canónico de que sRGB no sirve para interpolar, y la explicación que se da casi siempre es incompleta. Ahí no hay un defecto sino dos, con causas distintas y con arreglos distintos, y confundirlos lleva a elegir mal el espacio. Con los números delante se ve exactamente qué arregla cada opción y qué no arregla ninguna.

🎯 Al terminar esta lección sabrás
  • Distinguir el defecto de la gamma del defecto de la trayectoria rectilínea.
  • Elegir entre espacio rectangular y polar sabiendo qué hace cada uno.
  • Usar los cuatro métodos de interpolación de tono.
  • Anticipar que una interpolación polar puede salirse del gamut.

Dos defectos en el mismo degradado

Toma linear-gradient(#0000ff, #ffff00) y mide los colores intermedios en los cuatro espacios candidatos. Esto es lo que sale:

Punto in srgb in srgb-linear in oklab in oklch
25% #4040bf #8989e1 #1d77e7 #008df8
50% #808080 #bcbcbc #6cabc7 #00cfbd
75% #bfbf40 #e1e189 #b5d797 #19f268

Y las mismas coordenadas medidas en claridad y croma:

Punto in srgb in srgb-linear in oklab in oklch
25% L 46%, C 0.194 L 67%, C 0.128 L 58%, C 0.189 L 64%, C 0.187
50% L 60%, C 0 L 79%, C 0 L 71%, C 0.077 L 77%, C 0.136
75% L 78%, C 0.146 L 89%, C 0.110 L 84%, C 0.094 L 84%, C 0.237

Lee las dos tablas juntas y aparecen los dos problemas separados.

El primer defecto es la oscuridad. En srgb el punto del 25% tiene claridad 46% y el degradado entero se hunde en la mitad izquierda. La causa es que sRGB codifica con gamma: sus números no son proporcionales a la luz, así que promediarlos no promedia la luz. srgb-linear sube ese mismo punto a 67% porque promedia intensidades de verdad.

El segundo defecto es el gris. En srgb y en srgb-linear el punto medio tiene croma exactamente cero: es un gris puro. Y esto no lo arregla ninguno de los dos, porque no es un problema de gamma. Azul y amarillo son tonos casi opuestos, con lo que el segmento recto que los une en cualquier espacio cartesiano pasa por el eje neutro. Con oklab la cosa mejora —el croma en el centro es 0.077 en vez de cero— pero sigue siendo una interpolación rectilínea y el centro sigue siendo apagado.

Solo el espacio polar lo evita. oklch no interpola las coordenadas cartesianas: interpola claridad, croma y ángulo, así que en vez de cortar por el medio recorre el arco. El punto medio tiene croma 0.136 y es un turquesa vivo.

Qué arregla cada opción

De ahí sale una regla de decisión en dos pasos.

Si los dos colores son de tonos parecidos, cualquier espacio perceptual vale y oklab es la elección sensata: no hay ángulo que recorrer, la recta ya va por donde quieres, y evitas los efectos secundarios de la interpolación polar.

.superficie {
  background: linear-gradient(in oklab to bottom, #3364db, #112961);
}

Si los tonos están lejos y quieres mantener la intensidad, oklch es la respuesta. El degradado recorrerá los tonos intermedios en vez de atravesar el gris.

.arcoiris-corto {
  background: linear-gradient(in oklch to right, #0000ff, #ffff00);
}

Si de verdad quieres que pase por el gris, cosa que a veces es exactamente lo que buscas —una transición entre dos zonas de la página, un fundido que no llame la atención—, oklab es la elección correcta y no un defecto.

Y srgb sigue teniendo un uso legítimo: reproducir un degradado que ya existía. Si estás portando un diseño y necesitas el mismo resultado, el espacio original era sRGB.

Los cuatro métodos de tono

Al interpolar en un espacio polar hay que decidir por qué lado gira el ángulo. La especificación define cuatro métodos, y se escriben detrás del nombre del espacio:

Método Qué hace
shorter hue El arco más corto entre los dos ángulos. Es el valor por defecto
longer hue El arco más largo, es decir el complementario del anterior
increasing hue Siempre en sentido creciente, dando la vuelta si hace falta
decreasing hue Siempre en sentido decreciente
.corto  { background: linear-gradient(in oklch shorter hue to right,  #f38ba8, #89b4fa); }
.largo  { background: linear-gradient(in oklch longer hue to right,   #f38ba8, #89b4fa); }

longer hue es el que produce el arcoíris completo entre dos colores cualesquiera, y es la forma más corta de escribir una rueda de color:

/* Todo el espectro, con claridad y croma constantes. */
.rueda {
  background: conic-gradient(in oklch longer hue,
    oklch(70% 0.15 0), oklch(70% 0.15 359.9));
}

increasing hue y decreasing hue sirven cuando la dirección importa aunque el arco sea largo: una escala de temperatura que tenga que ir siempre de azul a rojo por el mismo lado, independientemente de dónde caigan los extremos, no puede depender de cuál sea el arco más corto.

⚠️
El método de tono solo existe en espacios polares

in oklab longer hue no es válido y descarta la declaración entera. Los cuatro métodos solo se admiten detrás de hsl, hwb, lch y oklch. Es un error fácil de cometer al cambiar de espacio y difícil de ver, porque el degradado simplemente desaparece.

El precio de interpolar en polar

Interpolar por el arco tiene un efecto secundario que conviene conocer antes de que te sorprenda: el arco puede salirse del gamut aunque los dos extremos estén dentro.

En el degradado de azul a amarillo interpolado en oklch, el croma en el punto medio pedido es 0.262 —la media de 0.313 y 0.211—, mientras que el máximo disponible en sRGB para esa claridad y ese tono es 0.124. De veintiún puntos muestreados a lo largo del recorrido, dieciséis están fuera del gamut de sRGB. Todos ellos se mapean, y el resultado que ves en el 50% no tiene croma 0.262 sino 0.136.

No es un fallo: es geometría. El gamut de sRGB en coordenadas OKLCH es un sólido con la cintura estrecha en la zona de los cianes, y un arco que mantiene el croma alto mientras cruza esa zona se sale por fuerza. Lo que ocurre después con esos colores es el mapeo de gamut, aplicado punto a punto. Los dos extremos están en las esquinas del cubo, que es donde más croma hay; el camino entre esquinas por el exterior no cabe.

Tres consecuencias prácticas:

El degradado sigue siendo mejor que el rectilíneo, porque incluso mapeado conserva croma 0.136 frente a 0. Salirse y ser recortado no es peor que ir por el centro.

El resultado depende del algoritmo de mapeo del navegador, con lo que un degradado polar entre colores muy saturados puede verse distinto en motores distintos. Si te importa la reproducibilidad exacta, baja el croma de los extremos hasta que el arco quepa.

Puede haber puntos intermedios más vivos que los extremos. Si los dos extremos están fuera del gamut y se comprimen mucho, mientras que el centro del arco cae en una zona ancha, verás un degradado que gana intensidad hacia el medio. Es desconcertante y es correcto.

La interpolación es sobre coordenadas, no sobre colores, y el degradado se resuelve por píxel

Merece la pena tener claro qué hace exactamente el motor, porque explica todo lo anterior de una vez. Un degradado no es una lista de colores que se mezclan: es una función de una variable. A cada píxel se le calcula una posición t entre 0 y 1 según la geometría del tipo de degradado, se localiza en qué tramo de la lista de paradas cae ese t, se normaliza dentro del tramo, y con ese número se interpolan las coordenadas de los dos colores del tramo en el espacio elegido. El resultado son tres números en ese espacio, que después se convierten al espacio de salida y, si hace falta, se mapean al gamut. Tres consecuencias caen de ahí y ninguna es obvia. Primera: el espacio de interpolación no afecta a los colores que escribiste, solo a lo que hay entre ellos; un degradado de dos colores idénticos se ve igual en cualquier espacio. Segunda: el mapeo de gamut ocurre después de interpolar y por píxel, no antes sobre los extremos, que es justo lo que produce el efecto de que el centro pueda ser más vivo que los bordes; hacerlo al revés sería más barato y daría un resultado distinto y peor. Y tercera, la que más se pasa por alto: como todo esto se hace por píxel sobre un valor continuo y el resultado se escribe en un búfer de ocho bits por canal, el degradado se cuantiza al final del proceso, no durante. Eso significa que añadir paradas intermedias no reduce las bandas —el problema no está en la interpolación, que es continua, sino en la escritura— y que el número de colores distintos que vas a ver depende del rango que recorra el degradado, no de cuántas paradas escribas. Es el error más común al intentar arreglar un degradado con bandas.

⚔️ Compara los cuatro espacios
  1. Pinta el mismo degradado de #0000ff a #ffff00 en srgb, srgb-linear, oklab y oklch en cuatro franjas apiladas.
  2. Cuenta cuántos puntos del recorrido en oklch se ven más apagados de lo que dicen los números.
  3. Haz una rueda de color completa con conic-gradient y longer hue.
  4. Prueba in oklab longer hue y comprueba que la declaración se descarta.
  5. Coge un degradado de tu proyecto y decide con la regla de dos pasos qué espacio le toca.