Dónde está Bloom: decisiones de diseño, progreso y lo que viene
Bloom cumplió cinco semanas esta semana. Con trescientos once commits encima, queríamos dejar por escrito qué tenemos, por qué lo construimos así y qué piezas aún están en bruto.
El pitch, en una sola frase
Escribe tu juego en TypeScript, compílalo de forma anticipada y publica un binario nativo real en macOS, Windows, Linux, iOS y tvOS — o un bundle WASM para la web. Sin Electron, sin WebView, sin un runtime de JavaScript embebido en el juego que entregas.
Esa frase ha hecho mucho trabajo. También es la razón por la que la mayoría de nuestras decisiones de diseño tempranas se ven como se ven.
Por qué TypeScript
No elegimos TypeScript porque amemos JavaScript. Lo elegimos porque es el lenguaje con tipado estático más usado, con un sistema de tipos estructural, una enorme historia de herramientas y una sintaxis que no asusta a nadie. La mayoría de las personas con las que queremos hacer juegos ya han publicado TypeScript. Muy pocas han publicado C++.
También queríamos un lenguaje que pudiera compilarse de forma anticipada de manera limpia, sin arrastrar un recolector de basura y un intérprete de bytecode dentro de cada binario que entregamos. Eso descartó cualquier cosa que requiera un runtime pesado (Python, C#, JS-el-lenguaje). TypeScript — menos los bits dinámicos por defecto — resultó ser el punto dulce.
Por qué Perry
Perry es el compilador anticipado que convierte tu TypeScript en código nativo. El camino de TS a binario es la parte que nos permite prometer “sin sobrecarga en tiempo de ejecución.” Tu juego se convierte en un único binario que llama a un núcleo en Rust a través de un ABI de C estable. No hay V8, no hay Bun, no hay JIT.
Elegir Perry nos permitió ser implacables con la superficie del lenguaje que exponíamos. La API de Bloom son funciones e interfaces simples — sin clases, sin decoradores, sin proxies, sin eval. Si una característica de TypeScript no compila limpiamente a una llamada nativa, no la usamos en la API. El resultado es una API que cabe en una hoja de referencia y un build que cabe en tu cabeza.
Por qué wgpu y no cuatro renderers a medida
Nuestro primer instinto fue escribir un renderer de Metal, luego uno de DirectX 12, luego uno de Vulkan. Nos disuadimos a nosotros mismos en un fin de semana. Cuatro backends significan cuatro lenguajes de shader, cuatro modelos de recursos, cuatro superficies de bugs y cuatro lugares donde olvidarse de HiDPI.
En cambio, todo el renderer está escrito una sola vez sobre wgpu. Los shaders son WGSL. Obtenemos Metal en plataformas Apple, DirectX 12 en Windows, Vulkan en Linux y Android, y WebGPU (con un respaldo en WebGL) en el navegador — desde una sola base de código. El costo es que estamos atados al conjunto de funcionalidades de wgpu, y algunas cosas exóticas (mesh shaders, ray tracing) están fuera de la mesa por ahora. Pensamos que es un trato justo para un equipo pequeño.
Qué hay realmente listo hoy
Tratamos de no poner nada en el sitio de marketing que no funcione. Esto es lo que es real, hoy:
- Nueve módulos importables —
bloom/core,bloom/shapes,bloom/textures,bloom/text,bloom/audio,bloom/models,bloom/math,bloom/physicsybloom/scene. - Un renderer PBR real — materiales en capas estilo substrate, mapas de sombras en cascada con caché para geometría estática, tone mapping ACES/AgX, autoexposición, bloom, profundidad de campo, motion blur, SSGI, SSAO, TAA y un paso de nitidez CAS para escalas de renderizado fraccionarias.
- Animación esqueletal por GPU — importación de glTF 2.0, linear blend skinning de cuatro huesos en la GPU, hasta 128 articulaciones por esqueleto.
- Físicas con Jolt — cuerpos rígidos y blandos, controladores de personaje, vehículos, raycasts, restricciones, callbacks de contacto. El destino web usa el respaldo de JoltPhysics.js para que el mismo código corra en el navegador.
- Seis plataformas destino — macOS, Windows, Linux, iOS, tvOS y Web. Android está parcialmente cableado pero aún no es publicable.
- Hot reload — guarda un shader WGSL o un JSON de material y el cambio aparece en pantalla en menos de un segundo durante el desarrollo. El código de file-watching se elimina en los builds de release.
- Diecisiete proyectos de ejemplo — desde un Pong de 170 líneas hasta cargar las escenas Intel Sponza y Bistro.
Algunas cosas recientes de las que estamos orgullosos
El renderer ha tenido un buen mes. Algunos puntos destacados:
- Auto-DRS. El renderer autoajusta su escala de renderizado para alcanzar tu framerate objetivo, luego corre un upscale más un paso de RCAS sharpen para que la imagen se mantenga nítida. Tú fijas un FPS objetivo; el motor hace el resto.
- Reflejos planares. Capturas reales en plano espejo con oblique-clip y respaldo de IBL cuando se agota el presupuesto de reflejos. Útil para agua, pisos pulidos y vidrieras.
- Splat mapping con array de texturas. El terreno y las capas de detalle ahora pueden enlazar un array de texturas con mips adecuados, así puedes pintar varios materiales por tile sin pagar una draw call por capa.
- Imposter baker. Un pequeño CLI que hornea atlas de impostors octaédricos para LODs lejanos. Necesitábamos esto en el momento en que empezamos a meter bosques en las escenas.
- HiDPI multiplataforma. Windows, Linux y Web por fin comparten el mismo manejo de HiDPI que macOS e iOS ya tenían. La UI se mantiene nítida en cualquier pantalla.
Cosas que no estamos pretendiendo que estén listas
También te debemos la lista poco favorecedora:
- Android. El crate de plataforma existe y la mayor parte de la superficie FFI está cableada, pero el pegamento de native-activity no está terminado. No le estamos diciendo a nadie que publique en Android con Bloom todavía.
- watchOS. Los shaders compilan, el stub de plataforma está ahí, pero estamos bloqueados a la espera del soporte de watchOS en Perry antes de que esto sea real.
- Geometría virtualizada. Sin equivalente de Nanite, sin mesh shaders, sin ray tracing por hardware. Esto está en la hoja de ruta, no en el binario.
- Tope de animación esqueletal. 128 articulaciones por esqueleto, límite duro, por el tamaño del UBO. Los rigs grandes necesitan workarounds hoy.
- El grafo de escena es joven. Transformaciones, visibilidad, sombras y enlace de materiales funcionan. Los sistemas de consultas y pasadas de optimización más amplias aún no existen.
- Shaders de usuario. Puedes escribir WGSL a mano y cargarlo a través del pipeline de materiales, pero no hay un shader graph dentro del motor ni compilación de shaders en tiempo de ejecución desde TypeScript.
La forma de la API
Tomamos prestado mucho de Raylib para las partes en modo inmediato de la API. Si has escrito un juego en Raylib, la memoria muscular se transfiere:
import { initWindow, windowShouldClose, beginDrawing,
endDrawing, clearBackground, drawText, Colors } from "bloom";
initWindow(800, 450, "Hello Bloom");
while (!windowShouldClose()) {
beginDrawing();
clearBackground(Colors.RAYWHITE);
drawText("Hello, Bloom!", 190, 200, 20, Colors.DARKGRAY);
endDrawing();
} Para el trabajo en 3D y físicas, aplica la misma filosofía: interfaces simples, funciones puras, sin máquinas de estado ocultas. Una cámara es un objeto literal. Un cuerpo rígido es un handle. El pipeline de renderizado se configura llamando funciones en un orden específico, no registrando listeners en un orquestador invisible.
Lo que viene
Nuestra lista corta para las próximas semanas:
- Terminar el pegamento de native-activity de Android y declarar Android publicable.
- Empujar el trabajo del grafo de escena más allá de “básico” hasta algo realmente útil: consultas, refinamiento de frustum culling, helpers de instancing.
- Aterrizar un primer corte de la integración con el editor para que puedas iterar sobre una escena sin reiniciar tu juego.
- Liberar el código de los juegos de ejemplo, incluido un demo un poco más grande que Pong.
- Documentar en detalle el pipeline de compilación de Perry. Varias personas lo han pedido.
Pruébalo
Bloom es de código abierto bajo MIT. Todo el repositorio está en GitHub. Si quieres seguirlo sin hacer fork, la documentación tiene un quick start de 12 líneas, y la página de vitrina lista lo que estamos construyendo nosotros mismos con él.
De cualquier manera: gracias por echar un vistazo. Seguiremos publicando aquí a medida que aterricen piezas más grandes.