Introduction
In this article, I’ll show you how to create a classic Snake game using HTML, CSS, and JavaScript. We’ll use HTML and CSS for the layout and style, and JavaScript to bring the game to life by controlling the snake's movement, detecting collisions, and updating the score.
Snake Game Setup
- HTML: Set up the basic structure with a <canvas> for the game area and a score display.
- CSS: Add minimal styling to center the game and make it visually appealing.
- JavaScript: Define the game logic, including the snake's movement, food generation, collision detection, and score updates.
HTML Code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Snake Game</title>
</head>
<body>
<h1>Snake Game</h1>
<canvas id="gameCanvas"></canvas>
<button class="retry" onclick="restartGame()">Try Again</button>
<p>Score: <span id="score">0</span></p>
<script src="script.js"></script>
</body>
CSS Code
The CSS centers the game elements on the page and adds some styling to canvas and the "Try Again" button, which appears when the game ends.
body {
display: flex;
flex-direction: column;
align-items: center;
font-family: Arial, sans-serif;
background-color: #333;
color: #fff;
}
h1 {
margin-top: 20px;
}
canvas {
border: 2px solid #3725db;
background-color: #000;
}
p {
font-size: 1.5em;
margin-top: 10px;
}
/* Style the Try Again button */
.retry {
display: none;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
padding: 15px 30px;
font-size: 1.5em;
color: #fff;
background-color: #ff6347;
border: none;
border-radius: 10px;
cursor: pointer;
}
.retry:hover {
background-color: #ff4500;
}
JavaScript Game Logic
The game logic is handled in the script.js file.
Initialize and Setup the Game Canvas and Variables
- The code first gets the HTML canvas element (gameCanvas) and sets up the 2D drawing context (ctx).
- Game variables like boxsize, score, snake, direction, food, and game are defined.
- The initializeGame function sets up the initial game state with a starting snake position, score, and direction, and generates the first food position.
const canvas = document.getElementById("gameCanvas"); const ctx = canvas.getContext("2d"); const retryButton = document.querySelector(".retry"); // Canvas dimensions canvas.width = 400; canvas.height = 400; // Game variables let boxSize = 20; let score = 0; let snake; let direction; let food; let game; // Initialize the game state function initializeGame() { score = 0; snake = [{ x: boxSize * 5, y: boxSize * 5 }]; direction = "RIGHT"; document.getElementById("score").innerText = score; food = generateFood(); } // Generate random food position function generateFood() { return { x: Math.floor(Math.random() * (canvas.width / boxSize)) * boxSize, y: Math.floor(Math.random() * (canvas.height / boxSize)) * boxSize }; }
Define Game Logic and Drawing
- The change direction function updates the snake's direction based on arrow key inputs, ensuring it can't immediately reverse direction.
- Clears the canvas, draws the food and snake, and moves the snake by updating its head position. If the snake eats the food, the score increases, and new food is generated; otherwise, the tail is removed.
- The game ends if the snake collides with a wall or itself.
// Control the snake direction with arrow keys document.addEventListener("keydown", changeDirection); function changeDirection(event) { if (event.key === "ArrowUp" && direction !== "DOWN") { direction = "UP"; } else if (event.key === "ArrowDown" && direction !== "UP") { direction = "DOWN"; } else if (event.key === "ArrowLeft" && direction !== "RIGHT") { direction = "LEFT"; } else if (event.key === "ArrowRight" && direction !== "LEFT") { direction = "RIGHT"; } } // Draw the game function draw() { // Clear the canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // Draw the food as a circle ctx.fillStyle = "red"; ctx.beginPath(); ctx.arc(food.x + boxSize / 2, food.y + boxSize / 2, boxSize / 2, 0, Math.PI * 2); ctx.fill(); // Draw the snake as circles ctx.fillStyle = "lime"; for (let segment of snake) { ctx.beginPath(); ctx.arc(segment.x + boxSize / 2, segment.y + boxSize / 2, boxSize / 2, 0, Math.PI * 2); ctx.fill(); } // Move the snake let head = { ...snake[0] }; if (direction === "UP") head.y -= boxSize; if (direction === "DOWN") head.y += boxSize; if (direction === "LEFT") head.x -= boxSize; if (direction === "RIGHT") head.x += boxSize; // Add new head to the snake snake.unshift(head); // Check if the snake has eaten the food if (head.x === food.x && head.y === food.y) { score++; document.getElementById("score").innerText = score; food = generateFood(); } else { // Remove the last part of the snake if no food is eaten snake.pop(); } // Check for collision with walls or self if (head.x < 0 || head.x >= canvas.width || head.y < 0 || head.y >= canvas.height || isCollision(head)) { endGame(); } } // Check if the snake collides with itself function isCollision(head) { for (let i = 1; i < snake.length; i++) { if (snake[i].x === head.x && snake[i].y === head.y) { return true; } } return false; }
Game Control Functions
- The endGame function stops the game loop and displays a "Retry" button, while restartGame hides the button, re-initializes the game, and starts the game loop again.
- The game is initialized for the first time and starts with a 100 ms interval for each frame.
// End the game function endGame() { clearInterval(game); retryButton.style.display = "block"; // Show the "Try Again" button } // Restart the game function restartGame() { retryButton.style.display = "none"; // Hide the "Try Again" button initializeGame(); game = setInterval(draw, 100); // Restart the game loop } // Start the game for the first time initializeGame(); game = setInterval(draw, 100);
Output

Enhancements
You might include more features.
- The snake's pace improves as it turns longer.
- High Score Tracking: Save the high score to local storage.
- Difficulty Levels: Allow the player to choose the game move from the start.
- Wall-wrapping: When the snake crosses the screen border, it should show on the opposite side.

Join the conversation! Your thoughts help the community grow.