¿Recuerdas el clásico Snake de los viejos Nokia? Ahora imagínalo con luces de neón, música sintetizada, partículas explosivas y, lo más importante: conectado a la nube.
El desarrollo de videojuegos web ha vivido un renacimiento gracias a la potencia de HTML5 Canvas y las herramientas Serverless. Antiguamente, si querías guardar las puntuaciones de tus jugadores para hacer un ranking global, necesitabas montar un servidor, configurar una API REST, gestionar bases de datos y pagar un hosting. Hoy, gracias al Backend-as-a-Service (BaaS), podemos hacerlo en minutos.
En este tutorial, vamos a construir “Neon Snake”, un arcade web estético y funcional que utiliza Supabase (una alternativa Open Source a Firebase) para persistir datos en PostgreSQL.

🛠️ El Stack Tecnológico
Vamos a mantenerlo simple pero potente. Sin frameworks pesados, solo la pureza de la web:
- Frontend: HTML5 Canvas + JavaScript Vanilla (ES6+).
- Estilos: Tailwind CSS (vía CDN) para la interfaz de usuario.
- Backend: Supabase (PostgreSQL + Realtime).
- Audio: Web Audio API para efectos de sonido generados por código.
Paso 1: Configurando el Backend con Supabase
Antes de escribir una sola línea de JavaScript, necesitamos nuestro cerebro en la nube. Supabase nos ofrece una base de datos PostgreSQL real con una API generada automáticamente.
- Crea una cuenta gratuita en supabase.com.
- Crea un nuevo proyecto llamado
NeonSnake. - Ve a la sección Table Editor y crea una nueva tabla llamada
snake_scores.
La Magia del SQL
Para agilizar el proceso, Supabase te permite ejecutar comandos SQL directamente. Ve al SQL Editor y pega el siguiente código. Esto creará la estructura de la tabla y configurará las políticas de seguridad (RLS - Row Level Security) para que tu juego pueda leer y escribir datos públicamente.
-- 1. Crear la tabla de puntuaciones
create table snake_scores (
id bigint generated by default as identity primary key,
username text not null,
score integer not null,
created_at timestamp with time zone default timezone('utc'::text, now()) not null
);
-- 2. Habilitar seguridad a nivel de fila (RLS)
alter table snake_scores enable row level security;
-- 3. Política para permitir que cualquiera vea el ranking (Lectura pública)
create policy "Ver ranking publico"
on snake_scores for select
using ( true );
-- 4. Política para permitir que cualquiera guarde su récord (Escritura pública)
create policy "Guardar puntuacion"
on snake_scores for insert
with check ( true );
Nota de Seguridad: En un juego comercial, usaríamos autenticación de usuarios completa. Para este arcade tipo demo, permitimos el acceso público (anónimo) para reducir la fricción y que cualquiera pueda jugar y registrar su récord al instante.
Finalmente, ve a Settings > API y copia tu Project URL y tu anon public key. Las necesitarás en breve.
Paso 2: El Frontend Cyberpunk (HTML5 + Canvas)
Nuestro juego vivirá en un solo archivo index.html. Usaremos Tailwind CSS para los menús flotantes (Game Over, Start Screen) y el elemento <canvas> para renderizar el juego a 60 FPS.
La estructura básica es la siguiente:
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<title>Neon Snake 2077</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap');
body { font-family: 'Orbitron', sans-serif; background-color: #050505; }
/* Efecto de brillo de neón para el canvas */
canvas { box-shadow: 0 0 20px #0ff; border: 2px solid #0ff; }
</style>
</head>
<body class="h-screen w-full flex items-center justify-center overflow-hidden bg-gray-900">
<div class="relative">
<canvas id="gameCanvas" width="400" height="400"></canvas>
<div id="gameOverMenu" class="hidden absolute inset-0 bg-black/90 flex flex-col items-center justify-center z-10">
<h2 class="text-4xl text-red-500 font-bold mb-4 drop-shadow-[0_0_10px_rgba(239,68,68,0.8)]">SYSTEM FAILURE</h2>
<input type="text" id="usernameInput" placeholder="Enter ID..." class="bg-gray-800 text-cyan-400 p-2 rounded border border-cyan-500 mb-4 text-center outline-none">
<button id="saveBtn" class="bg-cyan-600 hover:bg-cyan-500 text-white font-bold py-2 px-4 rounded shadow-[0_0_15px_#06b6d4] transition-all">
UPLOAD DATA
</button>
<div id="leaderboard" class="mt-6 text-sm text-gray-300 w-64 text-left h-40 overflow-y-auto custom-scrollbar"></div>
</div>
</div>
<script type="module">
import { createClient } from 'https://cdn.jsdelivr.net/npm/@supabase/supabase-js/+esm'
// ... Lógica del juego aquí (Ver paso 3) ...
</script>
</body>
</html>
Lógica del Juego: Más allá de comer manzanas
Para darle ese toque “Pro” y diferenciarlo de un tutorial básico, implementamos varias mecánicas clave en el JavaScript:
- Loop de Animación: Usando
requestAnimationFramepara mantener la fluidez a 60 FPS. - Sistema de Partículas: Un array de objetos que se generan en las coordenadas de la “comida” al ser ingerida y se desvanecen gradualmente, simulando una explosión de datos.
- Firewalls Dinámicos: A partir del nivel 5, generamos obstáculos estáticos que el jugador debe esquivar, aumentando la dificultad progresivamente.
- Controles Híbridos:
- PC: Escuchamos eventos
keydownpara las flechas direccionales. - Móvil: Capturamos
touchstartytouchendpara calcular la dirección del gesto (swipe) y mover la serpiente.
- PC: Escuchamos eventos
Paso 3: Conectando los puntos (Integración con Supabase)
Aquí es donde ocurre la magia del Backend-as-a-Service. Vamos a conectar nuestro Canvas con la base de datos PostgreSQL. Dentro de nuestra etiqueta <script type="module">:
1. Inicialización del Cliente
const supabaseUrl = 'TU_PROYECTO_SUPABASE_URL';
const supabaseKey = 'TU_SUPABASE_ANON_KEY';
const supabase = createClient(supabaseUrl, supabaseKey);
2. El Flujo de “Game Over”
Cuando el jugador pierde y pulsa “UPLOAD DATA”, necesitamos asegurarnos de que el dato se guarda antes de mostrar la tabla de clasificación. Usaremos async/await para controlar este flujo asíncrono.
async function saveScore(username, score) {
// 1. Guardar la puntuación
const { error } = await supabase
.from('snake_scores')
.insert([{ username: username, score: score }]);
if (error) {
console.error('Error al subir datos:', error);
alert('Error de conexión con el servidor central.');
return;
}
// 2. Inmediatamente después, actualizamos el ranking visualmente
fetchLeaderboard();
}
3. Obtener el Ranking (Select)
Recuperamos el Top 20 ordenado por puntuación descendente para mostrar quién manda en el servidor.
async function fetchLeaderboard() {
const { data: scores, error } = await supabase
.from('snake_scores')
.select('username, score')
.order('score', { ascending: false }) // Orden descendente
.limit(20); // Solo los 20 mejores
if (error) console.error('Error fetching leaderboard:', error);
// Renderizar en el DOM
const list = document.getElementById('leaderboard');
list.innerHTML = '<h3 class="text-cyan-400 border-b border-cyan-500 mb-2 font-bold">TOP HACKERS</h3>';
scores.forEach((entry, index) => {
// Destacar el top 3 con colores diferentes si quieres
const colorClass = index < 3 ? 'text-yellow-400' : 'text-gray-300';
list.innerHTML += `
<div class="flex justify-between py-1 border-b border-gray-800">
<span class="${colorClass}">#${index + 1} ${entry.username}</span>
<span class="text-cyan-300 font-mono">${entry.score}</span>
</div>`;
});
}
Bonus: Realtime ⚡
¿Quieres que el ranking se actualice en la pantalla de todos los jugadores conectados si alguien rompe el récord en ese preciso instante? Supabase hace esto trivial con sus suscripciones:
// Suscribirse a cambios en la tabla 'snake_scores'
supabase
.channel('public:snake_scores')
.on('postgres_changes',
{ event: 'INSERT', schema: 'public', table: 'snake_scores' },
(payload) => {
console.log('¡Nuevo récord detectado en la red!', payload);
fetchLeaderboard(); // Refresca la lista automáticamente para todos
}
)
.subscribe();
El Resultado Final
Al unir todo, obtienes una experiencia de usuario fluida y altamente competitiva:
- El usuario juega frenéticamente esquivando “Firewalls” de neón.
- Al chocar, el juego se detiene con un efecto visual de “glitch”.
- Introduce su nombre (ej: “Neo”).
- Al pulsar enviar, en milisegundos, su nombre aparece en la tabla de clasificación.
- Si su amigo está jugando en otro móvil al mismo tiempo, verá el nombre de “Neo” aparecer en su pantalla sin recargar la página.
Y una vez creado… sólo nos queda disfrutar y jugar 🐍😊
Conclusión
Hemos pasado de un simple canvas estático a una aplicación Fullstack en tiempo real sin tocar un servidor backend tradicional (Node.js, PHP, Python) ni gestionar infraestructura. La combinación de la creatividad del desarrollo de videojuegos con la potencia de herramientas BaaS como Supabase abre un mundo de posibilidades para desarrolladores frontend.
¿El siguiente paso? Te invito a clonar el proyecto y mejorarlo: añade autenticación con GitHub para evitar nombres duplicados, crea skins para la serpiente que se desbloqueen con la puntuación, o implementa un modo multijugador usando los canales de presencia de Supabase.
¡El código es tuyo! 🐍💻
¿Te ha gustado este tutorial? Compártelo en tus redes con tus High Scores y no olvides revisar la documentación oficial de Supabase para profundizar más.