La era de las aplicaciones personalizaVibe-codeando nuestra aplicación del impostor

Por qué hice esto

Este pequeño proyecto resonó en mí porque mi grupo de amigos solemos terminar jugando al impostor siempre que quedamos para comer, para cenar, o en fin, para lo que sea.

Es por ello, que después de probar múltiples aplicaciones del impostor, todas ellas con features premium, limitadas y poco personalizables, decidí que, aprovechando mis conocimientos de bases de datos SQL y lo que estoy aprendiendo en el máster de IA de @bigseo acerca del vibecoding, iba a crear una aplicación personalizable, donde pudierais jugar con vuestras palabras, de vuestra zona y vuestro dataset sin tener que pasar por caja.

¿Te suena esa sensación de que la tecnología promete libertad, pero te la vende por suscripción?

¿Te ha pasado eso de pagar premium solo para desbloquear lo más básico: poder cargar tu propio contenido?

Así nació Topo Party Game (Impostor): un juego local, de pasar el móvil de mano en mano, que pone el foco en dos cosas:

  1. Datasets personalizados (packs/categorías como General, Benicolet, Picantes).
  2. Que funcione sin Internet (modo offline-first, porque en una casa rural o en una sobremesa larga el Wi-Fi es un rumor).

Lo divertido es que, a nivel de producto, esto puede parecer una tontería. A nivel técnico, a mí me sirvió como laboratorio: PWA, sincronización offline, Supabase, y despliegue en servidor propio.

Si quieres, puedes acceder al codigo en GitHub desde el siguiente enlace.

Por otra parte, si quieres probar o conocer la aplicación, puedes hacerlo dese aquí: Enlace a Topo Party Game.

Requisitos: lo que yo quería del juego.

Antes de tocar código, me obligué a escribir “las reglas del mundo”:

Requisitos funcionales

  • Crear una partida eligiendo número de jugadores y número de topos.
  • Repartir cartas en secreto, pasando el móvil entre jugadores.
  • Jugadores normales: ven la palabra.
  • Topo: no ve la palabra, solo una pista.
  • Turnos de pistas + votación (la parte social la hace el grupo, no la app).
  • Variantes de juego:
    • Clásico (1 o más topos).
    • Doble topo (uno de los dos está “engañado”).
    • Adivina al jugador.

Requisitos no funcionales

  • Offline-first: el juego debe funcionar aunque el móvil esté sin datos.
  • Administración simple: poder gestionar palabras y pistas sin complicarte la vida.

Elección de stack: por qué React + Vite + Supabase

El stack final quedó así:

  • React 18 + TypeScript: velocidad de iteración y tipado para no romperlo todo cada día.
  • Vite: dev server rápido y build simple.
  • Tailwind CSS + shadcn/ui: UI decente sin perder una semana en CSS.
  • Supabase: base de datos Postgres + Auth + (si lo necesito) Realtime.
  • vite-plugin-pwa: instalación + cache + comportamiento offline.

¿Se podría hacer con otra cosa? Por supuesto. Pero yo quería un stack que me permitiera:

  • Iterar rápido.
  • “Pegarlo” a producción rápidamente.
  • Tener un backend competente sin montar un monstruo.

Supabase, para este tipo de proyectos, es como tener un taller ya montado: herramientas colgadas, luz, banco de trabajo… Entras y empiezas a cortar madera.

Diseño de dominio: cómo modelé el juego en datos

Aquí es donde muchos proyectos “de amigos” se mueren. Porque una cosa es jugar en la mesa, y otra representarlo en datos sin liarla.

En Supabase definí (al menos) estas tablas principales:

  • packs: categorías o conjuntos de palabras (General / Benicolet / Picantes).
  • cards: cada carta tiene palabra + pista (y su pack asociado).
  • game\_sessions: una sesión de juego (estado, configuración, carta seleccionada…).
  • session\_players: jugadores de esa sesión (orden, rol, etc.).

Si lo piensas, es lo mínimo para lograr dos cosas:

  1. Separar contenido (cards/packs) de partidas (sessions).
  2. Poder “pre-cargar” lo necesario para offline.

Y esta separación es oro: cuando separas “contenido” de “evento”, dejas de tener una app caótica y pasas a tener un sistema.

Flujo de juego: del botón “crear partida” a la carta secreta

El flujo “humano” del juego es sencillo, pero técnicamente tienes que evitar trampas:

  1. El host crea la partida: jugadores + topos + packs.
  2. El sistema selecciona una carta (palabra/pista).
  3. Se generan roles por jugador.
  4. Empieza el pase del móvil:
    • Jugador A desbloquea su carta.
    • Se cierra.
    • Siguiente jugador.

En el proyecto lo organicé como lógica encapsulada en hooks, con una separación bastante limpia:

  • useGameSession.ts: lógica principal del juego.
  • useOfflineCards.ts: sincronización offline.
  • useSavedRooms.ts: partidas guardadas.

Offline-first

Lo de “offline” es fácil de prometer y difícil de cumplir.

Una PWA offline-first, para mí, tiene que cumplir esto:

  • Si hoy juegas con el pack Benicolet y mañana no tienes Internet, la app debe recordar las cartas necesarias.
  • Si actualizas palabras cuando sí hay conexión, debe sincronizar sin que el usuario haga nada.

En la práctica, el enfoque fue:

  1. Service Worker (PWA) para cachear los assets (JS/CSS/icons).
  2. Cache de datos: cuando seleccionas packs, hago una sincronización de las cartas y las guardo localmente.
  3. En modo offline, el juego tira de esa cache y funciona.

La decisión clave fue esta: en una app de juego local, el valor está en el dataset. Si el dataset está en el dispositivo, el resto es interfaz.

Y aquí entra el aprendizaje “de oficio”: el offline se arregla diseñando para que el offline sea el estado por defecto.

Mi flujo de trabajo: prototipado y despliegue rápido

Aquí viene una parte que, para mí, es casi más importante que el stack: cómo pasé de “quiero esto” a “esto funciona y lo puede usar cualquiera”.

Porque una cosa es programar en mi ordenador. Y otra, bastante más humillante, es ponerlo en Internet y descubrir qué se rompe cuando el mundo toca mi juguete.

Fase 1 — Lovable

Yo empecé construyéndolo con Lovable. ¿Por qué? Porque cuando tienes una idea pequeña, lo peor que puedes hacer es convertirla en un proyecto infinito antes de ver si tiene sentido.

Lovable me dio lo que necesitaba al inicio: velocidad. Probar el flujo del juego, ajustar pantallas, detectar qué sobraba y qué faltaba, vamos, lo que se llama preparar un MVP o producto mínimo viable.

Fase 2 — Antigravity: el salto al “modo taller”

Cuando el proyecto ya pedía más control, me pasé a Antigravity, el IDE de Google con enfoque agéntico. Ahí la dinámica cambió:

  • Ya no era “generar y pegar”.
  • Era iterar con intención, tocar arquitectura, organizar la lógica y empezar a pensar como alguien que mantiene un sistema.

Y aquí aparece mi relación real con la IA: sirve si sabes guiarla. Si no, lo que obtienes es código “convincente” que falla cuando lo miras de cerca.

Fase 3 — Vercel: probar

Durante todo ese proceso, el despliegue fue deliberadamente pragmático: Vercel.

Para prototipar y validar, es difícil ganarles:

  • Conecta el repositorio.
  • Define variables de entorno.
  • Y cada cambio se despliega solo.

Eso me permitió una cosa muy valiosa: testear como usuario real (móvil, red regular, sesiones largas) sin invertir horas en infraestructura antes de tiempo.

En otras palabras: Vercel fue mi “cinta de correr”. No me hacía más fuerte, pero me dejaba entrenar a diario.

Fase 4 — Hetzner: el control (y la independencia)

Cuando ya tuve una versión estable, hice el movimiento que más me apetecía aprender: publicarlo en mi propio servidor.

Por control.

En Hetzner monté un despliegue sencillo, pero robusto:

  • La aplicación se sirve como estáticos (esencialmente, “una web” optimizada).
  • Un contenedor (por ejemplo, con nginx) se encarga de servir ese contenido.
  • Y delante, un proxy reverso (en mi caso, Caddy) gestiona el dominio y el certificado TLS.
  • A nivel de DNS, un registro A apunta el subdominio a la IP del servidor.

¿Resultado? Un acceso desde mi dominio, con un stack que entiendo, controlo y puedo ajustar sin pedir permiso.

Y aquí está la moraleja técnica: no hace falta complicarse desde el día uno.

Pero sí conviene llegar, en algún momento, a un despliegue que sea tuyo.

Supabase: migraciones, esquema y el mínimo viable de backend

La base de datos vive en Supabase y el esquema se versiona con migraciones SQL en supabase/migrations/.

Si trabajas con Supabase CLI, la idea es poder empujar cambios así:

supabase db push

Para mí, el valor de esto es más grande que la app: estoy aprendiendo a tratar el esquema como código, que es la diferencia entre “tengo una base de datos” y “tengo un sistema mantenible”.

Lo que me enseñó el vibecoding

Yo he iterado mucho con IA durante el desarrollo. Pero aquí va la realidad incómoda: la IA no programa por ti, sino que amplifica lo que eres capaz de dirigir.

En este proyecto, el patrón fue casi siempre el mismo:

  1. Me encuentro un problema (offline, estado inconsistente, lógica de roles, build roto, etc.).
  2. La IA me propone tres soluciones (dos falsas y una medio útil).
  3. Yo hago de “product owner” técnico: recorto, corrijo, pruebo, vuelvo a preguntar.

Vibecoding no es “teclear prompts”. Vibecoding es mantener el timón.

Y esa metáfora me gusta: el barco puede tener motor, autopiloto, mapas… pero si no sabes dónde está el puerto, la tecnología solo acelera el naufragio.

Objeciones típicas

«¿Por qué Supabase si el juego es local?»

Porque el dataset es el corazón. Yo quería:

  • Administrar palabras con facilidad.
  • Sincronizar al móvil cuando haya Internet.
  • No depender de “meter el CSV a mano cada vez”.

Supabase me da un backend listo sin reinventar Postgres.

«¿Y la usabilidad?»

Tienes razón: no es una app pensada para “usuarios masivos”. Es una herramienta para un contexto específico.

Pero incluso así, la usabilidad es un backlog claro: onboarding, estados de error, diseño de flujo, etc.

Deja un comentario

¡Únete a mi comunidad y optimiza tus RRHH con IA!

Suscríbete para recibir contenido exclusivo sobre cómo la inteligencia artificial puede revolucionar tus procesos de RRHH, y accede a mi guía gratuita de bienvenida.