
Snake Game in HTML, CSS, and JavaScript - Complete Source Code with Step-by-Step Logic Explanation
Snake Game in HTML, CSS, and JavaScript – Complete Code & Logic Explained
Introduction
The Snake game is a classic example used to understand real-time game loops, keyboard controls, collision detection, and canvas rendering. While many tutorials show the code, very few clearly explain what is happening behind the scenes.
Project Folder Structure
/ snake-game
├── index.html
├── style.css
└── script.js
Each file has a clear responsibility:
-
HTML → Game structure
-
CSS → Visual styling
-
JavaScript → Game logic and behavior
1. HTML Structure (index.html)
HTML defines where the game lives on the page.
Snake Game in JavaScript
Snake Game
Score: 0
What Is Happening Here?
-
is the drawing surface where the snake and food are rendered. -
Width and height create a grid-based playground.
-
JavaScript accesses this canvas using its
id.
2. CSS Styling (style.css)
CSS improves readability and user experience.
body {
margin: 0;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: #111;
font-family: Arial, sans-serif;
color: #fff;
}
.game-container {
text-align: center;
}
canvas {
background: #000;
border: 2px solid #4caf50;
}
.score {
margin-top: 10px;
font-size: 18px;
}
CSS Logic Explained
-
Flexbox centers the game vertically and horizontally.
-
Dark backgrounds reduce eye strain and highlight the snake.
-
The green border visually defines the game boundary.

3. JavaScript Game Logic (script.js)
This is where the real game mechanics live.
const canvas = document.getElementById("game");
const ctx = canvas.getContext("2d");
const scoreEl = document.getElementById("score");
Step 1: Accessing the Canvas
-
canvasrefers to the HTML canvas element. -
ctx(context) allows drawing shapes, colors, and clearing frames.
const box = 20;
const canvasSize = 400;
Step 2: Grid System
-
The entire game runs on a grid system.
-
Each snake movement shifts exactly
20px. -
This prevents smooth movement and ensures classic snake behavior.
let snake = [{ x: 200, y: 200 }];
let direction = "RIGHT";
let food = createFood();
let score = 0;
Step 3: Game State Variables
-
snakeis an array of objects, each representing a body segment. -
The first element is always the head.
-
directionstores current movement direction. -
foodstores the food position.
document.addEventListener("keydown", changeDirection);
Step 4: Keyboard Control
-
Listens for arrow key presses.
-
Updates the direction before the next frame.
function changeDirection(event) {
const key = event.keyCode;
if (key === 37 && direction !== "RIGHT") direction = "LEFT";
if (key === 38 && direction !== "DOWN") direction = "UP";
if (key === 39 && direction !== "LEFT") direction = "RIGHT";
if (key === 40 && direction !== "UP") direction = "DOWN";
}
Why Reverse Direction Is Blocked
-
Prevents instant self-collision.
-
Mimics classic snake game rules.
function createFood() {
return {
x: Math.floor(Math.random() * (canvasSize / box)) * box,
y: Math.floor(Math.random() * (canvasSize / box)) * box,
};
}
Food Generation Logic
-
Generates random positions aligned to the grid.
-
Ensures food always appears inside the canvas.
function collision(head, body) {
return body.some(segment => segment.x === head.x && segment.y === head.y);
}
Collision Detection Explained
-
Checks if the snake head overlaps any body segment.
-
Used to trigger game over.
function drawGame() {
ctx.clearRect(0, 0, canvasSize, canvasSize);
Game Loop Begins
-
Clears the previous frame.
-
Prevents motion trails.
snake.forEach((segment, index) => {
ctx.fillStyle = index === 0 ? "#4caf50" : "#8bc34a";
ctx.fillRect(segment.x, segment.y, box, box);
});
Snake Rendering Logic
-
Head is darker for visibility.
-
Each segment is drawn individually.
ctx.fillStyle = "red";
ctx.fillRect(food.x, food.y, box, box);
Food Rendering
-
Food is drawn every frame at its stored position.
let headX = snake[0].x;
let headY = snake[0].y;
Calculating New Head Position
-
Uses the current head as reference.
if (direction === "LEFT") headX -= box;
if (direction === "UP") headY -= box;
if (direction === "RIGHT") headX += box;
if (direction === "DOWN") headY += box;
Movement Logic
-
The snake moves one grid step per frame.
if (
headX < 0 || headY < 0 ||
headX >= canvasSize || headY >= canvasSize ||
collision({ x: headX, y: headY }, snake)
) {
clearInterval(game);
alert("Game Over! Score: " + score);
location.reload();
}
Game Over Conditions
-
Wall collision
-
Self collision
if (headX === food.x && headY === food.y) {
score++;
scoreEl.textContent = score;
food = createFood();
} else {
snake.pop();
}
Eating Logic Explained
-
When food is eaten, snake grows.
-
Otherwise, the tail is removed to simulate movement.
snake.unshift({ x: headX, y: headY });
}
Updating the Snake
-
Adds a new head at the front of the array.
const game = setInterval(drawGame, 120);
Game Loop Timing
-
Runs the game at a fixed speed.
-
Lower value = faster snake.
Why This Snake Game Works So Well
-
Clear separation of concerns
-
Predictable grid movement
-
Efficient collision logic
-
Beginner-friendly architecture
Included Topics
-
Snake game in JavaScript
-
HTML CSS JavaScript snake game tutorial
-
Canvas snake game explained
-
JavaScript game logic for beginners
Final Thoughts
This Snake game is more than just a demo—it teaches how real games manage state, render frames, and handle user input. Understanding this logic gives you a strong foundation for building more advanced JavaScript games.
You can now confidently extend this project with sound effects, levels, pause functionality, or mobile controls.