Cómo Crear un Sistema de Audio Global para una Web de Videojuegos
El Orbe de Resonancia es el sistema de audio global de la web de Quintaesencia. Música que empieza en la portada y sigue sonando mientras navegas — sin interrupciones, sin recargas, sin perder el hilo de la canción.
🎵 ¿Qué hace exactamente?
Cuando entras a la web de Quintaesencia, el sistema detecta si ya había música sonando en tu visita anterior (gracias a cookies) y la retoma desde donde la dejaste. Si es tu primera visita, arranca con una canción aleatoria de las 40 disponibles.
Lo importante es que la música no se corta al cambiar de página. Puedes ir del blog a la galería, de la galería a los personajes, y la canción sigue. Esto es lo que diferencia al Orbe de Resonancia de un simple reproductor de audio incrustado en una página.
- 40 canciones (30 de música del juego + 10 del libro)
- Modos de reproducción: orden, aleatorio y repetición
- Sistema de favoritos persistente por usuario
- Sincronización de estado entre páginas vía cookies y localStorage
- Carga diferida — no bloquea el renderizado inicial (FCP)
🏗️ Arquitectura: tres módulos, una experiencia
El sistema está dividido en tres archivos JavaScript independientes que trabajan juntos:
1. orbe-audio-master.js — El motor
Es el núcleo del sistema. Crea y gestiona el elemento <audio> real del navegador. Se ejecuta en cada página y es responsable de:
- Crear el elemento audio con
id="orbe-audio-master" - Leer el estado guardado (canción actual, posición, volumen) desde cookies
- Reproducir, pausar, avanzar y retroceder pistas
- Guardar el estado en cookies cada 500ms para que persista entre páginas
- Emitir eventos de sincronización al mini reproductor
function setCookie(name, value, days = 365) {
const date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
document.cookie = name + «=» + value + «;expires=» + date.toUTCString() + «;path=/»;
}
// Se llama en cada evento play/pause/timeupdate
function saveState() {
setCookie(‘orbeCurrentTrack’, currentTrack?.file);
setCookie(‘orbeCurrentTime’, audioElement.currentTime);
setCookie(‘orbeIsPlaying’, isPlaying);
setCookie(‘orbeVolume’, volume);
}
2. orbe-resonancia-mini.js — La interfaz
El mini reproductor visible en la esquina inferior de la pantalla. No controla el audio directamente — se comunica con el Audio Master a través de cookies y localStorage. Gestiona:
- La UI del reproductor (nombre de canción, botones, barra de progreso)
- El sistema de favoritos (qué canciones aparecen en la lista de reproducción)
- La lectura del estado del Audio Master para mostrar la canción correcta
- Los controles de usuario (play/pause, siguiente, anterior, modo)
Una decisión de diseño importante: el mini reproductor tiene su propia variable estaTocando como fuente de verdad para la UI, ignorando la cookie orbeIsPlaying después de la inicialización. Esto evita un bug donde el Audio Master sobreescribía el estado de pausa del mini reproductor.
3. orbe-sync.js — El puente
Un módulo ligero que se carga en la home para inicializar los favoritos en localStorage si no existen, y escucha cambios de estado entre ventanas usando el evento storage. Permite que si el usuario tiene la web abierta en dos pestañas, ambas estén sincronizadas.
🔄 Cómo persiste la música entre páginas
Este es el problema central que resuelve el sistema. Cuando el navegador carga una nueva página, el JavaScript anterior se destruye — y con él, el elemento <audio>. La solución es guardar el estado completo en cookies antes de que eso ocurra.
- Página A: El Audio Master guarda en cookies: canción actual, posición en segundos, volumen, estado play/pause.
- Navegación: El usuario hace clic en un enlace. El JS de la página A se destruye.
- Página B: El Audio Master se inicializa, lee las cookies, crea el elemento audio, carga la misma canción y salta a la posición guardada.
- Resultado: La música continúa desde donde se quedó, con un gap de ~200-400ms durante la carga.
Las cookies tienen un año de expiración y scope path=/, lo que garantiza que son accesibles desde cualquier URL del dominio.
⚡ Carga diferida con orbe-loader.js
Los tres módulos de audio suman ~123KB de JavaScript. Cargarlos de forma síncrona bloquearía el renderizado inicial y penalizaría el FCP (First Contentful Paint). La solución fue un loader que inyecta los scripts de forma dinámica solo cuando el usuario interactúa con el reproductor.
const AUDIO_MODULES = [
‘orbe-audio-master.js’,
‘orbe-resonancia-mini.js’,
‘orbe-sync.js’
];
// Se activa en el primer click/touch del usuario
function loadAudioModules() {
AUDIO_MODULES.forEach(src => {
const script = document.createElement(‘script’);
script.src = ‘/wp-content/themes/twentytwentyfive/assets/’ + src;
document.head.appendChild(script);
});
}
El resultado: esos 123KB no aparecen en el FCP. El tiempo de carga inicial bajó de ~6 segundos a 624ms en primera visita.
🍪 Cookies vs localStorage: por qué usamos ambos
El sistema usa dos mecanismos de persistencia con propósitos distintos:
- Canción actual, posición, volumen, estado play/pause
- Disponibles en todas las páginas del dominio
- Persisten 365 días
- Se actualizan cada 500ms mientras suena música
localStorage — para los favoritos:
- Lista de canciones favoritas del usuario (por defecto, todas)
- Compartido entre el mini reproductor y el Audio Master
- También lo lee el reproductor del juego en RPG Maker MZ
- Permite sincronización entre pestañas vía evento
storage
🎮 Integración con el juego
Una característica poco obvia del sistema es que los favoritos del mini reproductor web se sincronizan con el reproductor de música dentro del juego RPG Maker MZ. Ambos leen la misma clave orbe_favorites de localStorage, que contiene un array con los nombres de archivo de las canciones activas.
Esto significa que si el jugador desactiva una canción en el reproductor web, esa canción tampoco aparecerá en la rotación del juego — siempre que lo esté jugando en el mismo navegador.
🐛 El bug de la pausa y cómo lo resolvimos
Durante el desarrollo apareció un bug persistente: al pausar la música desde el mini reproductor, el Audio Master detectaba el evento pause y escribía orbeIsPlaying=false en la cookie. Hasta aquí correcto. El problema era que en la siguiente página, el mini reproductor leía esa cookie y mostraba el botón de play — pero el Audio Master ya había reanudado la reproducción internamente.
- Cookie
orbeIsPlayingdecía: pausado - Elemento
<audio>real decía: reproduciendo - La UI mostraba ▶ pero sonaba música
La solución fue declarar la variable interna estaTocando del mini reproductor como la única fuente de verdad para la UI, ignorando la cookie después de la inicialización. La cookie solo se usa para decidir si arrancar o no al cargar la página — nunca para actualizar el estado de los botones en tiempo real.
📊 Sistema de logs silenciados
Para no contaminar la consola del navegador en producción, el sistema implementa un logger interno. Todos los eventos (carga de canción, cambios de estado, errores) se guardan en window._orbeLogs sin mostrarse. Para depurar, basta con escribir orbeLog() en la consola del navegador.
orbeLog()
// Muestra una tabla con todos los eventos:
┌──────────┬────────┬──────────────────────────────────────┐
│ t │ src │ msg │
├──────────┼────────┼──────────────────────────────────────┤
│ 10:32:01 │ master │ ✓ Audio element creado │
│ 10:32:01 │ master │ ► Cargando canción: Nigromante │
│ 10:32:02 │ mini │ ✓ 40 canciones marcadas como fav. │
└──────────┴────────┴──────────────────────────────────────┘
💡 Decisiones técnicas clave
La Web Audio API ofrece más control (efectos, análisis de frecuencias) pero tiene una curva de complejidad alta y peor soporte en Safari. Para reproducción de música de fondo, el elemento <audio> nativo es más robusto y suficiente.
RPG Maker MZ usa OGG por defecto. Mantener el mismo formato en la web evita tener que convertir y mantener dos versiones de cada archivo de audio. OGG tiene mejor compresión que MP3 a la misma calidad perceptiva, y el soporte en navegadores modernos es completo.
localStorage no es accesible desde PHP (solo desde JavaScript). Las cookies sí. Aunque en este caso no se necesita acceso desde PHP, las cookies tienen la ventaja de que se envían automáticamente con cada request HTTP, lo que podría usarse en el futuro para personalización server-side.
El Orbe de Resonancia es uno de esos sistemas que el usuario no nota cuando funciona bien — y eso es exactamente el objetivo. La música de Quintaesencia merece acompañar la experiencia completa de la web, no interrumpirse en cada clic. Con ~400 líneas de JavaScript repartidas en tres módulos, conseguimos eso.