CSS y web

Degradados WebGL con shaders, explicados para diseñadores

Algunos degradados de la web se desplazan, brillan y centellean como ninguna imagen ni regla CSS puede. Son shaders. Así funcionan, en palabras que un diseñador puede usar.

GradientlyCuenta verificada de Gradiently··7 min de lectura
Portada: Thistle Mosaic · GR·2BAQ·CR

La versión corta

  • Un degradado WebGL lo dibuja un shader: un pequeño programa que se ejecuta en el chip gráfico y decide el color de cada píxel, a menudo muchas veces por segundo.
  • Como un shader calcula el color con matemáticas en lugar de leer una imagen guardada, puede añadir movimiento, ruido, grano y luz a cualquier tamaño sin que el archivo crezca.
  • Los degradados con shader cuestan más de ejecutar que los degradados CSS, así que uno bien hecho se pausa cuando está fuera de pantalla, respeta el movimiento reducido y tiene una alternativa sencilla en CSS.
  • Usa CSS para mezclas estáticas y movimientos sencillos, y recurre a un shader cuando la textura, la profundidad o el movimiento son la razón de ser del diseño.
En esta página

Un degradado WebGL es un degradado que tu navegador pinta con un shader: un programa corto que se ejecuta en el chip gráfico (la GPU) y responde a una pregunta para cada píxel de la pantalla, «¿de qué color eres ahora mismo?». Un degradado CSS es una receta fija de colores y posiciones. Un shader es una fórmula, así que puede doblar, granular, iluminar y mover el color, y volver a hacerlo sesenta veces por segundo, sin descargar ninguna imagen.

No necesitas escribir shaders para diseñar con ellos, pero saber lo que pueden y no pueden hacer te ayuda a hacer un briefing para un desarrollador, a juzgar una herramienta y a decidir cuándo es mejor opción un simple degradado CSS.

Qué es un shader, en palabras sencillas

WebGL es la forma que tiene el navegador de hablar con la GPU, y cada dibujo WebGL usa dos tipos de shader. El vertex shader coloca las formas; en un degradado a pantalla completa, eso es solo un rectángulo que cubre el lienzo. El fragment shader hace el trabajo interesante: se ejecuta una vez por píxel, recibe la posición de ese píxel y el momento actual, y devuelve un color.

Como la GPU ejecuta miles de estos pequeños programas en paralelo, una fórmula que sería lenta en código normal se vuelve lo bastante barata para redibujarse en cada fotograma. Aquí tienes un fragment shader completo para un degradado de tres colores cuyo límite se mece suavemente con el tiempo, con una línea de grano encima.

text
#version 300 es
precision highp float;

uniform vec2 u_resolution;   // canvas size in pixels
uniform float u_time;        // seconds since start
out vec4 outColor;

void main() {
  vec2 uv = gl_FragCoord.xy / u_resolution;   // 0 to 1 across the canvas

  vec3 night  = vec3(0.07, 0.04, 0.18);  // #120a2e
  vec3 violet = vec3(0.49, 0.23, 0.93);  // #7c3aed
  vec3 coral  = vec3(0.98, 0.45, 0.45);  // #fa7373

  // A slow wave bends the blend so the colours drift.
  float wave = sin(u_time * 0.3 + uv.x * 3.0) * 0.08;
  float t = clamp(uv.y + wave, 0.0, 1.0);

  vec3 color = mix(night, violet, smoothstep(0.0, 0.6, t));
  color = mix(color, coral, smoothstep(0.55, 1.0, t));

  // Fine grain: a different random value per pixel, per frame.
  float n = fract(sin(dot(gl_FragCoord.xy + u_time, vec2(12.9898, 78.233))) * 43758.5453);
  color += (n - 0.5) * 0.04;

  outColor = vec4(color, 1.0);
}
Un fragment shader de WebGL2 en GLSL. smoothstep suaviza cada transición, la onda sinusoidal mueve el límite y las últimas líneas añaden un grano que además oculta las bandas.
Los tres colores del shader. GLSL escribe el color de 0 a 1 por canal, así que #7c3aed se convierte en 124, 58 y 237 divididos entre 255.

En qué se diferencia un degradado con shader de un degradado CSS

Un degradado CSS es declarativo: enumeras colores y posiciones, y el navegador los pinta. Es la herramienta adecuada casi siempre. Un shader es procedimental: describes cómo debe comportarse el color y las matemáticas lo generan. Esa diferencia se nota en lo que puede hacer cada uno y en lo que cuesta.

AspectoDegradado CSSDegradado con shader
Se define conColores, paradas y una direcciónUna fórmula que se ejecuta por píxel
MovimientoMover la posición, el tamaño o propiedades personalizadas animadasTodo lo que puedas expresar con matemáticas, en cada fotograma
Textura y granoNecesita una capa extra, como ruido SVGIntegrado, y puede moverse
Coste de ejecuciónMuy bajo una vez pintadoMantiene ocupada la GPU mientras se anima
Funciona sin JavaScriptSíNo: un script prepara el lienzo
Funciona en emailEn parte, según el clienteNo
En general, ninguno es mejor. Cada uno gana en un trabajo distinto.
La alternativa en CSSlinear-gradient(180deg, #120a2e 0%, #4c1d95 40%, #7c3aed 62%, #fa7373 100%)
La misma paleta como degradado CSS estático. Todo fondo con shader debería incluir una alternativa como esta, que se muestra antes de que arranque el shader y siempre que no pueda ejecutarse.

Por qué los degradados con shader parecen vivos

Tres técnicas hacen la mayor parte del trabajo, y oirás a los desarrolladores nombrarlas. El ruido es aleatoriedad suave: en lugar de una mezcla recta, el color sigue un paisaje de colinas suaves, y por eso los degradados con shader parecen orgánicos y no geométricos. El domain warping realimenta el ruido consigo mismo, de modo que el color se pliega y se arremolina como tinta en el agua. El tiempo desliza todo el paisaje, lo que convierte un campo quieto en una deriva.

  • El grano se puede generar por píxel y por fotograma, así que centellea como la película en lugar de quedarse quieto como una textura puesta encima. Mira degradados con grano.
  • La luz se puede simular con unas pocas líneas: un brillo que sigue al ruido se lee como el lustre de la seda o del cristal.
  • La resolución sale gratis. La fórmula se ejecuta al tamaño que tenga la pantalla, así que un degradado con shader se ve nítido en un móvil y en un monitor 4K con el mismo código.
  • Las bandas son más fáciles de vencer, porque el shader puede añadir un poco de tramado antes de redondear el color a 8 bits. Bandas en degradados explica por qué importa.

Usa CSS cuando

  • El degradado está quieto o se mueve de una forma sencilla
  • Va detrás de mucho texto en una página larga
  • Tiene que funcionar sin scripts o en un email
  • Lo necesitas en una sola línea de código

Usa un shader cuando

  • El movimiento o la textura son la razón de ser del diseño
  • Es una única zona de cabecera, no cada sección
  • Quieres grano, lustre o flujo que CSS no puede imitar
  • Puedes ofrecer una alternativa en CSS y una pausa

El coste de un fondo WebGL en movimiento

Un shader animado mantiene la GPU trabajando mientras está en pantalla. En un ordenador de escritorio apenas se nota. En un móvil antiguo puede significar manos calientes, una batería que se agota y una página que se desplaza con menos fluidez. El coste crece con el número de píxeles y la complejidad de la fórmula, así que un shader a pantalla completa en una pantalla de alta densidad es el caso más pesado. ¿Los degradados ralentizan una web? explica cómo medirlo.

  1. 1

    Muestra primero la alternativa en CSS

    Pinta el degradado estático de inmediato y luego haz aparecer el lienzo con un fundido cuando el shader haya dibujado su primer fotograma. Nadie debería ver una caja vacía.

  2. 2

    Comprueba que WebGL está disponible

    Algunos navegadores, dispositivos y ajustes de privacidad lo desactivan. Si no se puede crear el contexto, mantén la alternativa.

  3. 3

    Respeta el movimiento reducido

    Si el visitante pide menos movimiento, dibuja un fotograma y detente. Movimiento reducido explica ese ajuste.

  4. 4

    Pausa cuando no se vea

    Detén el bucle de animación cuando el lienzo salga de la vista o la pestaña esté oculta.

  5. 5

    Renderiza a una resolución razonable

    Los degradados suaves rara vez necesitan todos los píxeles del dispositivo. Dibujar a media resolución y dejar que el navegador escale puede reducir mucho el trabajo para un desenfoque que no se distingue.

js
const canvas = document.querySelector('#hero-canvas')
const gl = canvas.getContext('webgl2')
const still = matchMedia('(prefers-reduced-motion: reduce)').matches
let visible = true

if (!gl) {
  canvas.remove() // the CSS gradient behind it stays
} else {
  new IntersectionObserver(([entry]) => {
    visible = entry.isIntersecting
  }).observe(canvas)

  const frame = (ms) => {
    if (visible && !document.hidden) draw(gl, ms / 1000) // your draw call
    if (!still) requestAnimationFrame(frame)
  }
  requestAnimationFrame(frame)
}
Las protecciones en unas pocas líneas: una alternativa cuando falta WebGL, un solo fotograma con movimiento reducido y nada de dibujar fuera de pantalla o en una pestaña oculta.

Como referencia, la API de WebGL en MDN documenta todas las llamadas usadas aquí, y caniuse sigue el soporte de WebGL2 en los navegadores.

Diseñar un degradado con shader: qué incluir en el briefing

Puedes dirigir un shader sin leer GLSL. Haz el briefing como lo harías para un material, no para una muestra de color. Da el color de fondo y los tres a cinco colores que viven sobre él, en hex. Describe la estructura: charcos suaves, bandas largas, seda plegada. Indica la velocidad en segundos por ciclo en lugar de decir «lento», porque lento significa cosas distintas para cada persona. Di cuánto grano quieres y si se mueve. Por último, di dónde irá el texto, para que la parte más recargada del campo quede lejos de él.

Cómo dibuja Gradiently los Marks con shaders

Los Marks de Gradiently son degradados con shader. Cada Mark es una receta de colores, hasta tres materiales superpuestos, luz, grano y movimiento, y el estudio lo dibuja en vivo con WebGL2 detrás de todo lo que diseñas. Como se calcula en lugar de guardarse, el mismo Mark se ve nítido en cualquiera de los tamaños de cada plataforma y se mueve cuando exportas un vídeo en bucle. El renderizador también sabe dónde están tus palabras, así que puede desplazar la parte más tranquila del campo detrás de ellas. Qué es un Mark explica la receta completa.

Thistle Mosaic

GR·2BAQ·CR

Thistle Mosaic

Ver este Mark
Un Mark es una receta de shader con un nombre, un código y un único propietario. Su página lo muestra renderizándose en vivo.

Preguntas frecuentes

¿Qué es un degradado WebGL?

Es un degradado dibujado por un shader, un pequeño programa que se ejecuta en el chip gráfico y calcula el color de cada píxel. Como es una fórmula, puede moverse, añadir grano y renderizarse a cualquier tamaño.

¿Un degradado con shader es malo para el rendimiento?

Un shader quieto o en pausa cuesta poco, pero uno animado mantiene ocupada la GPU mientras está en pantalla. Páusalo cuando no se vea, dibuja a una resolución razonable y mantén una alternativa en CSS.

¿Puedo hacer un degradado con shader sin programar?

Sí. Varias herramientas de diseño generan fondos con shader a partir de controles, y la Forge de Gradiently crea Marks, que son degradados con shader, a partir de una frase o control a control.

¿Los degradados con shader respetan los ajustes de movimiento reducido?

Solo si están hechos para ello. Comprueba la media query prefers-reduced-motion en JavaScript y dibuja un único fotograma quieto cuando esté activada.

¿Qué es GLSL?

GLSL es el lenguaje de sombreado que usa WebGL. Los fragment shaders escritos en él deciden el color de cada píxel, y el navegador los compila para el chip gráfico.

Compartir esta guía

Escrito por GradientlyCuenta verificada de Gradiently

El equipo detrás de Gradiently, una herramienta de diseño creada en torno a los Marks: degradados vivos que hacen que todo lo que diseñas parezca tuyo.

Ver nuestro perfil