Bringing Snake Back to Life with Vanilla JavaScript
Play and reminisce here:
I’ve always had a soft spot for the classic Nokia Snake game. I remember how much I enjoyed it back in the day—such a simple concept, yet it was so addictively fun. In a world now saturated with frameworks, tools, and endless libraries, I thought, why not take it back to basics and recreate the game in vanilla JavaScript? No fancy frameworks, no shortcuts—just pure, old-school coding, like the game itself.
Why Snake?
Snake represents a nostalgic era of gaming for me and many others. It’s one of the first mobile games that sparked joy for millions, and it’s the epitome of "less is more." The concept is simple: you control a snake that grows each time it eats food, and the challenge intensifies as the snake grows longer and the space becomes tighter. I wanted to recapture that simplicity in my code, with no added complexity that a framework might introduce.
Going Back to Basics: Vanilla JavaScript
The goal was straightforward: use nothing but vanilla JavaScript, HTML, and CSS. That means no React, no Vue, no libraries like jQuery—just good old-fashioned DOM manipulation. This approach forces you to think more deeply about how to structure your code, how to efficiently manipulate the DOM, and how to handle game logic without relying on third-party tools.
Breaking Down the Code
The code structure mirrors the simplicity of the original game but is packed with all the essential logic. Everything from handling snake movement, food generation, collision detection, and keeping track of the score is handled with clean, pure JavaScript.
Snake Movement and Food Generation
The movement of the snake is the core of the game. Using keyboard events to control the direction of the snake, each tick of the game updates the snake’s position. This is all managed in the move function:
function move() {
const head = { ...snake[0] }; // Copy the snake's head
switch (direction) {
case "up":
head.y--;
break;
case "down":
head.y++;
break;
case "left":
head.x--;
break;
case "right":
head.x++;
break;
}
// Add the new head position at the front of the snake array
snake.unshift(head);
// If the snake's head reaches the food, regenerate food at a new position
if (head.y === food.y && head.x === food.x) {
food = generateRandomFoodPosition();
increaseSpeed();
} else {
// If not, remove the last part of the snake to maintain the length
snake.pop();
}
}
Here, the game uses the switch statement to handle directional movement, and every movement updates the position of the snake’s head. It’s a simple but effective method to control the flow of the game.
Collision Detection and Game Reset
Snake isn’t much of a challenge if there are no consequences, so collision detection is crucial. If the snake’s head touches the walls or its own body, the game resets:
领英推荐
function checkCollision() {
const head = snake[0]; // The snake's head
// Check if the snake's head has collided with the boundaries
if (head.x < 1 || head.x > gridSize || head.y < 1 || head.y > gridSize) {
resetGame();
}
// Check if the snake has collided with itself
for (let i = 1; i < snake.length; i++) {
if (head.x === snake[i].x && head.y === snake[i].y) {
resetGame();
}
}
}
This function checks whether the snake’s head is out of bounds or if it runs into itself. If a collision is detected, the game resets, and the player gets another chance to start over.
Increasing Difficulty
A simple but fun way to increase the challenge is by speeding up the game each time the snake eats food. In the increaseSpeed function, the interval between each movement becomes shorter, giving the game a natural difficulty curve:
function increaseSpeed() {
if (gameSpeedDelay > 150) {
gameSpeedDelay -= 5;
} else if (gameSpeedDelay > 100) {
gameSpeedDelay -= 3;
} else if (gameSpeedDelay > 50) {
gameSpeedDelay -= 2;
} else if (gameSpeedDelay > 25) {
gameSpeedDelay -= 1;
}
}
By gradually increasing the speed, the game becomes more intense over time, just like in the original Snake.
Reset and Score Handling
The game resets when the snake collides, and the score is tracked to keep the player motivated:
function resetGame() {
updateHighScore(); // Check if a new high score has been achieved
stopGame(); // Stop the game
snake = [{ x: 10, y: 10 }]; // Reset snake's position
food = generateRandomFoodPosition(); // Generate new food position
direction = "right"; // Reset direction
gameSpeedDelay = 200; // Reset speed
updateScore(); // Update the score display
}
This function handles the game reset and makes sure the high score is updated if needed.
No Fancy Frameworks
One of the most satisfying aspects of this project was working without any frameworks. Writing in pure JavaScript made me appreciate the fundamental workings of DOM manipulation and game loops. Instead of letting a framework dictate how to structure my project, I could make it as lean as possible. The result is a game that runs smoothly and is easy to maintain.
Why Vanilla JavaScript?
In the current state of web development, it's easy to get caught up in using frameworks and libraries that sometimes add unnecessary complexity. This project reminded me how fun and rewarding it can be to code directly in JavaScript and manage everything myself—from event handling to DOM updates. It’s a great way to sharpen your core programming skills and understand the magic behind the scenes of more complex applications.
Conclusion
Recreating Snake with just vanilla JavaScript has been a nostalgic yet insightful journey. I was able to revive a game that I’ve loved for years while stripping back the complexity that modern frameworks often add to projects. It’s a fun challenge that any developer should try—especially if, like me, you have a soft spot for the classics.
Feel free to check out the code, take it for a spin, and maybe even challenge yourself to extend it with features like walls, bonuses, or multiplayer modes—just be sure to keep it simple, like the original.