wandres.dev
GSAP I · Por qué existe y por qué es gratis

Qué resuelve GSAP que las APIs nativas no

Los cuatro problemas concretos que una librería de animación resuelve y las APIs del navegador no: secuenciación compleja, interpolación de lo que no es CSS, control unificado y comportamiento consistente entre motores.

⏱ 18 min

La pregunta legítima ante cualquier librería de animación en 2026 es por qué existe. CSS tiene transiciones, keyframes, linear(), animaciones dirigidas por scroll y View Transitions; la Web Animations API da control programático completo. La respuesta no es “porque es más fácil” —eso sería una preferencia— sino que hay cuatro problemas concretos que las APIs nativas no resuelven, no por inmadurez sino porque están fuera de su alcance por diseño. Conocerlos con precisión es lo que te permite decidir cuándo GSAP sobra, que también pasa a menudo.

🎯 Al terminar esta lección sabrás
  • Enumerar los cuatro problemas que las APIs nativas no cubren y reconocerlos en un encargo real.
  • Escribir el equivalente nativo de una secuencia con dependencias y medir cuánto código cuesta.
  • Identificar qué animaciones no necesitan librería en absoluto.
  • Explicar por qué la interpolación de valores que no son CSS es la capacidad menos conocida y la más diferencial.

Problema uno: la secuenciación con dependencias

Una secuencia de tres pasos encadenados es fácil en cualquier API. El problema aparece cuando la secuencia tiene doce pasos, solapes deliberados entre ellos y alguien pide alargar el segundo medio segundo.

Con CSS puro, el mecanismo de secuenciación son los animation-delay. Cada paso lleva un retardo calculado a mano respecto al principio. Cambiar la duración del paso dos obliga a recalcular los diez retardos siguientes, y si el paso siete tenía que empezar cuando el seis llevaba el 70% en vez de al terminar, ese cálculo lo tienes que hacer tú y rehacer cada vez.

Con la Web Animations API la situación mejora porque tienes promesas y finished, pero encadenar con await te da secuencia estricta sin solape, y solapar significa volver a los retardos calculados a mano. Además, encadenar con promesas hace la secuencia no rebobinable: no hay forma de reproducir hacia atrás una cadena de await, ni de saltar al 60%, ni de escalar su velocidad.

// Secuenciacion nativa: los retardos son aritmetica que mantienes tu.
const t = { titulo: 0.6, sub: 0.4, cta: 0.5 };
tituloEl.animate(kfTitulo, { duration: t.titulo * 1000, fill: 'both' });
subEl.animate(kfSub,   { duration: t.sub * 1000, delay: (t.titulo - 0.2) * 1000, fill: 'both' });
ctaEl.animate(kfCta,   { duration: t.cta * 1000, delay: (t.titulo - 0.2 + t.sub - 0.15) * 1000, fill: 'both' });

Ese t.titulo - 0.2 + t.sub - 0.15 es el síntoma. Es aritmética que describe una intención —“que arranque 150 ms antes de que acabe el anterior”— pero no la expresa, así que se pierde en cuanto alguien lee el código. Con doce pasos, esa expresión ocupa una línea entera y nadie se atreve a tocarla. El parámetro de posición de las timelines de GSAP existe justamente para expresar esa intención en vez de su resultado numérico.

Problema dos: interpolar lo que no es una propiedad CSS

Esta es la capacidad menos conocida y la más diferencial. Las APIs nativas animan propiedades de elementos del DOM. Punto. element.animate() toma keyframes de propiedades CSS; transition y @keyframes, lo mismo.

GSAP anima cualquier propiedad numérica de cualquier objeto de JavaScript. Eso incluye la posición de una cámara de Three.js, el volumen de un AudioContext, un valor en un store de estado, la propiedad de un objeto que luego dibujas en un canvas, o un contador que quieres que suba con easing.

// Un objeto plano. No hay DOM por ningun lado.
const estado = { progreso: 0, hue: 200 };

gsap.to(estado, {
  progreso: 100,
  hue: 340,
  duration: 2,
  ease: 'power2.inOut',
  onUpdate() {
    ctx.fillStyle = `hsl(${estado.hue} 70% 55%)`;
    dibujarBarra(ctx, estado.progreso);
  },
});

El equivalente nativo es escribir tu propio bucle de requestAnimationFrame, tu propia función de easing, tu propio cálculo de delta y tu propia gestión de cancelación. Son cuarenta líneas por cada valor que quieras animar, y ninguna de esas cuarenta líneas participa del control unificado que viene después.

En cuanto tu animación toca WebGL, canvas, audio o cualquier cosa que no sea el árbol de elementos, las APIs nativas dejan de ser una opción y la comparación deja de tener sentido: no es librería contra plataforma, es librería contra escribirlo tú.

ℹ️
Interpolar strings complejos también cuenta

Además de números, GSAP interpola cadenas con números dentro: "0px 4px 12px rgba(0,0,0,0.2)" a "0px 20px 40px rgba(0,0,0,0.5)" funciona porque el motor localiza los números dentro de la cadena y los interpola uno a uno manteniendo el resto. Es cómo animan las sombras, los clip-path con el mismo número de vértices y los degradados. Es el mismo mecanismo que usa el navegador para interpolar valores computados, pero disponible sobre objetos arbitrarios.

Problemas tres y cuatro: control y consistencia

El control unificado

Cuando una animación involucra ocho elementos, tres bucles de canvas y un contador, las APIs nativas te dejan con ocho objetos Animation, tres identificadores de requestAnimationFrame y un setTimeout. Pausar todo eso significa recorrer tres colecciones distintas con tres mecanismos distintos, y rebobinarlo, sencillamente, no se puede: cancelAnimationFrame no rebobina.

GSAP mete todo en la misma estructura. Una timeline es un objeto con play, pause, reverse, seek, timeScale y progress, y esos métodos funcionan igual tanto si dentro hay transformaciones CSS como si hay propiedades de un objeto arbitrario. Puedes anidar timelines dentro de timelines y controlar la de arriba.

Esa uniformidad tiene un efecto en la forma de trabajar que se nota más que en la de escribir código: una animación que puedes rebobinar, pausar a mitad y escalar en velocidad es una animación que puedes depurar. Poner tl.timeScale(0.1) y ver a cámara lenta qué pasa en el frame 340 es la diferencia entre ajustar una coreografía y adivinarla.

El mismo resultado en los cuatro motores

Este argumento era decisivo en 2015 y hoy es más matizado, pero no ha desaparecido. Los motores han convergido mucho en lo básico y muy poco en los bordes.

Las transformaciones sobre SVG son el ejemplo canónico. transform-origin en un <g>, las unidades del transform en SVG frente a CSS, el comportamiento de transform-box: cada motor tiene su historia y sus diferencias residuales. GSAP normaliza eso aplicando las transformaciones al atributo transform con matrices calculadas por él, lo cual da el mismo resultado en todas partes a costa de no usar el camino nativo.

El otro caso es el orden de las transformaciones. En CSS, transform: rotate(45deg) translateX(100px) y transform: translateX(100px) rotate(45deg) dan resultados distintos, y eso es correcto pero es una fuente inagotable de errores cuando varias partes del código escriben en la misma propiedad. GSAP aplica siempre el mismo orden —traslación, escala, rotaciones, sesgado— y expone cada componente como una propiedad independiente con su propia caché, de modo que dos animaciones simultáneas sobre x y sobre rotation no se pisan. Con transform nativo, dos animaciones que quieran tocar componentes distintos de la misma cadena se pisan siempre, y la única solución es que una sola escriba la cadena completa.

Cuándo GSAP sobra

Esto importa tanto como lo anterior. Cargar 70 KB de librería para lo que hacen tres líneas de CSS es un error de criterio, no una preferencia.

Caso Herramienta correcta
Cambio de estado en hover, foco o clase transition de CSS
Bucle decorativo que no se controla @keyframes
Revelado al entrar en el viewport, sin coreografía Scroll-driven animations o IntersectionObserver
Transición entre dos vistas del documento View Transitions
Secuencia con solapes, etiquetas y control GSAP
Animar objetos que no son elementos del DOM GSAP o bucle propio
Gesto continuo con inercia GSAP o simulación propia

La regla que uso: si la animación se puede describir como “esta propiedad va de aquí a allá cuando pasa esto”, es CSS. Si hay que decir “y entonces, mientras aquello va por la mitad, esto otro empieza, y todo el conjunto se puede rebobinar”, es una librería.

El coste real de GSAP no son los kilobytes

El núcleo comprimido ronda los 25 KB, que en 2026 es menos que un tipo de letra o una foto de cabecera; discutir eso es un debate de 2016. El coste real es que todo lo que animes con GSAP sale del modelo declarativo del navegador. Una animación de GSAP no aparece en document.getAnimations(), no la ve prefers-reduced-motion automáticamente, no se pausa sola cuando la pestaña pasa a segundo plano de la misma forma, y no la puede optimizar el compositor salvo que anime propiedades que lo permitan. Todo eso tiene solución —GSAP tiene su propio ticker que sí se pausa, y respetar la preferencia de movimiento reducido es una condición que escribes tú— pero son responsabilidades que asumes al usar la librería y que la plataforma te daba gratis. La decisión correcta casi nunca es “todo GSAP” ni “nada de GSAP”: es CSS para los estados y GSAP para las coreografías, conviviendo en el mismo proyecto sin complejos.

⚔️ Reto práctico

Coge una coreografía real de tu proyecto con al menos cinco pasos y solapes, y escríbela dos veces: una con element.animate() y retardos calculados, y otra con una timeline. Después cambia la duración del segundo paso en las dos versiones y cronometra cuánto tardas en cada una. La diferencia de tiempo, y no la de líneas de código, es el argumento.