Snake Game
Open the editor and paste this code to play Snake live.
The Code
javascript
if (!state.init) {
state.cell = 20;
state.cols = Math.floor(w / state.cell);
state.rows = Math.floor(h / state.cell);
state.snake = [
{ x: 8, y: 8 },
{ x: 7, y: 8 },
{ x: 6, y: 8 }
];
state.dir = { x: 1, y: 0 };
state.nextDir = { x: 1, y: 0 };
state.food = { x: 15, y: 10 };
state.score = 0;
state.timer = 0;
state.speed = 0.12;
state.gameOver = false;
state.init = true;
}
if (!state.gameOver) {
if ((keys.ArrowUp || keys.w) && state.dir.y !== 1)
state.nextDir = { x: 0, y: -1 };
if ((keys.ArrowDown || keys.s) && state.dir.y !== -1)
state.nextDir = { x: 0, y: 1 };
if ((keys.ArrowLeft || keys.a) && state.dir.x !== 1)
state.nextDir = { x: -1, y: 0 };
if ((keys.ArrowRight || keys.d) && state.dir.x !== -1)
state.nextDir = { x: 1, y: 0 };
}
if (!state.gameOver) {
state.timer += dt;
if (state.timer >= state.speed) {
state.timer = 0;
state.dir = state.nextDir;
const head = state.snake[0];
const newHead = { x: head.x + state.dir.x, y: head.y + state.dir.y };
if (newHead.x < 0 || newHead.x >= state.cols || newHead.y < 0 || newHead.y >= state.rows) {
state.gameOver = true;
}
for (const s of state.snake) {
if (s.x === newHead.x && s.y === newHead.y) state.gameOver = true;
}
if (!state.gameOver) {
state.snake.unshift(newHead);
if (newHead.x === state.food.x && newHead.y === state.food.y) {
state.score++;
state.speed = Math.max(0.05, state.speed * 0.98);
while (true) {
const fx = Math.floor(Math.random() * state.cols);
const fy = Math.floor(Math.random() * state.rows);
let occupied = false;
for (const s of state.snake) { if (s.x === fx && s.y === fy) { occupied = true; break; } }
if (!occupied) { state.food = { x: fx, y: fy }; break; }
}
} else {
state.snake.pop();
}
}
}
}
const particles = [];
for (const s of state.snake) {
particles.push({ x: s.x * state.cell + state.cell / 2, y: s.y * state.cell + state.cell / 2, r: state.cell * 0.42, color: "#22C55E" });
}
particles.push({ x: state.food.x * state.cell + state.cell / 2, y: state.food.y * state.cell + state.cell / 2, r: state.cell * 0.42, color: "#EF4444" });
particles.push({ x: state.snake[0].x * state.cell + state.cell / 2, y: state.snake[0].y * state.cell + state.cell / 2, r: state.cell * 0.45, color: "#4ADE80" });
const text = { Score: String(state.score) };
if (state.gameOver) {
text["GAME OVER"] = "Press R to Restart";
if (keys.r || keys.R) state.init = false;
}
return { particles, text };Key Concepts
| Concept | How It's Used |
|---|---|
| Grid system | Compute cols/rows from w/h divided by cell size |
| Direction queue | nextDir buffers input between ticks; only flushed on tick |
| Tick timer | state.timer += dt — game only advances at fixed intervals |
| Collision | Wall bounds check + self-intersection loop |
| Food spawning | Random position, rejected if on snake body |
| Difficulty scaling | speed *= 0.98 each food eaten (capped at 0.05s) |
| Restart | state.init = false on R key during game over |
Building Your Own Games
The keys global opens up endless possibilities:
- Pong / Breakout — paddle control with arrow keys, ball physics with collision
- Platformer — gravity, jump with
w/space, left/right movement - Top-down RPG — 4-direction movement, grid collision, item pickups
- Rhythm game — timed key presses, scoring by accuracy
- Puzzle game — grid manipulation with keyboard controls
See the Keyboard Input guide for more patterns and techniques.
See Also
- Keyboard Input - full keyboard API reference
- State Persistence - managing game state
- Coordinate System - understanding the canvas space