Pruébalo en vivo Sobre este proyecto
Playroom es una recreativa de salón que funciona en el navegador. La página se abre sobre un escritorio de juego al estilo de 1989 (un monitor CRT, una consola, un mando y un cartucho de Snake ya insertado) que se puede girar e inspeccionar; al hacer clic en la consola o en POWER ON, la cámara avanza hacia la pantalla, atraviesa el tubo y aterriza en una biblioteca de tres cartuchos.
Los tres juegos son originales de Playroom: Snake va de encadenar bocados con ritmo, Breakout trae tres fases de ladrillos y potenciadores, y Blocks hace que apilar sea predecible con un fantasma de aterrizaje y bolsas de siete piezas. Sin cuenta, sin actualizaciones, sin partidas guardadas: las puntuaciones máximas se quedan en tu navegador, y la música y los efectos se apagan con un solo botón.
Todo el proyecto depende solo de Three.js y Vite, sin backend ni variables de entorno; seis modelos de hardware GLB y una única calcomanía con la marca facial de Tripo son toda la lista de recursos. Cuando WebGL no está disponible, un menú de texto toma el relevo y los juegos siguen siendo totalmente jugables.
Puntos clave
- Arrastra para orbitar alrededor de todo el montaje del salón (monitor CRT, consola, mando y cartucho) y luego haz clic en la consola o en POWER ON: la cámara avanza hacia la pantalla y atraviesa el CRT hasta la biblioteca de cartuchos.
- Tres cartuchos, tres juegos originales: Snake, con cadena de 5× en cinco segundos y fruta dorada de 50 puntos; Breakout, con tres fases de ladrillos y los potenciadores W (pala más ancha) y 3 (bola triple); Blocks, con bolsas de siete piezas, fantasma de aterrizaje y bonificaciones por combo.
- Flechas / WASD y espacio en escritorio, P para pausar y ESC para salir, controles táctiles en el móvil; las puntuaciones máximas solo viven en tu propio navegador.
- Cada juego tiene su propio bucle chiptune y sus efectos de sonido, y en los menús suena la pista «Trip Of Your Smile» (CC BY 4.0); si WebGL no está disponible, la recreativa recurre a botones de texto y sigue siendo jugable.
Análisis técnico
Recursos 3D · Seis piezas de hardware GLB revinculadas en tiempo de ejecución
El monitor CRT, la consola, el mando y los tres cartuchos se entregan como seis archivos GLB (exportados con el GLTFExporter de Three.js, low-poly con 128–384 triángulos cada uno). GLTFLoader los carga en paralelo y cada instancia es un clon profundo: los desplazamientos de centrado de la exportación se ponen a cero, los materiales se clonan por instancia y todas las mallas proyectan y reciben sombras. La placa de la etiqueta del cartucho se localiza por su desplazamiento local exportado, se nombra cartridge-label y se reasigna a una textura de portada pintada en Canvas; la mascota de píxeles se arma a partir de nodos con nombre —mascot-body, ambos brazos, la antena y una calcomanía de logo con la marca facial de Tripo—, con el tone mapping y la escritura de profundidad desactivados para que la calcomanía plana se mantenga nítida.
La capa de recursos conserva además una ranura de intercambio: si el campo controller de public/models/manifest.json apunta a cualquier GLB, fitProp lo escala por su caja envolvente a 2,54 unidades de ancho, lo apoya en el suelo, lo centra y ocupa la posición del mando original; un mando personalizado (por ejemplo, generado con Tripo) entra sin tocar el código.
Pantalla y etiquetas · Texturas Canvas como interfaz
La escena no carga ningún mapa de textura externo. La cara del CRT es una CanvasTexture de 1024×700: la pantalla de reposo (PLAYROOM SYSTEM / PLAYER ONE / PRESS START) se dibuja fotograma a fotograma en un contexto 2D y se usa a la vez como map y emissiveMap para que el tubo brille. Cada portada de cartucho es su propio lienzo de 640×610, pintado al vuelo a partir del color, la tinta, el número y el eslogan de ese juego en el catálogo, pixel art y rotulación del título incluidos.
Al encender, un controlador del ciclo de vida de la pantalla cambia el material por una textura de flujo de datos mientras la cámara vuelve al frente del tubo siguiendo una trayectoria esférica alrededor del centro de la pantalla, avanza y corta a la biblioteca al atravesar el cristal; con prefers-reduced-motion se omite todo el movimiento y se pasa directamente.
Interacción · Gestos de puntero, selección por raycast y órbita cinematográfica
En la vista del escritorio, una misma secuencia del puntero es a la vez arrastre de órbita y clic: el controlador de gestos registra theta / phi al pulsar, trata el movimiento que supera un umbral como arrastre y cualquier otra cosa como clic. Los impactos se resuelven con un Raycaster: en el escritorio todo el montaje actúa como botón de encendido y en la biblioteca el impacto sube por la cadena de padres hasta el nodo que lleva un gameId. Al soltar, la órbita nunca se congela; un controlador cinematográfico funde el desplazamiento manual de vuelta a un balanceo automático lento.
Al pasar el cursor o enfocar un cartucho, este se eleva 0,24, se inclina hacia delante con un leve balanceo y cambia la mascota de píxeles a la pose de ese juego, mientras cada cartucho oscila con su propia fase sinusoidal. Toda interpolación del bucle de renderizado usa el suavizado independiente de la tasa de fotogramas 1 − e^(−5·dt), así que los fotogramas lentos nunca dan saltos.
Juegos y audio · Sesiones 2D independientes y sonido sintetizado
Los tres juegos son módulos de lógica pura (create / step / input); una capa de sesión los dibuja en un lienzo 2D independiente: 480×480 para Snake y Breakout, 300×540 para Blocks. requestAnimationFrame solo se programa mientras se está jugando, así que la pausa detiene literalmente los fotogramas, y un tope de 250 ms por paso evita el tunneling tras cambiar de pestaña. Los eventos del juego (comer, ladrillo, línea, potenciador, nivel) alimentan una capa de efectos de partículas y se condensan por prioridad en una única señal de sonido.
Todo el audio se sintetiza en directo con osciladores de Web Audio: una melodía chiptune de 16 pasos por juego (82–120 BPM) y una veintena de señales construidas con deslizamientos de frecuencia y envolventes, con un tope de 64 voces. La pista del menú es una grabación CC BY 4.0 que recurre al bucle sintetizado si falla la descarga; el audio se silencia cuando la pestaña está oculta y el contexto solo se desbloquea tras el primer clic. Las puntuaciones máximas van a localStorage y pasan a memoria si se deniega el almacenamiento: un guardado fallido nunca interrumpe una partida.


