<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Snake JS - HTML5 Canvas</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: #1a1a2e;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-height: 100vh;
font-family: 'Courier New', monospace;
color: #eee;
}
h1 { font-size: 1.6rem; margin-bottom: 10px; color: #4ecca3; letter-spacing: 3px; }
#score-panel { font-size: 1rem; margin-bottom: 12px; color: #ccc; }
#score-panel span { color: #4ecca3; font-weight: bold; }
canvas {
border: 2px solid #4ecca3;
background: #16213e;
display: block;
}
#overlay {
position: absolute;
background: rgba(26, 26, 46, 0.92);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 16px;
width: 400px;
height: 400px;
border: 2px solid #4ecca3;
}
#overlay h2 { font-size: 2rem; color: #e94560; }
#overlay p { color: #aaa; font-size: 0.9rem; }
#overlay button {
background: #4ecca3;
color: #1a1a2e;
border: none;
padding: 10px 28px;
font-size: 1rem;
font-family: inherit;
font-weight: bold;
cursor: pointer;
border-radius: 4px;
}
#overlay button:hover { background: #38b58e; }
#wrapper { position: relative; }
</style>
</head>
<body>
<h1>SNAKE</h1>
<div id="score-panel">Puntuación: <span id="score">0</span> Máximo: <span id="best">0</span></div>
<div id="wrapper">
<canvas id="canvas" width="400" height="400"></canvas>
<div id="overlay">
<h2>SNAKE</h2>
<p>Usa las flechas del teclado o desliza en móvil</p>
<button id="btn-start">Empezar</button>
</div>
</div>
<script>
const COLS = 20, ROWS = 20, CELL = 20;
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const overlay = document.getElementById('overlay');
const scoreEl = document.getElementById('score');
const bestEl = document.getElementById('best');
const btn = document.getElementById('btn-start');
// Estado del juego
let snake, dir, nextDir, food, score, best, running, loop;
function init() {
// Serpiente inicial: 3 celdas en el centro
snake = [
{x: 12, y: 10},
{x: 11, y: 10},
{x: 10, y: 10}
];
dir = {x: 1, y: 0};
nextDir = {x: 1, y: 0};
score = 0;
scoreEl.textContent = '0';
spawnFood();
}
function spawnFood() {
// Colocar comida en una celda libre
let pos;
do {
pos = {
x: Math.floor(Math.random() * COLS),
y: Math.floor(Math.random() * ROWS)
};
} while (snake.some(s => s.x === pos.x && s.y === pos.y));
food = pos;
}
function step() {
dir = nextDir;
const head = {x: snake[0].x + dir.x, y: snake[0].y + dir.y};
// Colisión con paredes
if (head.x < 0 || head.x >= COLS || head.y < 0 || head.y >= ROWS) {
gameOver(); return;
}
// Colisión consigo misma (excepto la última celda que se va a mover)
if (snake.slice(0, -1).some(s => s.x === head.x && s.y === head.y)) {
gameOver(); return;
}
snake.unshift(head);
if (head.x === food.x && head.y === food.y) {
// Comer: no quitar cola, subir puntuación
score += 10;
scoreEl.textContent = score;
if (score > best) {
best = score;
bestEl.textContent = best;
}
spawnFood();
} else {
snake.pop();
}
draw();
}
function draw() {
// Fondo
ctx.fillStyle = '#16213e';
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Grid (sutil)
ctx.strokeStyle = 'rgba(255,255,255,0.03)';
ctx.lineWidth = 0.5;
for (let x = 0; x < COLS; x++) {
ctx.beginPath(); ctx.moveTo(x * CELL, 0); ctx.lineTo(x * CELL, canvas.height); ctx.stroke();
}
for (let y = 0; y < ROWS; y++) {
ctx.beginPath(); ctx.moveTo(0, y * CELL); ctx.lineTo(canvas.width, y * CELL); ctx.stroke();
}
// Comida
ctx.fillStyle = '#e94560';
ctx.fillRect(food.x * CELL + 3, food.y * CELL + 3, CELL - 6, CELL - 6);
// Brillo comida
ctx.fillStyle = 'rgba(255,255,255,0.3)';
ctx.fillRect(food.x * CELL + 5, food.y * CELL + 5, 5, 5);
// Serpiente
snake.forEach((seg, i) => {
if (i === 0) {
// Cabeza
ctx.fillStyle = '#4ecca3';
} else {
// Cuerpo con degradado hacia más oscuro
const alpha = Math.max(0.4, 1 - i / snake.length * 0.6);
ctx.fillStyle = `rgba(78, 204, 163, ${alpha})`;
}
ctx.fillRect(seg.x * CELL + 1, seg.y * CELL + 1, CELL - 2, CELL - 2);
});
}
function gameOver() {
clearInterval(loop);
running = false;
// Mostrar overlay de game over
overlay.innerHTML = `
<h2>GAME OVER</h2>
<p>Puntuación: <strong style="color:#4ecca3">${score}</strong></p>
<p>Máximo: <strong style="color:#4ecca3">${best}</strong></p>
<button id="btn-start">Reintentar</button>
`;
overlay.style.display = 'flex';
document.getElementById('btn-start').addEventListener('click', startGame);
}
function startGame() {
overlay.style.display = 'none';
init();
running = true;
// Velocidad base: 150ms; aumenta 5ms cada 50 puntos (mínimo 60ms)
clearInterval(loop);
function tick() {
if (!running) return;
step();
const speed = Math.max(60, 150 - Math.floor(score / 50) * 5);
clearTimeout(loop);
loop = setTimeout(tick, speed);
}
tick();
}
// Teclado
document.addEventListener('keydown', e => {
if (!running) return;
switch(e.key) {
case 'ArrowUp': if (dir.y !== 1) nextDir = {x:0, y:-1}; e.preventDefault(); break;
case 'ArrowDown': if (dir.y !== -1) nextDir = {x:0, y:1}; e.preventDefault(); break;
case 'ArrowLeft': if (dir.x !== 1) nextDir = {x:-1,y:0}; e.preventDefault(); break;
case 'ArrowRight': if (dir.x !== -1) nextDir = {x:1, y:0}; e.preventDefault(); break;
}
});
// Touch / swipe
let touchX, touchY;
canvas.addEventListener('touchstart', e => {
touchX = e.touches[0].clientX;
touchY = e.touches[0].clientY;
e.preventDefault();
}, {passive: false});
canvas.addEventListener('touchend', e => {
if (!running) return;
const dx = e.changedTouches[0].clientX - touchX;
const dy = e.changedTouches[0].clientY - touchY;
if (Math.abs(dx) > Math.abs(dy)) {
if (dx > 20 && dir.x !== -1) nextDir = {x:1, y:0};
if (dx < -20 && dir.x !== 1) nextDir = {x:-1,y:0};
} else {
if (dy > 20 && dir.y !== -1) nextDir = {x:0, y:1};
if (dy < -20 && dir.y !== 1) nextDir = {x:0,y:-1};
}
e.preventDefault();
}, {passive: false});
// Arranque inicial
btn.addEventListener('click', startGame);
</script>
</body>
</html>
Snake en JavaScript con HTML5 Canvas
Implementación completa del clásico juego Snake con HTML5 Canvas. Controles de teclado y swipe táctil, velocidad creciente según la puntuación y diseño dark responsivo. Todo en un único fichero HTML sin dependencias.
Descargar adjuntos
COMPARTE ESTE TUTORIAL
COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP