Qüizo es una plataforma de quizzes multijugador en vivo que construimos para eventos reales: un código QR aparece en el proyector, el celular que alguien tiene en el bolsillo se convierte en buzzer treinta segundos después, y un anfitrión maneja toda la sala desde una laptop. Está en vivo en quizo.dizenz.com, y existe para llenar los diez minutos del entretiempo, la hora antes del arranque, o el hueco entre una charla y el brindis —los momentos en que un lugar tiene la atención de una multitud y nada apuntado hacia ella.
No hay ninguna app para instalar ni cuenta para crear —un apodo y un escaneo son todo el sign-up—, y eso importa porque la sala es el celular de un desconocido sobre el wifi de otro, no un entorno de test controlado. Debajo de esa simpleza hay un problema de tiempo real genuinamente difícil: cada celular en la sala tiene que coincidir, al instante, en la misma pregunta, el mismo reloj y el mismo leaderboard, sin filtrarle nunca la respuesta correcta a alguien que todavía no contestó.
Este caso de estudio cuenta cómo llegamos ahí: un servidor que es dueño de cada reloj de la sala, tres modos de sesión construidos sobre una sola máquina de estados, un ejercicio de load testing que encontró el techo real en vez de suponerlo, un agente de IA para redactar preguntas que estructuralmente no puede escribir en la base de datos, y tres idiomas —incluyendo árabe de derecha a izquierda— corriendo en la misma pantalla a la vez.
El Desafío
La mayoría de los productos multijugador asumen una audiencia cautiva y técnica —una app ya instalada, una conexión estable, jugadores dispuestos a esperar una pantalla de carga. La audiencia de Qüizo es la opuesta: doscientos desconocidos sobre el wifi del lugar, entrando desde cualquier navegador que ya tengan abierto, esperando estar jugando a los treinta segundos de que aparezca el código QR. Cualquier fricción en ese camino —un join lento, una cuenta regresiva que se traba, un botón de respuesta que no registra el toque— se lee como que el show se rompió, no como un ticket de bug.
Además, un solo operador tiene que manejar todo el evento solo: arrancar el juego, cambiar de quiz en medio de la sala, reiniciar una ronda que salió mal, echar a un jugador que no debería estar, todo desde una laptop, sin bajar nunca el proyector. Y como la respuesta correcta es información genuinamente secreta, cada capa que toca el estado del juego —la base de datos, el servidor, tres serializadores distintos— tiene que mantenerla así aun mientras retransmite el estado de la sala varias veces por segundo.
El Enfoque
Elegimos el onboarding más simple que se nos ocurrió: un código QR y un código de sala de seis caracteres en la pantalla grande, un apodo, y nada más. Sin email, sin contraseña, sin descargar nada —todo el "sign-up" pasa en el tiempo que toma escribir seis letras. El código de sala sale de un alfabeto sin los caracteres visualmente ambiguos, porque un anfitrión leyéndolo en voz alta para doscientas personas no se puede dar el lujo de que la sala confunda un cero con una O.
La otra decisión de la que se desprende todo lo demás: el servidor es la única fuente de verdad de todo el juego, y no se confía en nada más. Cada pregunta tiene un único deadline autoritativo, calculado y guardado en el momento en que se abre —nunca en el cliente, nunca derivado de un timestamp del cliente—, así que un celular que se reconecta a mitad de pregunta después de un bache de wifi retoma la cuenta regresiva exactamente donde está la sala, en vez de arrancar con veinte segundos frescos que no se ganó.
El Servidor Es Dueño del Reloj
Cada mutación en Qüizo —unirse, contestar, arrancar una pregunta, echar a un jugador— pasa por una Server Action, nunca por un endpoint REST; los únicos dos Route Handlers de la app son streams de Server-Sent Events de solo lectura, y hasta esos solo llaman a una función de lectura. Esa única regla es lo que mantiene honesta la máquina de estados: hay exactamente un lugar donde puede cambiar la fase de una sesión, protegido por una versión de concurrencia optimista grabada en cada fila de sesión, así que una acción del anfitrión tocada dos veces termina siendo un no-op en vez de saltear o duplicar una transición.
El proyector, el controller del anfitrión, y cada celular de la sala son tres vistas sobre ese mismo estado del que es dueño el servidor, sincronizadas por Server-Sent Events alimentados por un canal de Postgres LISTEN/NOTIFY —con un poll cada pocos segundos como respaldo de corrección ante cualquier notificación perdida. Una conexión que sobrevive a un bache de wifi del lugar sigue mostrando el último estado que conocía, muestra una pequeña pastilla de "Reconectando…" en vez de quedar en blanco, y retoma el estado real de la sala apenas vuelve —porque el proyector apagándose en medio del juego es la única falla que toda una sala nota al mismo tiempo.
Proyector
La vista de presentador en pantalla grande —código QR, pregunta, cuenta regresiva y leaderboard para que vea toda la sala.
Controller del anfitrión
Una laptop maneja cada fase: arrancar, siguiente, revelar, reiniciar —con un contador en vivo de "once de veinticuatro contestaron".
Cada celular
La vista propia de cada jugador sobre la misma pregunta, su propio orden de respuestas mezclado, y su propia cuenta regresiva.
Una sesión respaldada por Postgres
Server-Sent Events alimentados por un canal de Postgres LISTEN/NOTIFY, con un poll cada pocos segundos como respaldo de corrección.
Ningún cliente —ni siquiera el controller del propio anfitrión— calcula nunca un deadline o un puntaje. Lo hace el servidor, una vez, y lo transmite.
Tres modos de juego corren sobre esa misma máquina de estados. Classic mantiene a todos en la misma pregunta al mismo tiempo, gana el puntaje más alto; Elimination saca a un jugador con una respuesta incorrecta o un timeout, y guarda en qué pregunta murió en vez de solo un booleano, así que un wipeout todavía se puede rankear por cuánto aguantó cada uno. El modo Deferred es la excepción a propósito —un anfitrión abre una ventana con límite de tiempo y cada celular juega todo el quiz a su propio ritmo, porque los dos modos sincronizados están acotados a una sola sala por diseño, y Deferred existe específicamente para levantar ese límite.
Classic
Todos contestan la misma pregunta en el mismo reloj. Gana el puntaje más alto.
Elimination
Una respuesta incorrecta o un timeout saca a un jugador —se guarda en qué pregunta murió, no solo que perdió.
Deferred
Un anfitrión abre una ventana con límite de tiempo; cada celular juega todo el quiz a su propio ritmo, sin ningún reloj compartido.
Esa razón es una lectura cuadrática: cada conexión abierta relee sola toda la lista de jugadores de la sala cada pocos segundos, lo que da aproximadamente N²/3 filas de jugador por segundo en toda la sala —unas 13.300 filas por segundo con doscientos jugadores, el tope duro de Classic y Elimination, solo para renderizar "esperando al anfitrión". Por eso existe el modo Deferred: su reloj por jugador elimina por completo el broadcast a toda la sala, así que sus lecturas se mantienen a costo constante sin importar cuánta gente juegue, y el tope de 200 jugadores que acota a los modos sincronizados simplemente no le aplica.
Lo que medimos en Classic / Elimination
- Cada conexión abierta relee todo el roster cada pocos segundos
- Cuesta aproximadamente N²/3 filas de jugador por segundo en toda la sala
- ≈13.300 filas por segundo en el tope de 200 jugadores
Modo Deferred
- Sin broadcast a toda la sala —cada celular es dueño de su propio reloj
- El presentador lee dos count() y un top-10, nunca el roster
- El costo de lectura se mantiene plano sin importar cuánta gente juegue
Un tope que elegimos, no uno con el que chocamos
200 jugadores es un tope duro y deliberado en el código para Classic y Elimination —un número que probamos con carga y después escribimos, no una aspiración de marketing. El modo Deferred no tiene broadcast a toda la sala, así que ese techo no le aplica.
Escribir preguntas de quiz a mano no escala para un cliente que tiene un evento la semana que viene, así que un agente de IA las redacta a partir de un prompt de una línea —pero estructuralmente no puede tocar la base de datos. Tiene exactamente dos tools, ambas de solo lectura y scopeadas por el client id de quien llama, nunca por algo que provea el modelo, y cada otra capacidad que el framework del agente trae por defecto —una shell, un sistema de archivos, acceso a la web, sub-agentes— está explícitamente deshabilitada. La salida del modelo es un draft tipado que termina en el mismo editor y la misma validación por la que pasa una pregunta escrita a mano; solo un humano que aprieta Guardar escribe una fila.
list_quizzes / get_quiz
Las únicas dos tools del modelo —ambas de solo lectura, ambas scopeadas por el client id de quien llama.
Todas las demás tools deshabilitadas
Sin shell, sin sistema de archivos, sin acceso a la web, sin sub-agentes —apagadas explícitamente, no simplemente sin usar.
Salida como draft tipado
El turno del modelo termina en un draft validado por schema, nunca en una escritura en la base de datos.
Revisión humana
Cada pregunta redactada se abre en el mismo editor que usaría una escrita a mano, completamente editable.
createQuiz / createQuestion
Las únicas dos funciones que tocan la base de datos alguna vez —y solo cuando un humano aprieta Guardar.
El agente puede leer los quizzes que ya tenés. No puede escribir ni uno solo.
Qüizo corre en inglés, español y árabe, y las dos nociones de "idioma" son sistemas deliberadamente distintos: en qué idioma renderiza la interfaz del anfitrión, y en qué idioma juega el contenido de un quiz, unidos en exactamente un solo lugar en vez de mezclados por todos lados. Una pantalla de presentador puede llevar dos idiomas de contenido a la vez para una multitud bilingüe, mientras cada celular renderiza en el idioma que su propio jugador eligió al unirse —con el flip de derecha a izquierda incluido—, y una clave de traducción desconocida falla como error de tipos en el build en vez de como un texto en blanco frente a una sala en vivo.
Idioma de la interfaz · cookie, BCP-47 en minúscula
En qué idioma renderiza la interfaz alrededor de la app —navbar, botones, formularios.
Idioma del contenido del quiz · enum de Prisma, en mayúscula
En qué idioma están escritas y se muestran las preguntas y opciones de un quiz, configurado por quiz.
Unidos en un solo lugar · un único mapeo
El único archivo que traduce entre los dos —cada otro punto de llamada se queda de su propio lado de la línea.
Una pantalla, todos los idiomas a la vez
El presentador puede mostrar dos idiomas de contenido lado a lado para una multitud bilingüe, mientras cada celular renderiza en el idioma que eligió su propio jugador —con el flip de derecha a izquierda incluido.
Una clave de traducción desconocida es un error de tipos en el build, no un texto en blanco frente a una sala en vivo.
Lo Que Construimos
Más allá del loop de juego principal, Qüizo creció hasta ser una herramienta completa de operación de eventos:
Tres modos de juego
Classic, Elimination, y el modo Deferred a ritmo propio, todos corriendo sobre una sola máquina de estados de sesión compartida
Salas de ensayo
Una sesión de test completamente jugable que un anfitrión puede correr y resetear sin tocar el roster ni las estadísticas del evento real
Branding de evento
El logo y el color de marca de un cliente en el proyector, el controller, y el celular de cada jugador —aprobados antes de que la sala los vea
Captura de leads
Campos opcionales de email, teléfono y empresa en el formulario de ingreso, con el consentimiento con timestamp y exportado a Excel o CSV después del evento
Redacción de quizzes con IA
Un prompt de una línea se convierte en un quiz borrador revisable en segundos, que nunca se guarda hasta que un humano lo confirma
Tres idiomas
Inglés, español y árabe de derecha a izquierda, en la pantalla de presentador y en cada celular a la vez
Resultados
Qüizo pasó de un repositorio vacío a una plataforma de eventos en producción en unas tres semanas, construida casi enteramente por un solo ingeniero trabajando con agentes de IA para programar —un workflow documentado con el mismo nivel de detalle que el producto en sí, hasta una doctrina de arquitectura de 650 líneas que los propios agentes leen antes de tocar una sola línea de código.
357
commits en tres semanas
191
issues resueltos
~72.000
líneas de TypeScript
~1.700
tests unitarios en 176 archivos
~250
tests end-to-end en 19 specs
200
tope de jugadores —un límite, no una aspiración
Un gate completo antes de cada push
Corre localmente antes de que el código llegue a CI.



