GlitchCore: Cuando C++ Conoce al Navegador
GlitchCore: Cuando C++ Conoce al Navegador
Todo comenzó con una frustración que probablemente muchos desarrolladores frontend conocen: una página web con animaciones que simplemente no corría a los FPS que necesitaba. Después de días optimizando Three.js y aprendiendo shaders en GLSL, una pregunta empezó a rondar mi cabeza: ¿Y si pudiera traer la velocidad bruta de C++ directamente al navegador?
Esa pregunta se convirtió en GlitchCore: un motor de procesamiento de imágenes escrito en C++ que corre a 60 FPS estables en tu navegador, sin plugins, sin extensiones, solo WebAssembly puro.
El Problema Real: No Era el Código, Era la Memoria
Cuando empecé a investigar WebAssembly, encontré que la tecnología en sí era bastante directa. Compilas C++ con Emscripten, generas un archivo .wasm, lo cargas en JavaScript y listo. Simple, ¿verdad?
Pues no tanto.
El verdadero cuello de botella apareció cuando intenté procesar imágenes en tiempo real. Estamos hablando de millones de píxeles que necesitan ser manipulados 60 veces por segundo. El flujo tradicional sería:
Cada uno de esos pasos de "copia" es un asesino de rendimiento. Para una imagen de 1920x1080, estamos hablando de copiar 8 millones de bytes... 60 veces por segundo. Los números simplemente no cuadraban.
La Solución: Arquitectura Zero-Copy
La solución fue eliminar las copias por completo. En lugar de mover datos entre JavaScript y C++, ambos trabajan sobre la misma región de memoria.
¿Cómo funciona? WebAssembly tiene acceso a un bloque de memoria lineal (el famoso HEAPU8 de Emscripten). Cuando C++ procesa una imagen, escribe directamente en esta memoria. Del lado de JavaScript, en lugar de copiar esos datos, simplemente creamos una vista que apunta a la misma dirección de memoria.
El resultado: cero copias, cero overhead. Solo píxeles fluyendo del heap de C++ directo a tu pantalla.
Los Shaders: Por Qué C++ y No WebGL
Una pregunta válida sería: ¿Por qué no usar WebGL para esto? Después de todo, la GPU está diseñada exactamente para este tipo de procesamiento paralelo.
La respuesta está en el tipo de efectos que quería implementar. Algunos algoritmos son inherentemente secuenciales o requieren acceso a datos de formas que las GPUs no manejan bien:
Pixel Sorting (Melting Effect): Este efecto ordena columnas de píxeles por su luminancia. Ordenar es una operación que se beneficia de algoritmos como QuickSort, que en la GPU serían un dolor de cabeza implementar.
Sobel Edge Detection: Aunque las convoluciones pueden hacerse en GPU, implementarlas en C++ me dio más control sobre optimizaciones específicas y fue más rápido de iterar.
Interactive Lens: Un efecto tipo lupa que aplica distorsiones matemáticas solo dentro de un radio del cursor. La GPU tendría que calcular esto para cada píxel de la imagen; en C++, puedo optimizar para solo procesar los píxeles afectados.
En total, el engine incluye 11 efectos diferentes: Swirl, Jitter, Mosaic, Solarize, RGB Noise, Scanlines, Ripple, y más.
El Stack Técnico
Para los que les interese replicar algo similar, este es el stack que usé:
El patrón Strategy fue clave para mantener el código organizado. Cada efecto es una clase que implementa una interfaz común, lo que hace trivial agregar nuevos shaders sin tocar el código existente.
Lecciones Aprendidas
Después de este experimento, algunas cosas me quedaron claras:
¿Qué Sigue?
Este proyecto fue un experimento de aprendizaje, pero me dejó pensando en las posibilidades. ¿Editores de imagen en el navegador con rendimiento nativo? ¿Filtros de video en tiempo real sin depender de APIs propietarias? ¿Juegos 2D con física compleja corriendo a 60 FPS constantes?
WebAssembly está abriendo puertas que antes estaban cerradas para el desarrollo web. Y apenas estamos empezando a explorar lo que es posible.
¿Te interesa explorar el código? El repositorio está en GitHub y hay una demo en vivo donde puedes probar todos los efectos. Los enlaces están arriba de este artículo.