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.
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.
- 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.
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.
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.
- Pinta el mismo degradado de
#0000ffa#ffff00ensrgb,srgb-linear,oklabyoklchen cuatro franjas apiladas. - Cuenta cuántos puntos del recorrido en
oklchse ven más apagados de lo que dicen los números. - Haz una rueda de color completa con
conic-gradientylonger hue. - Prueba
in oklab longer huey comprueba que la declaración se descarta. - Coge un degradado de tu proyecto y decide con la regla de dos pasos qué espacio le toca.