Póker de dados → online
Juego multijugador online de póker de dados, con física 3D, marketplace de Bules, torneos, retos 1v1, backoffice de administración y apps nativas iOS/Android.
Resumen del proyecto
Bule Bule es una webapp multijugador en tiempo real que implementa el juego tradicional de póker de dados (dados AS, K, Q, J, 8, 7). Los jugadores se reúnen en salas con código, turnan para tirar dados, descartar y quedarse con la mejor mano posible. Quien pierde cada ronda rompe un palillo; sin palillos queda en capilla y una derrota más lo elimina. Un Repóker (cinco iguales) libera al jugador de la competición.
Multijugador real
Salas públicas y privadas con código de 4 caracteres. Hasta 10 jugadores. Lobby de espera con vista en tiempo real, invitaciones directas y retos 1v1.
Física 3D
Dados renderizados con Three.js y simulación de física real con Rapier3D. Sombras, rebotes, colisiones deterministas (semilla por tirada) y antialiasing FXAA.
Marketplace y Bules
Moneda virtual del juego (Bules) ganada jugando o comprada (paquete de 1.000 Bules vía Bizum). Se gasta en skins de dados (mármoles, rojo transparente) e items cosméticos de bar (cubata, churros, balón…). Economía cerrada: no canjeable por dinero real.
Torneos con acceso por item
Torneos exclusivos a los que solo se accede poseyendo un item concreto del marketplace (p. ej. el torneo "Bar Doce" requiere el item Churros). Gestionados desde el backoffice, con rango de fechas y niveles de acceso.
Bot IA
Modo 1 vs Bot. La IA evalúa la mano, decide qué dados guardar y cuándo plantarse según probabilidad.
Sistema de vidas
Palillos visuales por jugador. Capilla, liberación por Repóker de X y modo de juego limitado por rondas.
Retos, favoritos e historial
Reto directo 1v1 a otro jugador (online u offline vía push/FCM), favoritos con estrella en el ranking, historial de actividad unificado (partidas y compras) e indicador de estado online.
Notificaciones push nativas
Firebase Cloud Messaging (FCM) para retos e invitaciones aunque la app esté cerrada en Android. Web Push como fallback en navegador.
Mensajería en partida
Reacciones rápidas y burbujas de chat durante la partida (prueba de concepto), visibles para todos los jugadores en la sala, con sonido al pulsar.
Diseño sonoro completo
Cubilete antes de lanzar, rebotes de dados, descarte, palillo roto, música de lobby y de partida. Todo sincronizado con animaciones.
Login y publicidad nativos
Google Sign-In nativo en Android (además de OAuth web) con verificación de token en servidor. AdMob integrado (banners + UMP consent), pendiente de vincular App ID real hasta publicación en producción. Sin login: modo invitado con nombre autogenerado tipo el_pirri_042, persistido en localStorage.
Mobile-first y apps nativas
Agitar el móvil para tirar, gestos táctiles para descartar dados, PWA con service worker. App Android empaquetada con Capacitor (v1.3.37), actualización forzosa por versión mínima gestionada desde el backoffice.
Backoffice de administración
Panel propio (React, servido desde el backend en Railway) para gestionar items del marketplace, torneos, usuarios, feedback, versiones de app publicadas y ajustes globales (límite de jugadores, feature flags).
Reglas de juego implementadas
| Mano | Rango | Descripción |
|---|---|---|
| Repóker de X | 7 | 5 iguales → libera al jugador de futuras rondas |
| Póker | 6 | 4 iguales |
| Full | 5 | 3 + 2 iguales |
| Escalera | 4 | 5 valores consecutivos |
| Trío | 3 | 3 iguales |
| Dobles parejas | 2 | 2 pares distintos |
| Pareja | 1 | 2 iguales |
| Carta alta | 0 | Sin combinación |
Mecánica del Liberado
Cuando un jugador consigue un Repóker de X (cinco dados iguales), queda liberado:
- Ya no participa en las tiradas de rondas sucesivas.
- No puede ser declarado ganador ni perdedor de ninguna ronda posterior.
- En una partida de 2 jugadores: la partida termina inmediatamente; el rival pierde.
- En partidas de 3+ jugadores: los demás siguen jugando con normalidad hasta que alguien acumule 3 roturas (capilla).
Arquitectura técnica
Cliente
React 18 SPA · Vite
- Three.js — render 3D
- Rapier3D — física WASM
- Socket.io-client
- @react-oauth/google
- Web Audio API
- Service Worker
Servidor
Node.js · Express · SQLite
- Socket.io — eventos de juego, marketplace, torneos, retos
- SQLite persistida en volumen Railway (usuarios, items, torneos, ajustes)
- web-push + Firebase Admin (FCM) — VAPID y push nativo
- Rate limiting por socket + limpieza automática de salas
- Estado de partidas en memoria · lógica de juego pura
- Backoffice (React) servido como app estática desde el propio backend
Infraestructura
Cloud · CI/CD automático
- Firebase Hosting (frontend) — deploy automático por GitHub Actions
- Railway Hobby (backend + SQLite en volumen) — deploy automático al hacer push a
main - GitHub — rama
main(producción) + ramadev(trabajo en curso) - App Android empaquetada con Capacitor, publicada vía Play Console
Stack completo
| Capa | Tecnología | Versión | Rol |
|---|---|---|---|
| UI framework | React | 18.3 | Gestión de estado y render |
| Bundler | Vite | 5.4 | Build, HMR, dev server |
| 3D render | Three.js | 0.184 | Escena WebGL, materiales toon, sombras, FXAA post-process |
| Física | Rapier3D (WASM) | 0.19 | Simulación de cuerpos rígidos de dados |
| Tiempo real | Socket.io | 4.7 | Comunicación bidireccional cliente-servidor |
| Auth | @react-oauth/google | 0.13 | OAuth 2.0 con Google (opcional) |
| Backend HTTP | Express | 4.18 | API REST mínima + VAPID endpoint |
| Push | web-push | 3.6 | Notificaciones push VAPID/WebPush (fallback navegador) |
| Push nativo | Firebase Admin SDK (FCM) | — | Notificaciones push nativas en Android para retos e invitaciones |
| Base de datos | SQLite (better-sqlite3) | — | Usuarios, items, torneos, feedback, ajustes — persistida en volumen Railway |
| App nativa | Capacitor | — | Empaqueta el frontend web como app Android; Google Sign-In nativo, AdMob, FCM |
| Publicidad | @capacitor-community/admob | — | Banners + UMP consent; App ID de test hasta publicación en producción |
| Backoffice | React + Vite (app separada) | — | Panel de administración servido como estático desde server.js |
| Hosting frontend | Firebase Hosting | — | CDN global, HTTPS automático, deploy vía GitHub Actions |
| Hosting backend | Railway (Hobby) | — | Contenedor Node.js + volumen persistente para SQLite, auto-SSL, deploy automático |
Estructura de directorios
bule-bule/
├── backend/
│ ├── server.js # Lógica de servidor (2.241 líneas)
│ ├── gameLogic.js # Evaluación y comparación de manos (51 líneas)
│ ├── data/ # SQLite (bulebule.db) persistida en volumen Railway
│ ├── backoffice/ # App React separada — panel de administración
│ │ └── src/pages/ # Login, Settings, Items, Tournaments, Users, Feedback
│ ├── Dockerfile
│ └── railway.json
└── frontend/
├── android/ # Proyecto Capacitor (build nativo Android)
├── public/
│ ├── assets/ # Imágenes, audios (.mp3), items, dados, sw.js
│ └── index.html
└── src/
├── App.jsx # Routing, música, auth, modo invitado
├── socket.js # Singleton Socket.io
├── analytics.js
├── version.js
├── index.css # Todos los estilos
├── hooks/useSheetDrag.js
├── utils/ # admob.js, push.js, imgSrc.js
└── components/
├── GameBoard.jsx # Pantalla de juego principal
├── DiceRollerScene.jsx # Escena 3D Three.js + Rapier
├── Marketplace.jsx # Tienda de Bules e items
├── TournamentList.jsx / TournamentLobby.jsx
├── ChallengeInvite.jsx # Retos 1v1
├── UserSection.jsx / UserDetailSheet.jsx
├── RoomList.jsx / WaitingRoom.jsx / WaitingBar.jsx
├── CreateRoom.jsx / CountdownButton.jsx
├── AdBanner.jsx # Banner AdMob
├── AnimacionNextPlayer.jsx / AnimacionPalilloRoto.jsx
├── Die.jsx
└── AlaCaidaToast.jsx
Flujo de comunicación
Cliente Servidor
│ │
│─── create_room / join_room ────────▶│ rooms{} en memoria
│◀── room_state (broadcast) ─────────│
│ │
│─── roll { keptIndices } ───────────▶│ rollDie() × descartados
│◀── room_state ──────────────────────│ nueva semilla + rollSeed
│ │
│─── report_faces { faces } ─────────▶│ sincroniza caras físicas 3D
│─── stand / continue_turn ──────────▶│ finishTurn / proceedTurn
│◀── room_state ──────────────────────│
│ │
│─── bot_ready ──────────────────────▶│ botAct() → siguientes tiradas
│◀── room_state ──────────────────────│
│ │
│─── buy_item / buy_bules_pack ──────▶│ descuenta/añade Bules en SQLite
│─── get_marketplace / get_user_items▶│ catálogo + inventario del usuario
│◀── ok / error (callback) ──────────│
│ │
│─── join_tournament ────────────────▶│ valida item requerido, inscribe
│─── challenge_user ──────────────────▶│ crea reto, envía push/FCM si offline
│◀── get_settings (público) ─────────│ maxPlayersLimit + featureFlags
Backoffice — panel de administración
App React independiente (backend/backoffice/) compilada a estático y servida desde el propio
backend en /backoffice, protegida con token de administrador. Permite gestionar items del
marketplace, torneos, versiones de app publicadas, feedback de usuarios, usuarios registrados y, desde
julio de 2026, una sección de Ajustes con el máximo de jugadores por sala y una lista de
feature flags on/off — pensada para poder aplicar reglas por país o segmento en el futuro sin
rehacer el modelo de datos. Los flags se guardan en la tabla config pero ningún flag real
se consume todavía en el juego.
Histórico de commits — línea de vida
report_faces sincroniza la cara real visible con el servidor.preventDefault en touchend para móvil.awaitContinue serverside, contador de 30s.world.timestep × 1.15. Ghost colliders fijos para dados quietos.applyRoundLoss extraído como helper.isPrivate, aparecen en el listado; sanitizeForList incluye el estado de privacidad.rolling deshabilita el botón desde el primer click hasta que el servidor confirma la tirada.endRound termina la partida con endReason: 'liberado'.finishTurn comprueba nonLiberado.length (total) en lugar de pending.length (en-ronda), evitando que se corte el turno de jugadores pendientes.ads.txt, ajustes de layout.ALLOWED_ORIGIN, sustituyendo el origin: '*' original.finished/inactivas.Audio, constante IS_MOBILE extraída del render, limpieza de usuarios invitados al desconectar.server.js, App.jsx y GameBoard.jsx.Backend — server.js + gameLogic.js
Estado global
rooms{} — diccionario en memoria indexado por código de sala. Cada sala contiene la lista de jugadores, fase de juego, índice del jugador activo, timers y metadatos de ronda.
registeredUsers{} — usuarios registrados en la sesión (Google u otros). Mapea userId → { name, email, picture, socketId, pushSubscription, fcmToken }, cargado en memoria al arrancar desde SQLite.
socketToUser{} — índice invertido socketId → userId para lookup O(1) en desconexiones y broadcasts dirigidos.
Desde junio de 2026, usuarios, items del marketplace, torneos, feedback y ajustes globales se persisten en SQLite (volumen de Railway), sobreviviendo a reinicios del proceso.
rooms{}) siguen viviendo solo en memoria del proceso Node. Un reinicio de Railway durante una partida activa la destruye — la persistencia cubre usuarios/economía/configuración, no el estado de juego en vivo.
gameLogic.js — Evaluación de manos
Módulo puro sin efectos secundarios. Exporta rollDie(), evaluateHand(dice[]) y compareHands(h1, h2). El rango 7 (Repóker de X) devuelve { rank: 7, name: 'Repóker de K', topKey: 'K', … }.
rollDie() usa Math.random() en servidor. El cliente usa rollSeed solo para la animación visual. Si report_faces diverge del resultado servidor, la mano mostrada puede no coincidir con la evaluada.
Marketplace y economía de Bules
Eventos get_marketplace, buy_item, buy_bules_pack, get_user_items y get_user_history. Los items tienen categoría (skin, bar, pack) e imagen gestionadas desde el backoffice. El paquete pack-1000-bules se cobra por Bizum fuera de la app; el servidor solo acredita el saldo tras confirmación manual del admin.
Torneos con acceso por item
Eventos get_tournaments, join_tournament, leave_tournament. Un torneo puede requerir la posesión de un item concreto (requiredItemId) para poder inscribirse — validado server-side contra el inventario del usuario en SQLite antes de aceptar el join_tournament. Gestión completa (crear, editar, fechas, item requerido) desde el backoffice.
Ciclo de vida de una sala
lobby → playing → results → playing → ... → finished
- lobby: jugadores se unen. El host arranca con
start_game. - playing: ronda activa. Ciclo de turnos con timers.
- results: entre rondas. El host avanza con
next_round. - finished: partida terminada (
endReason:capilla|rounds|liberado). El host puede hacer revancha.
Mecánica Liberado — lógica serverside
En finishTurn: si el jugador acaba de conseguir Repóker, se fija player.liberado = true y player.hand = null (excluido de la comparación de rondas). Se comprueba nonLiberado.length — si ≤1, se llama a endRound.
En endRound: si hay un liberado y nonLiberado.length ≤ 1, la partida termina inmediatamente con endReason: 'liberado'. En rondas sucesivas, los liberados tienen done = true desde el inicio (startRound).
Salas privadas
Al crear una sala con isPrivate: true, el código de 4 caracteres actúa como contraseña. Las salas privadas aparecen en el listado público (con indicador visual), pero el cliente valida el código antes de emitir join_room.
join_room sin pasar por la modal, saltándose la protección. Para seguridad real, el servidor debería rechazar join_room si el código no coincide.
Gestión de turnos — timers
| Timer | Duración | Acción al expirar |
|---|---|---|
turnTimerId | 30s | Si no ha tirado: tira automático. Si ya tiró: planta. |
continueTimerId | 30s (human) / 3s (bot) | Llama a proceedTurn, arrancando el turno. |
Desconexión de jugadores
Al desconectarse, el jugador se elimina de la sala con un retraso de 20 segundos para permitir reconexiones. Desde el fix del 30 de julio (03b3631), la app minimizada en Android ya no deja al jugador colgado ni provoca comportamiento errático del bot: el frontend muestra feedback de conexión explícito y el backend está blindado contra las caídas de socket que este escenario provocaba.
currentPlayer; el turno queda bloqueado hasta que expire el turnTimer (hasta 30s adicionales). El fix de julio mejora el feedback visual del resto de jugadores durante esa ventana, pero el mecanismo de espera en sí no ha cambiado.
report_faces — Superficie de ataque
Notificaciones push — Web Push (VAPID) y FCM nativo
Web Push (VAPID) para navegador y Firebase Cloud Messaging para la app Android nativa. Los retos a usuarios offline consultan primero el token FCM y, si no está disponible, hacen fallback a la suscripción Web Push guardada en SQLite.
VAPID_PUBLIC_KEY / VAPID_PRIVATE_KEY fijadas en Railway, cada reinicio generaría claves nuevas e invalidaría las suscripciones Web Push existentes. En producción están fijadas como variables de entorno.
Recursos acotados — hardening de julio 2026
A diferencia de las primeras versiones, el servidor ahora limita explícitamente el uso de recursos:
- Rate limiting por socket — límites diferenciados por tipo de acción:
action(roll/stand/discard) 15/5s,room(crear/unir/salir) 5/min,social(retos/búsqueda) 10/min,buy(compras) 5/min,read(consultas) 30/10s. - Límite de salas por usuario — máximo
MAX_ROOMS_PER_USER = 2salas activas como host simultáneas. - Limpieza automática de salas — barrido cada 5 minutos que elimina salas
finishedo inactivas; además, cada fase tiene un timeout de seguridad de 2h como red de protección ante partidas atascadas. - CORS restringido —
ALLOWED_ORIGINreemplaza elorigin: '*'original tanto en Express como en Socket.io.
API REST de administración — /api/admin/*
Endpoints protegidos con middleware requireAdmin (token de admin persistido en SQLite), usados exclusivamente por el backoffice: items, tournaments, settings, app-versions, feedback, users y upload (subida de imágenes). El evento de socket público get_settings expone maxPlayersLimit y featureFlags sin autenticación, para que el cliente del juego los consuma en tiempo real.
Actualización forzosa por versión mínima
El backoffice publica versiones de la app Android (app-versions) marcando cuál es la versión mínima soportada. El cliente compara su versionCode local contra ese mínimo al arrancar y bloquea el acceso si está desactualizado, mostrando un aviso de actualización obligatoria.
Frontend — Componentes y casos de uso
App.jsx — Controlador raíz
Gestiona la pantalla activa, el estado de usuario, la música (lobby + partida), el service worker, el modo invitado y la comprobación de versión mínima al arrancar.
- Modo invitado:
loadGuestName()genera un nombre tipo el_pirri_042 la primera vez (pool de nombres galegos/españoles) y lo persiste en localStorage. - Login Google:
handleLoginreemplaza el nombre invitado por el nombre real y registra al usuario en el backend. En Android usa Google Sign-In nativo con verificación de token en servidor; en web,@react-oauth/google. - Singleton de socket importado de
socket.js— una sola conexión para toda la sesión. utils/admob.jsyutils/push.jsencapsulan la inicialización de AdMob (banners + UMP consent) y el registro de token FCM/Web Push respectivamente.
RoomList.jsx — Pantalla principal
Visible sin login. Muestra:
- Sin login: botón "Iniciar sesión con Google" (encima) + tarjeta blanca de invitado con nombre autogenerado.
- Con login: tarjeta de usuario (foto, nombre, email, saldo de Bules, ajustes).
- Salas privadas: icono de candado junto al nombre. Al hacer click en "Unirse", aparece una modal para introducir el código de 4 caracteres.
- Filtro de estado online y estrella de favoritos, con invitación directa a favoritos desde el listado.
GameBoard.jsx — Pantalla de juego
Componente principal durante la partida (~1.050 líneas). Incluye:
- Anti-doble click: flag
rollingdeshabilita el botón "Tirar dados" desde el click hasta que el servidor confirma (socket.emit('roll', …, cb)). También bloquea shake-to-roll. pendingDiscards[],sceneValues,rollingIndices,rollSeed— datos para la escena 3D.- Animación cambio de jugador, palillo roto, scoreboard con dados ordenados por valor, feedback visual de estado de conexión (tras el fix del 30 de julio).
- Mensajería POC: reacciones rápidas y burbujas de chat, visibles para todos los jugadores de la sala.
- Constante
IS_MOBILEcalculada una sola vez a nivel de módulo (no en cada render).
turnDeadline) usa Date.now() cliente vs timestamp servidor. Desincronización de reloj puede mostrar contador inexacto.
Marketplace.jsx — Tienda de Bules e items
Catálogo de items (skins de dados, items de bar, paquete de Bules), inventario del usuario, historial de compras unificado con el historial de partidas. Al comprar un skin de dados, se aplica de inmediato en DiceRollerScene.jsx para ese jugador.
TournamentList.jsx / TournamentLobby.jsx — Torneos
Listado de torneos activos con rango de fechas; si un torneo requiere un item concreto, se muestra su nombre e imagen y el botón de inscripción queda deshabilitado hasta que el usuario lo posea.
ChallengeInvite.jsx / UserSection.jsx / UserDetailSheet.jsx — Social
Reto directo 1v1 a otro jugador (con notificación push si está offline), ficha de perfil con estadísticas e items, sección de usuario con historial de actividad unificado (partidas + compras) e indicador de estado online.
DiceRollerScene.jsx — Escena 3D
El componente más complejo. Gestiona el ciclo completo: sonidos → física → animación → callback.
- Cell-shading:
MeshToonMaterial+ outline pass con geometría escalada al 108%. - FXAA:
EffectComposer+ShaderPass(FXAAShader)en lugar deantialiasnativo. Resuelve el efecto pixelado del borde negro del outline. - Snap cuaternión: al agruparse, los dados rotan a la cara correcta arriba vía
slerpQuaternions(fq, tq, t). - Zoom adaptativo al descartar: con 4 dados en zona de descarte (x ±2.67) la cámara se mueve a (0, 7.25, 5.65); con 5 (x ±3.56) a (0, 9.0, 7.0). Vuelve al zoom cerrado si los descartes bajan de 4.
ctx.pendingRoll. Si el componente se desmontó mientras cargaba, la tirada se pierde silenciosamente.
Sistema de audio
| Archivo | Trigger | Implementación |
|---|---|---|
dado1/2/3.mp3 | Rebote físico dado | Rotación cíclica, volumen proporcional al impacto |
cubilete.mp3 | Antes de lanzar dados | La física espera al evento ended |
eliminar_dados.mp3 | Fade-out de dados | Duración del mp3 = duración del tween α→0 |
cogerdado.mp3 | Marcar dado para descartar | Click táctil/ratón + bot picking |
romper_palillo.mp3 | Fin de ronda con perdedor | Simultáneo con AnimacionPalilloRoto |
button_press.mp3 | Cualquier botón | Listener global en document |
bule-escaleira.mp3 | Pantalla de inicio | Loop, pausa al entrar en partida |
dice-lemonlight.mp3 | Durante la partida | Loop vol 0.2, para al volver al lobby |
La mayoría de objetos Audio se instancian una sola vez a nivel de módulo y se reutilizan entre reproducciones (perf, junio 2026), en lugar de crear un new Audio() en cada evento.
AdBanner.jsx — Publicidad AdMob
Banner de @capacitor-community/admob con formulario de consentimiento UMP integrado (obligatorio en la UE). Usa IDs de test de Google mientras la app no esté publicada en producción en Play Store; el App ID real está pendiente de vincular hasta ese momento.
Roadmap y mejoras futuras
Ya completado desde la primera versión del roadmap
Estas eran "deuda técnica prioritaria" o "features pendientes" en la versión anterior de este documento y ya están implementadas — verificado directamente en el código:
- ✓ hecho Rate limiting por socket, con límites diferenciados por tipo de evento.
- ✓ hecho CORS restringido vía
ALLOWED_ORIGINen Express y Socket.io. - ✓ hecho Limpieza automática de salas inactivas/finalizadas cada 5 minutos.
- ✓ hecho Límite de salas activas por usuario (
MAX_ROOMS_PER_USER). - ✓ hecho Índice invertido
socketId → userIdpara lookups O(1). - ✓ hecho Persistencia de usuarios/economía en SQLite — sobrevive a reinicios de Railway.
- ✓ hecho Reutilización de objetos
AudioyIS_MOBILEextraído del render. - ✓ hecho CI/CD automático — GitHub Actions (frontend) + integración Railway (backend), deploy al hacer push a
main. - ✓ hecho App nativa Android (Capacitor), con Google Sign-In y push nativos.
- ✓ hecho Chat en sala — POC de mensajería con reacciones y burbujas.
- ✓ hecho Ranking global / favoritos y sistema de torneos (con acceso condicionado por item).
Deuda técnica prioritaria — pendiente hoy
- Backups automáticos de SQLite — riesgo real y activo: el fichero único en el volumen de Railway no tiene copia de seguridad. Máxima prioridad antes de cualquier otra cosa.
- Lanzamiento a producción en Play Store — prerrequisito para vincular el App ID real de AdMob y empezar a monetizar con publicidad. Vídeo, capturas, mockups y feature graphic ya listos; falta subir la ficha/build.
- Validación serverside del código de sala privada — el código sigue viajando en el listado público (
sanitizeForListincluyecode); la "privacidad" es solo visual (candado + modal), no una protección real. - Validación serverside de caras —
report_facessigue sin verificar coherencia con la semilla generada en servidor. - Modularizar
server.js(2.241 líneas) en módulos por dominio (juego, marketplace, torneos, admin). - Dividir
GameBoard.jsxyRoomList.jsx(~1.050–1.060 líneas cada uno) en subcomponentes. - Limpieza de
registeredUsers{}inactivos en memoria — hoy crece sin cota con el histórico de usuarios. - Notificación de desconexión — informar a la sala cuando un jugador se desconecta, más allá del feedback visual ya añadido en julio.
Features pendientes
🎮 Juego
- Espectadores (modo solo lectura)
- Revancha abierta a todos (no solo host)
- Variantes de reglas configurables
- Historial de manos de la partida
- Estadísticas de jugador ampliadas (W/L, manos)
- Consumir el primer feature flag real desde el backoffice
👤 Social
- Torneos en formato bracket (eliminación directa)
- Compartir resultado de partida
- Avatares personalizados
- Más paquetes de Bules vía Stripe (además del actual por Bizum)
🏗 Infraestructura
- Backups automáticos de SQLite (urgente)
- Monitoring y alertas (Sentry)
- Tests unitarios e integración
- Logs estructurados
- Reglas de negocio por país/segmento sobre los feature flags ya existentes
📱 Cliente
- Publicación en producción en Play Store
- App iOS (actualmente solo Android)
- App ID real de AdMob (pendiente de publicación)
- Haptic feedback en móvil
- Accesibilidad (ARIA, alto contraste)
- i18n (castellano / galego / inglés)
Escalabilidad — plan por tramos de usuarios
Análisis vigente a fecha de este documento. Arquitectura actual: SQLite de fichero único sin réplica, estado de partidas en memoria (no distribuible entre instancias), Socket.io sin adapter de Redis (un solo proceso Node), sin caché de rankings, servidor monolítico. Capacidad estimada sin cambios: ~500–2.000 usuarios concurrentes cómodamente.
| Umbral | Qué falla primero | Cambio necesario |
|---|---|---|
| ~1.000 usuarios | Rankings recalculados en cada consulta; riesgo de pérdida de datos sin backup | Caché de rankings + backups automáticos del volumen SQLite (urgente, no depende del nº de usuarios) |
| ~5.000 usuarios | SQLite de fichero único no soporta la concurrencia de escritura | Migrar a PostgreSQL |
| ~20.000 usuarios | Un solo proceso Node no reparte la carga de Socket.io | Redis adapter para Socket.io + múltiples instancias |
| ~100.000 usuarios | Servidor monolítico, sin CDN para assets 3D | Microservicios + CDN dedicado |
Propuesta de arquitectura escalable
┌─────────────────────┐
│ Firebase Hosting │ (CDN estática, igual)
└──────────┬──────────┘
│ HTTPS / WSS
┌──────────▼──────────┐
│ Load Balancer │ (Railway / Fly.io)
└──┬──────────────┬──┘
┌────────▼───┐ ┌─────▼──────┐
│ Node.js 1 │ │ Node.js 2 │
└────────┬───┘ └─────┬──────┘
└──────┬───────┘
┌─────────▼──────────┐
│ Redis │ (estado rooms + Socket.io adapter)
└─────────┬──────────┘
┌─────────▼──────────┐
│ PostgreSQL │ (users, stats, historial, reemplaza SQLite)
└────────────────────┘
Monetización — opciones y viabilidad
Inicialmente se planteó que los jugadores apostaran entre sí un importe real por partida. A continuación se analizan todas las opciones técnicas y legales evaluadas en su momento, ordenadas de menos a más viable para un MVP sin burocracia.
Opciones no viables ●
Bizum como pasarela de cobro (API)
Bizum no dispone de API pública para terceros. Funciona exclusivamente dentro de las apps bancarias españolas. No existe integración técnica posible desde una web o app propia.
Stripe / PayPal para apuestas de dinero real entre jugadores
En España, cualquier plataforma que intermedie en apuestas de dinero real está sujeta a la Ley 13/2011 de Regulación del Juego y requiere licencia de la DGOJ (Dirección General de Ordenación del Juego). Stripe y PayPal prohíben explícitamente el juego de azar no licenciado en sus Terms of Service y cierran la cuenta al detectarlo.
- Coste de una licencia DGOJ: proceso largo, depósito de garantía de decenas de miles de euros, auditorías técnicas.
- Sanción por operar sin licencia: hasta 50 millones de euros (Ley 13/2011, art. 41).
Cobrar un porcentaje de dinero real (rake) sin licencia PSP
Actuar como intermediario de pagos (recibir dinero de jugadores y redistribuirlo menos una comisión) requiere una licencia de Entidad de Pago o trabajar con un PSP licenciado. Sin ella, la actividad es ilegal independientemente del método de pago.
Opciones dudosas ●
Stripe con moneda virtual no canjeable
Los jugadores compran "fichas" con Stripe (venta normal de producto digital). Las fichas se usan como apuesta dentro del juego. El ganador acumula fichas. Si las fichas no son canjeables por dinero real (ni directa ni indirectamente), la actividad queda fuera del ámbito de la DGOJ según la interpretación mayoritaria.
Por qué se descartó (de momento): requiere abrirse cuenta en Stripe e implementar webhooks. Se optó primero por un canal de pago más simple (Bizum, sin integración técnica ni comisión de pasarela) para validar la demanda de compra de Bules antes de invertir en la integración con Stripe. Es el paso siguiente natural una vez validado el modelo.
Publicidad — AdMob
El SDK @capacitor-community/admob ya está integrado en la app Android con banners y formulario de consentimiento UMP (obligatorio en la UE). Actualmente funciona con IDs de test de Google.
Por qué está pendiente: vincular el App ID real de producción en AdMob requiere que la app esté publicada en producción en Play Console — no es suficiente con una pista de prueba cerrada o interna. Es decir, AdMob real depende del lanzamiento a producción en Play Store (ver sección 9).
Modelo implementado ●
Bules — moneda virtual + marketplace + torneos con acceso por item
Los jugadores ganan Bules jugando partidas, o compran un paquete de 1.000 Bules por 1 € vía Bizum (pago manual, sin pasarela integrada — el saldo se acredita tras confirmación del admin desde el backoffice). Los Bules se gastan en el Marketplace: skins de dados (mármol azul/negro/rojo/verde, dados rojos transparentes) e items cosméticos de bar (cubata, churros, balón…). Algunos items funcionan además como llave de entrada a torneos exclusivos — por ejemplo, el torneo "Bar Doce" solo es accesible poseyendo el item Churros.
- ✓ Economía cerrada: los Bules no se canjean de vuelta a dinero real → no requiere licencia DGOJ, no requiere PSP licenciado.
- ✓ Ya implementado y en uso: rename puntos→Bules, marketplace, skins, items de bar, torneos con acceso condicionado, todo gestionable desde el backoffice.
- ✓ El paquete de 1.000 Bules por Bizum ya genera el primer ingreso real, aunque sea manual y de bajo volumen.
- ✗ El cobro por Bizum es manual (no escalable) — el siguiente paso es integrar Stripe para pagos automáticos y más paquetes.
- ✗ Todavía no hay AdMob en producción (pendiente de publicación en Play Store) ni suscripción premium.
Siguientes pasos de monetización, en orden:
- Lanzamiento a producción en Play Store → habilita AdMob real (banners con ingresos reales por impresión/click).
- Más paquetes de Bules vía Stripe (pagos automáticos, sin intervención manual del admin).
- Explorar suscripción premium o cuota B2B a bares como se planteó originalmente (ver secciones 8–9), ahora como complemento a la economía de Bules ya validada.
Resumen comparativo
| Opción | Registro necesario | Licencia DGOJ | Implementación | Viabilidad |
|---|---|---|---|---|
| Bizum API | — | — | Imposible | No viable |
| Stripe / apuestas reales | Sí (Stripe + empresa) | Sí (DGOJ) | Compleja | No viable |
| Rake sobre dinero real | Sí (PSP o licencia) | Sí | Muy compleja | No viable |
| Moneda virtual vía Stripe (paquetes adicionales) | Sí (Stripe) | Probablemente no | Media | Pendiente — siguiente paso |
| AdMob en producción | Sí (Play Console) | No | Baja (ya integrado) | Pendiente publicación |
| Bules + Marketplace + torneos por item | No (Bizum manual) | No | Ya implementado | ✓ En producción |
Costes de desarrollo — Claude vs. equipo humano
Todo el proyecto Bule Bule se ha construido usando Claude Code como herramienta principal de desarrollo. A continuación se analiza el coste real en tokens, el coste equivalente con un equipo humano, y la estimación de escalar el proyecto en ambos escenarios.
Coste real con Claude — uso de tokens
Estimación basada en el análisis de los ficheros de sesión (~40 MB de historial JSONL). Los costes varían según la tasa de cache hit; se asume un 65 % de tokens en caché (Anthropic prompt caching, TTL 5 min). Modelo: Claude Sonnet 4.6 · Tarifa: $3/MTok input · $0.30/MTok cached · $15/MTok output.
Estimación proporcional al crecimiento del proyecto (×3,6 commits) desde la cifra de referencia calculada el 26 de junio de 2026 (~5,5M tokens de entrada, ~750k de salida, ≈$23). No se ha vuelto a analizar el historial completo de sesiones para este documento.
Coste equivalente sin Claude — equipo de desarrollo humano
Estimación para un desarrollador senior full-stack en España (€60–70/h) construyendo el mismo producto desde cero.
| Módulo | Horas estimadas | Coste (€65/h) | Notas |
|---|---|---|---|
| Arquitectura + setup inicial | 16 h | €1,040 | Socket.io, Firebase, estructura de proyecto |
| Backend — lógica de juego | 90 h | €5,850 | 6 fases, liberado, capilla, privadas, bots |
| Sistema 3D — Three.js + Rapier3D | 120 h | €7,800 | Física, texturas, toon shader, cámara adaptativa, 10+ animaciones |
| Frontend React + UI/CSS | 70 h | €4,550 | Modo invitado, OAuth, salas privadas, scoreboard |
| Auth, deployment, CI | 20 h | €1,300 | Google OAuth, Firebase Hosting, pipeline |
| Testing + debugging | 60 h | €3,900 | Multijugador cross-device, edge cases del juego |
| Documentación técnica | 24 h | €1,560 | HTML + PDF con histórico, arquitectura, roadmap |
| Subtotal MVP (26 jun 2026) | 400 h | €26,000 | Estado del documento anterior |
| Marketplace + economía de Bules | 60 h | €3,900 | Rename, catálogo, compras, paquete Bizum, inventario |
| Skins de dados + items de bar | 30 h | €1,950 | Texturas de mármol, dados transparentes, action sheet |
| Sistema de torneos + acceso por item | 40 h | €2,600 | Listado, lobby, inscripción, validación server-side |
| Backoffice de administración | 100 h | €6,500 | App React separada, CRUD items/torneos/usuarios/versiones/ajustes, auth de admin |
| App nativa Android + AdMob + push nativo | 90 h | €5,850 | Capacitor, Google Sign-In nativo, UMP consent, FCM |
| Retos, favoritos, historial, estado online | 50 h | €3,250 | 1v1, push offline, timeline unificado |
| Mensajería en partida (POC) | 20 h | €1,300 | Reacciones y burbujas de chat en tiempo real |
| Hardening de seguridad del backend | 25 h | €1,625 | Rate limiting, limpieza de salas, CORS, índice invertido |
| Migración a persistencia SQLite | 30 h | €1,950 | Usuarios, items, torneos, feedback, ajustes |
| Testing + debugging adicional | 45 h | €2,925 | Multiplataforma (web + Android), edge cases de economía |
| Actualización de documentación técnica | 8 h | €520 | Esta regeneración (v0.5) |
| TOTAL a fecha de este documento | ~898 h | ≈€58,370 | Sin overhead ni herramientas |
≈€60 en tokens de Claude producen el equivalente a ≈€58.400 en tiempo de desarrollo senior.
El mismo proyecto habría requerido 5–7 meses de trabajo de un desarrollador senior (o 3–4 meses con un equipo de 2). Con Claude Code se completó en 18 días naturales de desarrollo activo, repartidos en ~8 semanas de calendario.
Escenarios de escalado
¿Qué coste implicaría llevar Bule Bule a una producción robusta y escalable a partir de aquí?
✓ Escenario A — Continuar con Claude
- Fase 1 — MVP mejorado — ✓ completado: marketplace, torneos, retos, historial.
- Fase 2 — Backend robusto — ✓ mayormente completado: SQLite, rate limiting, CORS, limpieza de salas. Pendiente: backups automáticos (urgente) y tests formales → $20–40 en tokens
- Fase 3 — Escalado real (2 meses, sin empezar): PostgreSQL, Redis + Socket.io adapter, CDN para assets 3D, monitoring → $150–300 en tokens
- Hosting en producción: €15–50/mes (100 partidas simultáneas), €80–200/mes (1.000 partidas)
Coste restante hasta escalado real: $170–340 en API + €200–600 en hosting/año
✗ Escenario B — Equipo de desarrollo tradicional
- Fase 1 — MVP mejorado (2 semanas, 1 senior): €8,000–12,000
- Fase 2 — Backend robusto (1 mes, 1 senior): €12,000–16,000
- Fase 3 — Escalado real (2 meses, 2 personas): €28,000–40,000
- Mantenimiento ongoing: €4,000–8,000/mes (1 desarrollador part-time)
- Hosting en producción: mismo coste de infraestructura que el escenario A
Coste total para llegar a producción robusta: €48,000–68,000 + €48,000–96,000/año en mantenimiento
| Concepto | Con Claude | Sin Claude | Ratio |
|---|---|---|---|
| Desarrollo hasta hoy (estado actual) | ≈ $65 / 18 días activos | ≈ €58,400 / 5–7 meses | ~900× |
| Hasta escalado real (Fase 3 restante) | $170–340 adicionales | €28,000–40,000 | ~100–200× |
| Mantenimiento / iteración anual | $500–1,500/año | €50,000–100,000/año | 50–100× |
| Velocidad de iteración (feature nueva) | 1–4 horas | 2–5 días | 10–30× |
| Barrera de entrada | Ninguna | Contratación, onboarding, gestión | — |
Análisis de mercado — A Coruña
Escenario de comercialización: ciudad de A Coruña como mercado inicial. Usuario objetivo: personas que juegan a los dados (cubilete / bule bule) habitualmente en bares y podrían trasladar esa actividad (total o parcialmente) a formato online.
Datos de partida
Tamaño del mercado (TAM / SAM / SOM)
Embudo desde la población adulta hasta usuarios activos mensuales (MAU) realistas en el primer año.
| Nivel | Definición | Estimación A Coruña | Criterio de filtro |
|---|---|---|---|
| TAM — Mercado total | Adultos que conocen y juegan dados/cubilete en bares | 8.000–12.000 personas | ~20% de los 50.000 adultos que van a bares semanalmente · "bule bule" es juego regional muy conocido en Galicia |
| SAM — Mercado disponible | De los anteriores, con smartphone y disposición a probar una app de dados | 2.400–4.800 personas | 30–40% del TAM · filtra personas sin interés en apps o sin smartphone frecuente |
| SOM (orgánico) — Año 1 sin marketing | Captación boca a boca, compartir en grupos de WhatsApp | 120–240 MAU | 3–5% del SAM · crecimiento natural en redes sociales cercanas |
| SOM (activo) — Año 1 con marketing local | Campaña en redes + acuerdos con bares + prensa local | 400–800 MAU | 10–15% del SAM · bajo presupuesto de €1.000–2.000 en difusión |
Modelo de recurrencia
El juego presencial en bar y la app online no son sustitutos perfectos — son complementarios. La app cubre los momentos en que el grupo no puede juntarse físicamente.
Cuándo juegan online
- Grupo disperso por trabajo / viaje
- Sobremesa remota (videollamada + partida)
- Torneo organizado con hora fija
- Esperas largas (transporte, guardia, etc.)
- Sustitución del bar en días de lluvia / invierno
Frecuencia estimada por perfil
- Usuario activo (20% base): 3–5 sesiones/semana
- Usuario medio (40% base): 1–2 sesiones/semana
- Usuario casual (40% base): 2–4 sesiones/mes
- Promedio ponderado: ~2,2 sesiones/semana
- Duración media de partida: 15–25 minutos
Riesgo principal: efecto red
- El juego requiere otros usuarios activos simultáneos
- Con <100 MAU, encontrar partida puede ser difícil
- Mitigación ya implementada: bot player
- Solución adicional: torneos programados con hora fija
- Sin masa crítica inicial, el producto no engancha
Escenarios de ingresos
| Modelo | MAU base | Conversión | Ingreso/mes | Coste infra/mes | Resultado |
|---|---|---|---|---|---|
| Sistema de honor (actual) | 300 | 0 % | €0 | €5–10 | Proyecto gratuito |
| Donaciones voluntarias (ko-fi) | 300 | 1–2 % | €15–45 | €5–10 | Simbólico |
| Suscripción premium (€2,99/mes) | 500 | 8–12 % | €120–180 | €10–20 | Cubre costes |
| B2B — bares (€20/mes por bar) | — | 30–60 bares | €600–1.200 | €20–40 | Viable A Coruña |
| Expansión Galicia (B2C + B2B) | 5.000–15.000 | 5 % suscripción + 200 bares | €4.750–6.250 | €80–200 | Negocio viable |
Viabilidad — diagnóstico
Hoja de ruta para la viabilidad
Paso 1 — Masa crítica (gratis)
Llegar a 300–500 usuarios activos en A Coruña mediante grupos de WhatsApp, Instagram local, y menciones en bares. Sin coste. Objetivo: demostrar tracción antes de monetizar.
Paso 2 — Torneos semanales
Programar torneos con hora fija (viernes 21h, por ejemplo). Esto resuelve el problema de masa crítica, crea hábito de uso y genera contenido compartible. Implementación: ~1 día con Claude.
Paso 3 — Canal B2B bares
Visita presencial a 10–15 bares con propuesta: QR en mesa, personalización del nombre del bar, estadísticas de uso. €20/mes. Con 30 bares = €600/mes. ROI positivo desde el primer mes.
Paso 4 — Expansión Galicia
El juego es galego-español: el salto a Vigo, Santiago, Ferrol, Lugo es natural. Misma app, misma cultura. Con 5× usuarios y 200 bares, el negocio se vuelve un negocio real de €4.000–6.000/mes.
Proyección financiera — cuatro escenarios
Todos los escenarios asumen el modelo mixto: B2B bares + suscripción de usuario (el único con rentabilidad real a escala A Coruña / Galicia). Los costes de desarrollo son los de Claude (ver Sección 7) — se asume que no hay equipo asalariado. Se incluye el coste de oportunidad del fundador como referencia. Horizonte: 3 años.
Supuestos comunes: tarifa bares €20-25/mes · suscripción usuario €2,99/mes · tasa conversión suscripción 6-10% MAU · hosting escala con usuarios · marketing = único gasto variable significativo.
Sin acción comercial ni solución al efecto red
El mayor riesgo de cualquier juego multijugador: los primeros usuarios llegan, no encuentran partida y se van. Sin masa crítica inicial, el producto entra en espiral de abandono.
Cómo evitarlo: torneos programados con hora fija (resuelve el problema de concurrencia), captación activa del primer grupo de 50 usuarios habituales, bots ya implementados como fallback.
Sin equipo · sin inversión en marketing · solo A Coruña · suscripción básica
El fundador dedica ~3-4h semanales. Sin presupuesto de marketing. Crecimiento orgánico por WhatsApp y boca a boca. Se lanza suscripción a los 6 meses. No se busca B2B activo.
| Año 1 | Año 2 | Año 3 | |
|---|---|---|---|
| MAU medio | 150 | 280 | 400 |
| Bares B2B | 5 | 12 | 18 |
| Ingresos suscripción | €270 (6% × 150 × €2.99 × 6 meses) | €600 | €860 |
| Ingresos B2B bares | €600 (5 bares × 6 meses) | €2.880 | €4.320 |
| Ingresos totales | €870 | €3.480 | €5.180 |
| Costes (hosting + Claude) | €220 | €480 | €720 |
| Beneficio neto | €650 | €3.000 | €4.460 |
| Retribución fundador (€60/h · 3h/sem) | €9.360 coste oportunidad | €9.360 | €9.360 |
Veredicto: cubre costes técnicos ampliamente pero no justifica el tiempo invertido como negocio. Válido como proyecto lateral que genera ingresos residuales con muy poco esfuerzo de mantenimiento.
Fundador dedicado · expansión Galicia en Año 2 · canal B2B activo · marketing €1.000-3.000/año
Se invierte tiempo real en ventas B2B (visitas a bares), redes sociales, y torneos semanales para construir masa crítica. En Año 2 se expande a Vigo, Santiago y Ferrol. En Año 3, norte de España.
| Año 1 (A Coruña) | Año 2 (Galicia) | Año 3 (Norte España) | |
|---|---|---|---|
| MAU medio | 500 | 2.200 | 6.000 |
| Bares B2B | 30 | 100 | 260 |
| Ingresos suscripción | €1.076 (7% × 500 × €2.99 × 10 meses) | €7.907 | €21.528 |
| Ingresos B2B bares | €5.400 (30 × €20 × 9 meses) | €24.000 | €74.880 |
| Ingresos totales | €6.476 | €31.907 | €96.408 |
| Costes (hosting + Claude + marketing) | €1.800 | €5.200 | €12.000 |
| Beneficio neto | €4.676 | €26.707 | €84.408 |
| ¿Justifica dedicación completa? (umbral €30k/año) | No (proyecto lateral) | Sí, desde mitad de año | Sí, con margen |
Punto de inflexión: ~80 bares activos + 1.500 MAU → ingresos recurrentes de €2.000+/mes → negocio sostenible. Alcanzable en 12-18 meses con dedicación real al canal B2B.
Cobertura mediática · viral en redes · expansión España + LATAM · torneos con patrocinadores
El producto aparece en prensa especializada o viral en TikTok/Instagram. Se expande al mercado hispanohablante donde el cubilete / dados es cultura popular (México, Argentina, Venezuela, Cuba…). Se añaden patrocinios de marcas de bebidas.
| Año 1 | Año 2 | Año 3 | |
|---|---|---|---|
| MAU medio | 3.500 | 22.000 | 80.000 |
| Bares / locales B2B | 120 | 600 | 2.000 |
| Ingresos suscripción | €12.558 | €79.074 | €287.520 |
| Ingresos B2B locales | €21.600 | €162.000 | €600.000 |
| Torneos y patrocinios | €0 | €15.000 | €80.000 |
| Ingresos totales | €34.158 | €256.074 | €967.520 |
| Costes (infra + marketing + equipo mínimo) | €12.000 | €65.000 | €220.000 |
| Beneficio neto | €22.158 | €191.074 | €747.520 |
Probabilidad: baja pero no despreciable — el cubilete tiene cultura profunda en todo el mundo hispanohablante (500M+ personas). LATAM no requiere ningún cambio en el producto. El riesgo principal es la competencia de apps ya existentes y la necesidad de un equipo real a partir del Año 2.
Resumen comparativo — Beneficio neto a 3 años
| Escenario | Año 1 | Año 2 | Año 3 acumulado | Condición clave |
|---|---|---|---|---|
| Producto muere | −€80 | −€80 | −€240 | Sin acción. Efecto red no resuelto. |
| Mínimo | €650 | €3.000 | €8.110 | Solo A Coruña, 3h/sem, orgánico. |
| Realista ← recomendado | €4.676 | €26.707 | €107.791 | B2B activo + expansión Galicia en A2. |
| Máximo | €22.158 | €191.074 | €960.752 | Viral + LATAM + equipo + patrocinios. |
Invertir ~15h/semana en captación B2B de bares + mantenimiento de producto con Claude. Sin equipo. Sin inversión externa. Sin licencias ni burocracia.
Inversión total en tecnología hasta ese punto: <$200 en tokens de Claude + ~€600 en hosting.
Plan de acción — equipo de 2 personas
Escenario elegido: B2B bares + suscripción de usuario · expansión Galicia en año 2. Es el camino de menor riesgo, menor inversión y mayor probabilidad de generar ingresos reales en un plazo razonable. No requiere licencias, inversión externa ni equipo adicional.
Roles y responsabilidades
● Persona 1 — UX / Fullstack
- Desarrollo de producto con Claude (features, bugs, mejoras)
- Diseño de UX/UI: demos visuales, materiales para pitch a bares
- Infraestructura y despliegue (Firebase, servidor)
- Dashboard de analytics y métricas de negocio
- Soporte técnico a bares (onboarding, incidencias)
- Dedicación estimada: 8–12 h/semana (fase 1) → full-time (fase 3)
● Persona 2 — Inversor / Comercial
- Prospección y visitas a bares: demo en móvil + propuesta de valor
- Gestión de contratos B2B y cobro mensual
- Comunidad de usuarios: WhatsApp, Instagram, TikTok
- Organización de torneos semanales presenciales/online
- Relaciones con prensa local (La Voz de Galicia, medios hostelería)
- Aportación de capital inicial: €2.000–3.000 (ver inversión)
- Dedicación estimada: 10–15 h/semana (fase 1) → full-time (fase 2)
Hitos en el tiempo
Preparación
Validación
Tracción
Galicia
Norte España
Fases detalladas
Fase 0 — Preparación del producto comercial
Objetivo: tener un producto demostrable y un pitch listo para el primer bar.
- Dev Pantalla de liquidación (€ por palillo) — superado: en su lugar se construyó la economía de Bules + marketplace, un modelo distinto pero que cumple el mismo objetivo de monetización sin tocar dinero real entre jugadores.
- Dev Analytics básico —
frontend/src/analytics.jsya trackea eventos clave (creación de sala, compras, etc.). - Dev Landing page atractiva con captura de email y demo en vivo embebida — pendiente
- Dev Backups automáticos de SQLite — pendiente, prioridad técnica real número uno
- Dev Publicar ficha y build en producción en Play Console — pendiente; assets (vídeo, capturas, feature graphic, mockups) ya están listos
- Comercial Identificar los 20 bares target en A Coruña (zona Juan Flórez, Orzán, zona vella) — pendiente
- Comercial Crear pitch de 1 página: propuesta de valor, precio, qué gana el bar — pendiente
- Ambos Definir precio B2B (propuesta: 30 días gratis → €20/mes sin permanencia) — pendiente
- Comercial Crear perfil de Instagram y grupo de WhatsApp "Bule Bule A Coruña" — pendiente
Fase 1 — Validación A Coruña
Objetivo: demostrar que el modelo B2B funciona y que los usuarios vuelven.
- Comercial 5 visitas/semana a bares con demo en móvil — apuntar objeciones y mejorar pitch
- Dev Página personalizada por bar: logo, nombre, QR imprimible para la mesa — ~4h con Claude
- Comercial Torneos de jueves o viernes a las 21h — anunciados en WhatsApp/Instagram con 48h de antelación
- Dev Notificación push "Tu torneo empieza en 15 min" — ya implementado tecnicamente, solo activar
- Comercial Recoger feedback semanal: ¿qué rompe la experiencia? ¿qué falta? — pasar al Dev
- Dev 1–2 mejoras de producto por semana según feedback (velocidad clave en esta fase)
- Ambos Revisar métricas cada lunes: MAU, partidas/día, churn de bares, MRR
Fase 2 — Expansión Galicia
Objetivo: replicar el modelo en Vigo, Santiago, Ferrol, Lugo y Ourense.
- Comercial Expandir a las 5 ciudades gallegas principales — misma dinámica de visitas presenciales
- Dev Lanzar suscripción de usuario premium (€2,99/mes): historial de partidas, estadísticas, perfil
- Dev Función "Invitar amigos" con link directo a partida — palanca de crecimiento orgánico clave
- Dev Dashboard para el dueño del bar: partidas jugadas este mes, usuarios activos, horarios pico
- Comercial Enviar nota de prensa a La Voz de Galicia, El Correo Gallego y blogs de hostelería
- Comercial Buscar acuerdo con 1–2 distribuidores de hostelería gallegos para canal indirecto
- Ambos Subir el precio a €25/mes para nuevos clientes si el churn es <10%
Fase 3 — Escalado Norte de España
Objetivo: independencia financiera completa para ambas personas.
- Comercial Expandir a Asturias, Cantabria y País Vasco — cultura de bar muy similar a Galicia
- Dev Sistema de torneos con inscripción de pago (€1–2/torneo) — ingresos adicionales sin licencia DGOJ
- Dev App móvil nativa (PWA instalable o React Native) para mejorar retención
- Comercial Negociar patrocinio con 1–2 marcas de bebidas para torneos (Estrella Galicia, Mahou...)
- Ambos Evaluar contratar 1 persona comercial para liberar tiempo al fundador comercial
- Dev Preparar infraestructura para LATAM si el MRR supera €10.000 (sin cambios en el producto)
Métricas clave a seguir
| Métrica | Qué mide | Frecuencia | Umbral de alerta |
|---|---|---|---|
| MRR (Monthly Recurring Revenue) | Ingresos recurrentes mensuales garantizados | Semanal | < €200 al mes 2 → revisar pitch |
| Churn rate bares | % de bares que cancelan al mes | Mensual | > 15% → llamar a todos y pedir feedback |
| MAU | Usuarios activos mensuales — salud del producto | Semanal | Decrecimiento 2 semanas seguidas → investigar |
| Partidas/día | Actividad real — proxy de engagement | Diaria | < 5 partidas/día pasado el mes 1 → problema de masa crítica |
| Conversión premium | % MAU que pagan suscripción | Mensual | < 3% al mes 6 → revisar propuesta de valor premium |
| Tiempo de sesión medio | Engagement por partida — ¿les engancha? | Mensual | < 8 min → el juego no retiene lo suficiente |
Riesgos y mitigaciones
🔴 Efecto red — no hay con quién jugar
Probabilidad: alta si no se actúa. Mitigación: torneos a hora fija (jueves 21h), bot ya implementado como fallback, lanzamiento concentrado en 1 sola zona de la ciudad.
🔴 El comercial no cierra suficientes bares
El cuello de botella más probable. Mitigación: objetivo mínimo de 3 visitas/día, demo en 2 minutos en el propio móvil del dueño, 30 días gratis sin compromiso.
🟡 Bar paga pero sus clientes no juegan
El bar cancela al mes 2. Mitigación: onboarding activo (el comercial enseña el QR al camarero), torneo inaugural en el propio bar la primera semana.
🟡 Competencia de apps similares
No existe competencia directa en bule bule gallego. Riesgo en juego de dados genérico. Ventaja: conocimiento local, relaciones con bares, cultura del juego.
🟢 Costes técnicos se disparan
Riesgo bajo. Firebase escala en precio solo cuando hay volumen (problema bueno). Claude API: <$200/año hasta escala nacional. Hosting: <€50/mes hasta 5.000 MAU.
🟢 Problemas legales de juego
El modelo de honor (app no toca el dinero) está fuera del ámbito de la DGOJ. Los torneos con inscripción pequeña (<€50 bote) también están en zona segura.
Inversión inicial y reparto
Inversión total estimada: €2.000–3.000
| Concepto | Importe |
|---|---|
| Material impreso (QR, roll-ups para bares) | €400–600 |
| Publicidad en redes sociales (Meta Ads) | €500–800 |
| Dominio, certificados, herramientas | €100–150 |
| Hosting primer año (Railway + Firebase) | €120–300 |
| Desplazamientos y demos presenciales | €300–500 |
| Claude API todo el año | ~€180 ($200) |
| Total | €1.600–2.530 |
Estructura sugerida
- Persona 2 (inversor) aporta el 100% del capital inicial (€2.000–3.000) como préstamo participativo o a cambio de equity acordado.
- Persona 1 (dev) aporta el trabajo técnico (producto, infraestructura) valorado en €15.000–20.000 equivalente.
- Reparto de beneficios desde mes 1: 50/50 o ajustado según aportación real (a negociar entre ambos).
- No se necesita constituir sociedad hasta superar €3.000/mes de ingresos — hasta entonces, autónomos o facturación puntual.
- El breakeven de la inversión inicial se alcanza con ~15 bares firmados.
No se necesita inversión externa, licencias ni equipo adicional. El único cuello de botella real es la captación de bares — todo lo demás está resuelto o es barato de resolver con Claude. Breakeven técnico: 15 bares · Breakeven financiero completo (ambas personas): ~80 bares.
Glosario de términos
Definiciones de todos los acrónimos, métricas y términos técnicos usados en este documento.
Usuarios activos en un mes natural. En Bule Bule, un usuario cuenta como activo si completa al menos 1 partida en los últimos 30 días. No basta con abrir la app o registrarse — se requiere engagement real con el juego.
Ej: 500 MAU significa 500 personas distintas que terminaron una partida en el mes.
Usuarios que completan al menos 1 partida en los últimos 7 días. Métrica más sensible que el MAU para detectar caídas de engagement a corto plazo. Un MAU alto con WAU bajo indica usuarios que juegan esporádicamente.
Usuarios activos en un día concreto. En juegos sociales como Bule Bule, el DAU tiene picos los jueves-viernes noche y fines de semana. La ratio DAU/MAU indica la frecuencia media de uso: si es 0.1, cada usuario juega ~3 días al mes.
Ingresos recurrentes garantizados cada mes, procedentes de contratos activos (bares B2B) y suscripciones de usuario. Es la métrica de salud financiera más importante para un negocio de suscripción: predecible, acumulable y difícil de inflar.
Ej: 30 bares × €20/mes + 50 suscripciones × €2.99 = €749,50 MRR.
Porcentaje de clientes o usuarios que dejan de usar el servicio en un período dado. Un churn del 10% mensual en bares significa que 1 de cada 10 bares cancela al mes — insostenible. Por debajo del 5% mensual se considera saludable para un producto en fase inicial.
Ej: 30 bares activos, 3 cancelan ese mes → churn rate = 10%.
Métrica concreta y medible que indica si el negocio avanza hacia sus objetivos. Un buen KPI es específico, temporal y accionable: si cae por debajo del umbral, sabes exactamente qué investigar. No todos los datos son KPIs — elegir 4-6 métricas clave evita la parálisis por análisis.
Beneficio obtenido en relación a lo invertido, expresado como porcentaje o ratio. Un ROI de ×1.200 (como el calculado para el desarrollo con Claude) significa que por cada euro invertido en API se obtiene el equivalente a 1.200 euros de valor en trabajo humano.
Fórmula: (beneficio − inversión) / inversión × 100.
Ingresos totales que genera un cliente durante toda su relación con el producto. Para un bar que paga €20/mes durante 18 meses, el LTV es €360. Para ser rentable, el LTV debe ser al menos 3× el coste de adquirir ese cliente (CAC).
Coste total de conseguir un nuevo cliente: tiempo del comercial, desplazamientos, materiales de marketing. Si visitar 10 bares cuesta 5 horas de trabajo y se consiguen 2 clientes, el CAC es 2.5h × €0 (sin salario aún) + materiales. Cuando se pague salario, el CAC definirá si el canal B2B es rentable.
Porcentaje de usuarios que pasan de un estado a otro: de visita a registro, de registro a partida jugada, de usuario gratuito a suscriptor de pago. En las proyecciones del informe se usa una conversión del 6-10% de MAU a suscripción premium.
Ej: 500 MAU × 8% conversión = 40 suscriptores de pago.
El mercado total al que el producto podría llegar si capturara el 100% de sus posibles usuarios, sin restricciones de distribución o recursos. Es el techo teórico. Para Bule Bule en A Coruña: ~8.000-12.000 personas que juegan dados en bares.
La parte del TAM a la que realmente se puede llegar con el modelo de negocio y canal de distribución actuales. Filtra a quienes no tienen smartphone, no usarían una app o están fuera del alcance geográfico/comercial. Siempre menor que el TAM.
La cuota de mercado realista a capturar a corto plazo, teniendo en cuenta los recursos disponibles, la competencia y el tiempo. Es el número que debe aparecer en los objetivos de negocio reales, no el TAM ni el SAM. Para Bule Bule año 1: 120-800 MAU según el nivel de inversión en marketing.
Modelo de negocio donde se vende a otras empresas o negocios, no al consumidor final. En Bule Bule, el canal B2B es vender a bares (€20-25/mes) la herramienta para que sus clientes jueguen. El bar es el cliente que paga; sus clientes usan la app gratis.
Modelo donde se vende directamente al usuario final. En Bule Bule, el B2C corresponde a la suscripción premium de usuario (€2,99/mes). Requiere mayor volumen de usuarios que el B2B para generar ingresos equivalentes.
Primera versión lanzable de un producto que incluye solo las funcionalidades esenciales para aportar valor real a los primeros usuarios y validar hipótesis de negocio. No es un producto incompleto — es un producto completo para un caso de uso concreto. Bule Bule en su estado actual ya es un MVP funcional.
Modelo de distribución de software en el que el usuario paga una cuota periódica (mensual/anual) para acceder a la aplicación vía internet, sin instalar nada localmente. La suscripción premium de Bule Bule (€2,99/mes) es un modelo SaaS B2C.
Comisión que cobra la plataforma (la "casa") sobre cada apuesta o bote acumulado en el juego. Es el modelo de ingresos estándar de casinos y salas de póker online. En España requiere licencia DGOJ para ser legal. Bule Bule no implementa rake — el dinero nunca pasa por la app.
Fenómeno por el que el valor de un producto crece de forma no lineal con el número de usuarios. En un juego multijugador como Bule Bule, sin usuarios no hay partidas, sin partidas no hay retención, sin retención no hay usuarios nuevos. Es el mayor riesgo en la fase inicial y la mayor ventaja una vez alcanzada la masa crítica.
Número mínimo de usuarios activos simultáneos que hace que el producto sea útil de forma autónoma. Por debajo de ese umbral, los usuarios no encuentran partida y abandonan. Para Bule Bule se estima en ~50-80 MAU concentrados en la misma zona horaria. Se mitiga con torneos programados y bot players.
El valor de la mejor alternativa a la que se renuncia al tomar una decisión. En el contexto de Bule Bule: si el desarrollador dedica 10h/semana al proyecto en lugar de a trabajo remunerado (€60/h), el coste de oportunidad anual es ~€31.200. El proyecto solo es financieramente racional cuando los ingresos generados superan ese umbral.
Momento en que los ingresos igualan a los costes y el negocio deja de perder dinero. Hay dos break-even relevantes en Bule Bule: el técnico (cubrir hosting + herramientas, ~15 bares) y el financiero completo (cubrir el tiempo de ambas personas a jornada completa, ~80 bares).
Mercado hispanohablante de ~450 millones de personas en América Latina. Relevante para Bule Bule porque el cubilete y los juegos de dados tienen arraigo cultural profundo en países como México, Cuba, Venezuela, Argentina y Colombia. No requeriría cambios en el producto para operar allí.
Interfaz que define cómo dos sistemas de software pueden comunicarse entre sí. En Bule Bule: la API de Google permite el login con cuenta de Google, la API de Anthropic permite usar Claude para el desarrollo, y el servidor expone una API interna vía Socket.io para el juego en tiempo real.
Protocolo de comunicación que mantiene una conexión persistente y bidireccional entre el navegador y el servidor. A diferencia del HTTP clásico (el navegador pregunta, el servidor responde y cierra), con WebSockets el servidor puede enviar datos al cliente en cualquier momento. Imprescindible para juegos multijugador en tiempo real.
Librería JavaScript que facilita la comunicación bidireccional en tiempo real, construida sobre WebSockets con fallbacks automáticos. Gestiona reconexiones, salas de jugadores y eventos nombrados. Es la columna vertebral de toda la lógica multijugador de Bule Bule.
Formato binario que permite ejecutar código compilado de lenguajes como C++ o Rust directamente en el navegador, con rendimiento cercano al nativo. En Bule Bule, el motor de física Rapier3D está compilado en WASM, lo que permite simular colisiones y física de dados a 60fps sin necesidad de servidor de física.
Motor de física 3D de alta precisión compilado en WebAssembly. Simula cuerpos rígidos, colisiones, gravedad y fricción en tiempo real. En Bule Bule se usa para que los dados reboten de forma realista sobre el tablero, con física determinista: la misma semilla numérica produce exactamente el mismo resultado en todos los dispositivos.
Librería JavaScript para crear y renderizar escenas 3D en el navegador usando WebGL. Abstrae la complejidad de WebGL (shaders, matrices de transformación, gestión de GPU) en una API de alto nivel. En Bule Bule gestiona los dados, la cámara, las luces, las sombras y todos los efectos visuales.
Plataforma de backend de Google que incluye hosting estático, autenticación (Google, email, anónimo), base de datos en tiempo real, almacenamiento y funciones cloud. Bule Bule usa Firebase Hosting para servir el frontend. La capa gratuita cubre holgadamente las necesidades hasta varios miles de usuarios.
Protocolo estándar que permite a una aplicación verificar la identidad de un usuario a través de un proveedor externo (Google, GitHub, Facebook) sin necesidad de gestionar contraseñas. El usuario autoriza a Bule Bule a leer su nombre y email de Google; Google devuelve un token seguro que la app verifica.
Red de servidores distribuidos geográficamente que sirven el contenido estático (imágenes, JS, CSS, assets 3D) desde el servidor más cercano al usuario, reduciendo la latencia. Firebase Hosting incluye CDN global automático. Relevante para Bule Bule porque los assets 3D (texturas, sonidos) pesan varios MB.
Prácticas de automatización que ejecutan pruebas y despliegan el código automáticamente cada vez que se hace un commit. Eliminan los errores manuales del proceso de release. En Bule Bule actualmente el despliegue es manual (firebase deploy); un pipeline CI/CD automatizaría esto en cada push a main.
Técnica de renderizado 3D que imita el estilo visual de un cómic o dibujo animado: sombras cuantizadas en bandas planas (en lugar de degradado suave) y contornos negros marcados alrededor de los objetos. En Bule Bule le da a los dados ese aspecto visual característico que los distingue de un simulador de física realista.
Técnica de post-procesado para suavizar los bordes dentados ("serrucho") que aparecen en escenas 3D cuando los polígonos se alinean en ángulos oblicuos a los píxeles. FXAA es una versión ligera que opera sobre la imagen final en lugar de renderizar a resolución mayor, lo que la hace adecuada para móviles.
Protocolo de autenticación para el envío de notificaciones push en aplicaciones web. Consiste en un par de claves pública/privada que identifican de forma segura al servidor que envía la notificación, evitando que terceros suplanten la identidad de la app. En Bule Bule se usa para notificar al usuario cuando le invitan a una partida.
Formato de fichero en el que cada línea es un objeto JSON independiente y completo, separado por salto de línea. Ideal para logs, streaming de datos y exportaciones grandes porque se puede leer línea a línea sin cargar el fichero entero en memoria. Las sesiones de Claude Code se almacenan en este formato.
Tiempo máximo que un dato almacenado en caché se considera válido antes de descartarse y recalcularse. En el prompt caching de Anthropic el TTL es de 5 minutos: si el intervalo entre llamadas supera ese tiempo, el contexto no se puede reutilizar y hay que enviarlo de nuevo completo (mayor coste).
Unidad de medida estándar para la facturación de modelos de lenguaje como Claude. Un token equivale aproximadamente a 4 caracteres de texto en inglés o 3 en español. Un mensaje de 1.000 palabras son ~1.400 tokens. El precio de Claude Sonnet 4.6: $3/MTok de entrada, $15/MTok de salida.
Sistema de Anthropic que almacena en caché partes del contexto de una conversación para reutilizarlas en llamadas sucesivas sin reenviarlas al modelo. Reduce el coste de los tokens de entrada en un 90% (de $3 a $0.30/MTok) cuando se produce un hit de caché. Clave para que el desarrollo con Claude Code sea económico.
Aplicación web que puede instalarse en el dispositivo del usuario (como una app nativa) directamente desde el navegador, sin pasar por la App Store o Google Play. Tiene acceso a funciones del dispositivo como notificaciones push, modo offline y pantalla completa. Una posible evolución de Bule Bule en la fase 3 del plan de acción.
Organismo del Ministerio de Consumo de España responsable de regular, supervisar y licenciar los juegos de azar online. Cualquier plataforma que intermedie en apuestas de dinero real entre usuarios necesita su licencia. El proceso es largo, costoso (garantías de decenas de miles de euros) y con auditorías técnicas. Las sanciones por operar sin licencia alcanzan los 50M€ (Ley 13/2011).
Entidad autorizada por el Banco de España (o el BCE) para procesar, liquidar y gestionar pagos entre partes. Ejemplos: Stripe, PayPal, Adyen. Actuar como intermediario de pagos sin ser un PSP autorizado es ilegal en la UE (PSD2). Por eso Bule Bule no puede cobrar ni redistribuir dinero entre jugadores directamente.
Organismo oficial español que publica estadísticas demográficas, económicas y sociales. Los datos de población de A Coruña usados en el análisis de mercado (246.056 habitantes, 2023) proceden del padrón municipal del INE.
En Bule Bule se considera usuario activo quien completa al menos 1 partida en el período de referencia (mes para MAU, semana para WAU). No basta con abrir la app, registrarse o unirse a una sala. Se requiere completar el flujo de juego. Los usuarios invitados (guest) cuentan igual que los registrados si completan una partida.
No cuenta: abrir la app, ver la lista de salas, unirse y salir antes de terminar.
Modelo de monetización en el que la app calcula cuánto debe cada jugador al final de la partida (según el importe por palillo configurado), pero el dinero nunca pasa por la plataforma. Los jugadores se pagan entre sí por Bizum, efectivo u otro medio externo. No requiere licencia DGOJ ni cuenta de pagos.
Estado especial en Bule Bule al que llega un jugador que consigue un Repóker (cinco dados con el mismo valor). El jugador liberado deja de participar en las rondas siguientes hasta que otro jugador pierda la partida. En una partida de 2 jugadores, el liberado gana automáticamente.
Estado en el que un jugador ha perdido todos sus palillos (vidas) y está en la última ronda antes de ser eliminado. Si vuelve a perder la ronda estando en capilla, queda eliminado de la partida. El término es parte del argot tradicional del juego de cubilete español.
Moneda virtual del juego (llamada "puntos" hasta el rename de junio de 2026). Se gana jugando partidas o se compra con dinero real (paquete de 1.000 Bules por 1 € vía Bizum). Se gasta en el Marketplace en items cosméticos y acceso a torneos exclusivos. Es una economía cerrada: los Bules nunca se canjean de vuelta a dinero real, lo que mantiene la actividad fuera del ámbito de la Ley 13/2011 de regulación del juego.
Torneo del que solo puede participar quien posea un item concreto del marketplace, comprado con Bules. El item funciona como "llave de entrada". Ejemplo: el torneo "Bar Doce" solo es accesible poseyendo el item Churros. La validación de que el usuario posee el item se hace en el servidor al recibir join_tournament, no solo en el cliente.
Panel de administración interno, no visible para jugadores, usado para gestionar el contenido y la configuración del juego: items del marketplace, torneos, usuarios, feedback, versiones de app publicadas y ajustes globales (feature flags, límite de jugadores). En Bule Bule es una app React independiente (backend/backoffice/) servida como estático desde el propio backend, protegida por token de administrador.
Interruptor remoto que activa o desactiva una funcionalidad sin necesidad de publicar una nueva versión de la app. En Bule Bule se gestionan desde la sección Ajustes del backoffice y se guardan en la tabla config de SQLite; el evento de socket público get_settings los expone al cliente. A fecha de este documento, el mecanismo existe pero ningún flag real se consume todavía en el juego — está listo para cuando se necesite aplicar reglas distintas por país o segmento de usuarios.
Formulario de consentimiento de publicidad personalizada de Google (User Messaging Platform), obligatorio en la Unión Europea antes de mostrar anuncios de AdMob que usen datos personales. Bule Bule lo integra en AdBanner.jsx; hasta que la app se publique en producción en Play Store, se prueba con IDs de AdMob de test.
No se encontraron términos para esa búsqueda.
Marco legal — Ley 13/2011 de Regulación del Juego
Análisis del coste y viabilidad de operar Bule Bule como juego regulado bajo la normativa española de juego online, gestionada por la DGOJ (Dirección General de Ordenación del Juego).
Clasificación del juego
El póker de dados combina azar (tirada de dados) y estrategia (elección de descartes). Bajo la Ley 13/2011, la componente aleatoria dominante lo clasifica como juego de azar, lo que activa los requisitos más exigentes de licencia.
Costes de entrada — licencia DGOJ
La barrera más significativa es el aval bancario, una garantía que la DGOJ exige al operador para cubrir posibles deudas con jugadores. Es un depósito inmovilizado, no un gasto consumido.
| Concepto | Coste estimado | Tipo |
|---|---|---|
| Aval bancario (garantía DGOJ) | €1.500.000 – €3.000.000 | Capital inmovilizado |
| Tasas DGOJ (tramitación licencia) | €15.000 – €30.000 | Pago único |
| Certificación técnica del software (GLI / BMM) | €50.000 – €150.000 | Pago único + renovación |
| Sistemas AML + KYC obligatorios | €80.000 – €200.000 | Implementación |
| Herramientas de juego responsable | €30.000 – €80.000 | Obligatorio DGOJ |
| Legal y compliance inicial | €100.000 – €200.000 | Asesoría especializada |
| TOTAL inversión inicial | €1,8M – €3,6M | Sin contar el aval |
Costes anuales recurrentes
Coste anual total estimado: €200K – €420K + 20% GGR — antes de contar personal de producto y tecnología.
Alternativas con menor barrera
Moneda virtual, sin premios en metálico. Sin obligación de licencia DGOJ. Coste regulatorio: €0. Es el modelo actual de Bule Bule.
Los bares pagan una cuota mensual por acceso. Los jugadores no apuestan dinero directamente. Sin licencia de juego si no hay premio en metálico entre jugadores. El modelo más viable a corto plazo.
Operar desde Malta con jugadores españoles. Coste de licencia MGA: €25K–50K. Zona gris legal en España desde la Ley 13/2011 — riesgo de bloqueo por la DGOJ.
Si se acredita componente de habilidad dominante, puede reclasificarse como concurso. Regulación más ligera, pero requiere dictamen jurídico. A estudiar con abogado especializado.