Cuándo el canvas deja de ser la herramienta adecuada
Diagnosticar con dos experimentos qué limita tu fotograma, reconocer las señales que no son de rendimiento, y elegir la salida sabiendo lo que cuesta cada migración.
Todas las optimizaciones del nivel tienen un techo, y llega un punto en el que insistir es tirar tiempo. Reconocer ese punto es una habilidad distinta de optimizar y bastante más rara: exige separar qué limita el fotograma, aceptar que a veces la respuesta no es hacer el canvas más rápido sino no usar canvas, y estimar honestamente lo que cuesta cada alternativa antes de convencer a nadie de reescribir nada.
- Diagnosticar con dos experimentos si el límite es de relleno, de órdenes o externo.
- Reconocer las señales que obligan a cambiar de tecnología aunque el rendimiento sea bueno.
- Comparar las alternativas con su coste real de migración.
- Diseñar la solución híbrida, que casi siempre es la respuesta correcta.
Dos experimentos que responden la pregunta
Antes de discutir tecnologías hay que saber qué limita el fotograma, y se averigua con dos cambios de una línea cada uno.
flowchart TB lento[El fotograma no cabe en el presupuesto] --> p1[Experimento 1 reducir la resolucion del canvas a la mitad] p1 -->|El tiempo cae a cerca de la cuarta parte| relleno[Limitado por tasa de relleno] p1 -->|Apenas cambia| p2[Experimento 2 dibujar la mitad de los objetos] p2 -->|El tiempo cae casi a la mitad| ordenes[Limitado por numero de ordenes de dibujo] p2 -->|Apenas cambia| fuera[El coste no esta en el dibujo] relleno --> r1[Menos pixeles menos capas menos sombras. Si no basta GPU] ordenes --> o1[Agrupar cachear region sucia. Si no basta WebGL] fuera --> f1[Perfilar actualizacion entrada layout y recoleccion] style lento fill:#f38ba8,color:#11111b style p1 fill:#cba6f7,color:#11111b style p2 fill:#cba6f7,color:#11111b style relleno fill:#fab387,color:#11111b style ordenes fill:#f9e2af,color:#11111b style fuera fill:#89b4fa,color:#11111b style r1 fill:#a6e3a1,color:#11111b style o1 fill:#a6e3a1,color:#11111b style f1 fill:#a6e3a1,color:#11111b
Experimento uno: la mitad de resolución. Fija el búfer a la mitad de ancho y alto sin cambiar el tamaño CSS. Si el tiempo por fotograma se desploma, estás limitado por la cantidad de píxeles que tocas, y ninguna optimización de llamadas te va a ayudar.
// Diagnostico: mitad de pixeles, misma escena
canvas.width = Math.round(ancho * dpr * 0.5);
canvas.height = Math.round(alto * dpr * 0.5);
ctx.setTransform(dpr * 0.5, 0, 0, dpr * 0.5, 0, 0);
Experimento dos: la mitad de los objetos. Si el tiempo baja proporcionalmente, el límite es el número de órdenes de dibujo y el coste por objeto. Si no baja, el trabajo está en otra parte: la actualización del estado, la gestión de la entrada, un recálculo de layout forzado o la recolección de basura.
Con esa clasificación, las decisiones dejan de ser opinión.
Si el límite es la tasa de relleno, las salidas por orden de retorno son: reducir la densidad del canvas —dibujar a 1,5 en lugar de a 3 en móviles es apenas perceptible y ahorra la mitad de los píxeles—, eliminar capas translúcidas superpuestas, quitar sombras y desenfoques, y usar la región sucia para tocar menos área. Cambiar a WebGL no arregla esto por sí solo: la GPU es la misma y el relleno cuesta lo mismo.
Si el límite es el número de órdenes, las salidas son agrupar por estado, unir figuras en una sola ruta, cachear en mapas de bits, y la región sucia. Cuando todo eso está hecho y el número de objetos sigue creciendo, ahí sí aparece el límite estructural del canvas 2D: una orden de dibujo por objeto, ejecutada desde JavaScript.
Los órdenes de magnitud, medidos en un portátil de escritorio reciente y como referencia aproximada, no como promesa: unas decenas de miles de rectángulos de color plano por fotograma a sesenta hercios; unos pocos miles de rutas complejas con trazo; unos cientos de textos. Un móvil de gama media está entre tres y diez veces por debajo. Si tus números están en el mismo orden que esos, el canvas está haciendo su trabajo y el problema es que le pides demasiado.
Las señales que no son de rendimiento
Hay razones para abandonar el canvas que no tienen nada que ver con la velocidad, y suelen aparecer tarde en el proyecto, cuando ya duele.
El contenido tiene que ser accesible de verdad. No un texto alternativo, sino navegable, leíble y anunciable. La lista completa de lo que sí y lo que no se puede conseguir está en la accesibilidad del canvas, y el resumen es que un canvas interactivo complejo no llega.
Hay que imprimir o exportar a PDF. El canvas se imprime como un mapa de bits a la resolución del búfer, que sobre papel a 300 puntos por pulgada se ve pixelado. Un vector se imprime nítido a cualquier tamaño.
El texto tiene que ser texto. Buscable con la búsqueda del navegador, seleccionable, copiable, traducible por el traductor automático, y capaz de reajustarse al tamaño de letra del sistema. Nada de eso existe dentro de un canvas.
El equipo no puede mantenerlo. Una escena en canvas es código de dibujo imperativo que solo entiende quien lo escribió. Un gráfico en SVG lo puede tocar un diseñador con CSS. Esto no aparece en ningún análisis técnico y decide muchos proyectos.
Hace falta indexación o previsualización. Lo dibujado en un canvas no existe para ningún rastreador ni para ninguna herramienta que lea la página.
Las alternativas y lo que cuesta cada una
| Salida | Cuándo es la buena | Coste de migrar |
|---|---|---|
| CSS con transformaciones | Pocas decenas de elementos que se mueven | Bajo; muchas veces es simplificar |
| SVG | Cientos de elementos, interacción y accesibilidad | Medio; hay que rehacer el dibujo como datos |
| Canvas 2D bien optimizado | Miles de elementos, dibujo homogéneo | Ninguno: es donde ya estás |
| WebGL o WebGPU | Decenas de miles de elementos o efectos por píxel | Alto; se reconstruye todo el dibujo |
| Renderizado en el servidor | Visualizaciones enormes y estáticas | Medio; cambia la arquitectura, no el cliente |
La primera fila se pasa por alto constantemente y es la que más veces resuelve el problema. Una transformación de CSS sobre un elemento la ejecuta el compositor, no el hilo principal, así que animar cincuenta cajas con transform es literalmente más barato que dibujarlas en un canvas, y encima son accesibles y estilables. Mucho canvas existe porque alguien asumió que el canvas es lo rápido, sin comprobar que para pocos elementos es justo al revés.
La decisión de pasar a WebGL se toma casi siempre con el modelo mental equivocado. Se imagina como “aprender shaders”, que es la parte pequeña y además divertida. Lo caro es otra cosa: todo lo que el canvas 2D te daba hecho y que en WebGL no existe. Empezando por el texto, que es el golpe. No hay fillText: hay que generar un atlas de glifos, resolver el espaciado entre pares, manejar varias fuentes y varios tamaños, y decidir qué haces con los idiomas que no sabías que ibas a soportar. Un sistema de texto en WebGL que se vea bien es un proyecto de semanas, y es la razón por la que tantas aplicaciones WebGL acaban con un canvas 2D superpuesto solo para las etiquetas. Y detrás del texto viene la lista: los trazos con grosor, que en WebGL hay que teselar a triángulos porque las líneas nativas tienen un grosor máximo de uno en la práctica; los guiones; las uniones y los extremos; los gradientes; los modos de fusión; el recorte por ruta arbitraria; el antialiasing de calidad. Todo eso existe y funciona bien en canvas 2D, y en WebGL es código tuyo. La regla honesta para decidir: migra a WebGL cuando estés limitado por número de órdenes, la escena sea homogénea —muchísimas instancias de la misma forma, que es exactamente lo que el dibujo instanciado resuelve— y ya hayas agotado el agrupamiento y la caché. Un mapa de puntos con doscientos mil marcadores idénticos es el caso perfecto. Un editor de diagramas con cuarenta formas distintas, texto y sombras es exactamente el caso contrario, por muy lento que vaya, y ahí la respuesta es optimizar el canvas. Y una observación que cambia muchas decisiones: un canvas 2D bien escrito supera con facilidad a una implementación ingenua de WebGL, porque los navegadores llevan años acelerando el canvas 2D con las mismas GPU y las mismas técnicas. Si tu canvas va lento y aún no has implementado agrupamiento por estado, caché y región sucia, no tienes un problema de tecnología: tienes trabajo pendiente. La migración que se hace saltándose ese trabajo suele terminar con una versión WebGL que va peor que el canvas original y con un equipo que ya no puede volver atrás.
El enfoque híbrido
La respuesta correcta casi nunca es una tecnología para todo. Es repartir cada parte de la escena a lo que mejor la hace, y ese reparto tiene un patrón que se repite en casi todas las visualizaciones serias del mundo real.
El canvas dibuja lo denso. Los cientos de miles de puntos, las líneas del mapa, la imagen de fondo, la capa de datos.
El DOM pone lo interactivo y lo que lleva texto. Ejes con sus etiquetas, leyenda, tooltip, botones, campos, menús. Son pocos elementos, cambian poco, y siendo elementos reales salen gratis la accesibilidad, el foco, la selección de texto y el estilado con CSS.
El SVG cubre las capas intermedias cuando hacen falta formas vectoriales interactivas encima: una selección, unas guías, un recuadro de zoom.
<div class="grafico" style="position:relative">
<canvas id="datos" style="position:absolute;inset:0;width:100%;height:100%"></canvas>
<svg id="seleccion" style="position:absolute;inset:0;width:100%;height:100%;
pointer-events:none"></svg>
<div id="ejes" style="position:absolute;inset:0;pointer-events:none"></div>
<div id="tooltip" role="status" style="position:absolute;pointer-events:none"></div>
</div>
Las tres claves de que esto funcione: el mismo sistema de coordenadas para todas las capas, con una única función de proyección que las tres consultan; pointer-events: none en las capas superiores que no deben robar los eventos, dejando que lleguen al canvas; y una sola fuente de verdad, el modelo, del que las tres capas se derivan.
El coste de este diseño es tener que sincronizar tres representaciones. La ganancia es que cada problema se resuelve donde es fácil: el tooltip es un div con texto real y no una caja dibujada a mano con fillText y su medida; la leyenda es una lista con casillas de verificación de verdad; y los doscientos mil puntos siguen siendo un canvas, que es exactamente lo que el canvas hace mejor que nadie.