Rendimiento de Canvas y WebGL para Experiencias Interactivas Fluidas

Los sistemas de partículas interactivos, las simulaciones físicas y los playgrounds necesitan mantenerse a 60 fotogramas por segundo estables tanto en escritorio como en móvil. Esta guía original cubre técnicas prácticas usadas en UtiliHub: buenas prácticas de requestAnimationFrame, object pooling, manejo de devicePixelRatio, soporte de reduced-motion, presupuestos móviles y medición con la Performance API.

Por qué importan los 60fps

A 60fps cada fotograma dispone de unos 16,7 milisegundos. Cualquier trabajo que supere regularmente ese presupuesto produce fotogramas perdidos, jank y una sensación menos responsive. Canvas 2D y WebGL son potentes, pero siguen ejecutándose en el hilo principal junto a React, manejadores de entrada y layout. El objetivo es mantener el bucle de animación corto y predecible.

Buenas prácticas de requestAnimationFrame

Impulsa siempre la simulación con requestAnimationFrame. Se alinea con la tasa de refresco de la pantalla y se pausa automáticamente cuando la pestaña está en segundo plano.

  • Guarda el id del callback para poder cancelarlo al desmontar o cuando el componente esté oculto.
  • Calcula el delta-time (dt) desde el timestamp anterior, pero limítalo (por ejemplo máximo 50 ms) para que un fotograma largo no produzca un salto enorme.
  • Prefiere un timestep de simulación fijo para estabilidad física y, si hace falta, renderiza con interpolación.
  • Evita crear objetos, arrays o strings nuevos dentro del bucle.

Object pooling

Crear y descartar miles de objetos de partículas cada segundo provoca pausas de recolección de basura. El object pooling reutiliza las mismas instancias.

Mantén una lista libre de partículas inactivas. Cuando necesites una nueva, sácala de la lista libre (o crece el pool si está vacía). Cuando una partícula muere, reinicia sus propiedades y devuélvela a la lista libre. El mismo patrón sirve para vectores temporales, segmentos de estela o buffers WebGL.

devicePixelRatio y pantallas de alta densidad

Las pantallas de alta densidad multiplican el número de píxeles que hay que rellenar. Configurar el backing store del canvas a devicePixelRatio da líneas nítidas, pero puede cuadruplicar el fill rate en una pantalla 2×.

Enfoque práctico: usa un ratio limitado (por ejemplo Math.min(window.devicePixelRatio, 2)) y solo súbelo cuando la experiencia sea ligera. En escenas con muchas partículas, un ratio de 1 en móvil suele ser el mejor compromiso para fluidez.

Reduced motion y accesibilidad

Respeta la media query prefers-reduced-motion. Cuando coincida, reduce el número de partículas, desactiva estelas, ralentiza o pausa el movimiento continuo y evita gradientes animados grandes. Mejora la comodidad de usuarios con sensibilidad al movimiento y también reduce la carga de CPU/GPU.

Presupuestos móviles

Las GPU móviles y los límites térmicos son más estrictos. Reglas prácticas útiles:

  • Ofrece un control de número de partículas o calidad que por defecto sea más bajo en pantallas pequeñas o punteros gruesos.
  • Omite efectos costosos (bloom, blur grande, estelas de alta resolución) por debajo de un umbral de ancho.
  • Pausa el bucle cuando el canvas esté fuera de pantalla (IntersectionObserver).
  • Mantén el tamaño CSS del canvas fijo con una caja de aspect-ratio para que el layout no se desplace.

Mira las técnicas en acción

Partículas de Gravedad y Laboratorio de Imanes de Partículas usan estos patrones para mantenerse fluidos mientras interactúas.

Medición con la Performance API

Usa la Performance API del navegador para ver dónde se gasta realmente el tiempo.

  • performance.now() alrededor de las fases de actualización y dibujo.
  • performance.mark / performance.measure para secciones con nombre que aparecen en el panel Performance de DevTools.
  • Vigila fotogramas largos (más de 50 ms) y eventos de GC. El object pooling y evitar asignaciones suelen eliminar los picos.
  • En navegadores compatibles, performance.memory (Chrome) puede indicar crecimiento del heap si el pool tiene fugas.

Graba una sesión corta mientras interactúas y mira el flame chart. El callback de animación debería ser una rebanada pequeña y regular del fotograma.

Trabajo fuera del hilo principal

El cálculo pesado que no necesita tocar el DOM o el canvas puede moverse a un Web Worker. Candidatos típicos: pathfinding, actualizaciones grandes de partículas que solo necesitan posiciones, o solvers de física. El hilo principal solo recibe el último estado y lo dibuja. El paso de mensajes tiene coste, así que agrupa actualizaciones en lugar de enviar cada partícula cada fotograma.

Experiencias relacionadas en UtiliHub

Conclusión

Las experiencias interactivas fluidas a 60fps provienen de bucles disciplinados, reutilización de objetos, elecciones sensatas de resolución y respeto a las restricciones móviles y de reduced-motion. Mide primero y luego optimiza el camino caliente. Los playgrounds enlazados arriba demuestran estas ideas en la práctica.