Crashout es un juego de carreras multijugador en vivo que construimos para eventos: una pantalla grande en el lugar, un anfitrión manejando todo desde una tablet, y los invitados manejando con el celular que ya tienen en el bolsillo. Está en vivo hoy en crashout.dizenz.com.
La idea nació de una restricción simple: el entretenimiento grupal en un evento real no tiene ninguno de los lujos que asume un juego multijugador normal. No podés pedirle a veinte invitados que instalen una app antes de que arranque la fiesta. No podés confiar en un wifi rápido y estable. Y quien esté a cargo tiene que poder arrancar, resetear y encadenar carrera tras carrera sin tocar nunca la TV.
Este caso de estudio cuenta cómo diseñamos alrededor de esa restricción: las decisiones de producto, y la ingeniería detrás de ellas.
El Desafío
La mayoría de los juegos multijugador asumen una audiencia cautiva y técnica: una app ya instalada, una conexión estable, jugadores que ya conocen los controles. Nada de eso se cumple en una fiesta corporativa, una activación de marca o una salida con amigos. El onboarding tiene que funcionar para un invitado que nunca vio el juego, con cualquier celular que tenga a mano, sobre el wifi que el lugar ofrezca.
Además, una sola persona tiene que poder manejar el evento de punta a punta —arrancar una carrera, resetearla, arrancar la siguiente— desde una tablet, sin acercarse nunca a la TV. Y cada pantalla en la sala, desde la pantalla grande hasta el celular de cada jugador, tiene que coincidir exactamente en qué está pasando en cada momento, o todo se desarma en vivo frente a la gente.
El Enfoque
Elegimos el onboarding más simple posible: un código QR en la pantalla grande, una pestaña del navegador en el propio celular del jugador, sin app, sin cuenta. Escanear, escribir un nombre, elegir un auto, arrancar a manejar. Esa única decisión determinó casi todo lo demás del producto.
Escanear
El invitado escanea el código QR de la pantalla grande.
Nombre
Escribe un nombre — nada más que completar.
Auto
Elige un auto del catálogo en vivo.
Manejar
Maneja con el celular que ya tiene en el bolsillo.
sin app · sin cuenta · solo el navegador
La otra decisión clave fue hacer que el servidor sea la única fuente de verdad para todo el juego. Ningún cliente —ni la pantalla grande, ni el celular de un jugador, ni el controller del anfitrión— puede decidir por su cuenta qué pasó en la carrera. Solo renderizan lo que el servidor les dice. Eso es lo que permite que una sala llena de celulares de desconocidos, sobre un wifi impredecible, coincidan todos en la misma carrera.
Un Servidor Autoritativo, Tres Pantallas Sincronizadas
El backend es un servidor en Go construido sobre el router HTTP de la librería estándar y una librería liviana de WebSocket, sin ningún framework web de por medio. Cada evento tiene su propia sala, y cada sala es dueña de su propia goroutine de simulación y su propio canal de entrada: las salas nunca comparten estado ni se bloquean entre sí, lo que mantiene simple razonar sobre un juego con decenas de eventos simultáneos.
La simulación corre a un tick interno de 60Hz, pero solo transmite un snapshot a los clientes 20 veces por segundo, y el cliente interpola entre snapshots para lograr movimiento fluido. Esa proporción no es arbitraria: originalmente corríamos el tick y el snapshot en una relación 30/20, que producía un patrón de transmisión irregular de 33/33/67 milisegundos y se notaba como un tartamudeo visible en la pantalla grande. Pasar a una relación 60/20, un múltiplo exacto, da una cadencia perfectamente uniforme de 50 milisegundos y lo solucionó. Es un buen ejemplo del tipo de detalle que solo se nota cuando estás viendo una carrera correr en una TV real.
Tres pantallas se conectan a ese mismo servidor: la pantalla grande renderiza la carrera y el QR del lobby en canvas, la página de joystick convierte el celular de un jugador en dirección y bocina, y el controller le da al anfitrión inicio, reset y configuración. Un cuarto modo, una carrera demo de un solo dispositivo, reutiliza exactamente el mismo protocolo: solo abre dos conexiones WebSocket desde un celular en vez de inventar un nuevo rol de cliente.
Pantalla grande
Renderiza la carrera y el QR del lobby en canvas.
Celular del jugador
Página de joystick: dirección y bocina.
Tablet del anfitrión
Controller: inicio, reset y configuración.
Servidor Go
Una sala por evento, cada una con su propia goroutine de simulación y canal de entrada — sin estado compartido, sin locks.
El servidor es la única fuente de verdad — cada cliente solo renderiza lo que le dicen.
Todo eso corre repartido entre dos proveedores. El frontend está en Vercel — cada una de las tres pantallas es simplemente una ruta de la misma app Next.js, que llega al backend por orígenes WebSocket y REST absolutos, fijados en tiempo de build. El backend es deliberadamente aburrido: una sola instancia EC2 en us-east-2, provisionada con Terraform. Sin contenedores, sin orquestador, sin balanceador de carga.
Caddy va adelante, terminando TLS para cada conexión WebSocket con certificados que él mismo emite y renueva contra Let's Encrypt — los puertos internos del backend nunca son accesibles desde afuera. Todo lo que tiene que sobrevivir a un proceso vive en PostgreSQL en una máquina aparte, con conexión por SSL y migraciones que corren al arrancar. Los deploys son lo único que hace GitHub Actions: lint, typecheck, tests y build corren localmente antes de cualquier push.
Vercel
App Next.js — Las tres pantallas son rutas de una misma app — pantalla grande, joystick, controller.
AWS · us-east-2
Caddy
Termina TLS, renueva sus propios certificados de Let's Encrypt y rutea al slot sano.
Una instancia EC2
PostgreSQL
Una máquina aparte, solo SSL. Ambos slots comparten una base — el único estado que un deploy traspasa.
Solo Caddy está expuesto — los puertos de los slots nunca son accesibles desde internet.
Como Crashout corre en eventos en vivo, un deploy nunca puede interrumpir una carrera en curso. Corremos producción detrás de slots de deploy blue/green en la misma máquina, y una señal de apagado le indica al slot viejo que drene —termine las carreras que estén corriendo, rechace las nuevas— durante hasta tres horas en vez de cortar las conexiones de inmediato. Los clientes anclan sus reconexiones al slot en el que arrancaron, y un token de reanudación le permite a un jugador cuyo celular se cae en medio de una carrera volver a exactamente el auto que estaba manejando.
Slot nuevo · Activo
Acepta todas las carreras nuevas.
Slot viejo · Drenando, hasta 3h
Termina las carreras que ya están corriendo, rechaza las nuevas.
Lo Que Construimos
Más allá del loop de carrera principal, el producto creció hasta ser una herramienta completa de operación de eventos:
Catálogo de contenido
15 vehículos en 5 líneas de modelo, 8 tipos de obstáculos, y 4 fondos de carrera seleccionables
Salas configurables
De 2 hasta 40 jugadores, con carreras de eliminación y un límite de tiempo de 60 segundos
Tres idiomas, en vivo
Inglés, español y árabe, cambiables en vivo por el anfitrión —incluyendo soporte completo de layout de derecha a izquierda
Dos esquemas de control
Botones en pantalla o un joystick analógico, a elección de cada jugador
Leaderboards públicos
De solo lectura para cada evento, para compartir después de la carrera
Panel de administración
Protegido por contraseña: creación de eventos, códigos de seguridad, un catálogo en vivo de vehículos y fondos, y un registro de auditoría de autenticación
Soporte al anfitrión
Escala directo a un mensaje de WhatsApp pre-completado con el código del evento y el estado de la carrera
Resultados
Crashout lleva alrededor de cinco meses y medio de desarrollo continuo y activo, y lo construimos con el estándar de software de producción, no el de un prototipo de juego para fiestas:
478
commits desde el scaffold inicial (feb 2026)
~17.000
líneas de Go en el backend
~23.000
líneas de TypeScript en el frontend
400+
tests en Go
~1.900
tests de JS/TS
46
specs E2E de Playwright
Un gate completo antes de cada push
Corre localmente antes de que el código llegue a CI.




