Sometimes the best projects come into being without any particular pretension, just for the pleasure of coding and diving back into video game classics. That's exactly the spirit behind this retro version of Snake, built entirely with HTML and vanilla JavaScript, with no framework or external library. A small fun project that deliberately stays at the prototype stage, even if a few requests could push it toward something more polished.
Why Snake remains the perfect project to learn JavaScript
Snake hits that rare sweet spot in programming: simple enough to build in a few hours, yet rich enough to explore the fundamentals of web development. No need to configure webpack, install endless npm dependencies or grasp the subtleties of React. Just one HTML file, one JavaScript file, and the magic happens.
This iconic game that appeared on early Nokia phones shaped an entire generation. Its mechanics, almost disarmingly pure — a snake moves on a grid, eats food to grow, and dies if it bites its own tail or hits a wall — hide a few interesting technical questions that teach you how to structure code, manage state and set up game loops.
Rebuilding Snake from scratch forces you to understand how animation really works in a browser. No game engine to handle rendering for you, no built-in physics. Everything has to be coded by hand: snake movement, collision detection, random food generation, score. That constraint becomes a tremendous learning opportunity.
The retro side of the project adds undeniable nostalgic charm. Rediscovering those deliberately simple pixelated visuals, those vibrant colors on a black background, that snappy gameplay where every millisecond counts creates an emotional bond with the arcade games of yesteryear. The aim isn't to reinvent the genre but to capture that vintage essence that makes the classics so good.
How to structure the HTML canvas to host the snake
The technical foundation of this Snake relies on the HTML5 canvas element, that programmable drawing surface that lets you manipulate pixels one by one via JavaScript. Unlike approaches using divs or tables to represent the playfield, the canvas offers far better performance and pixel-perfect control over rendering.
Creating the canvas takes just a few lines of HTML: a simple tag with defined dimensions, typically a 400x400 or 500x500 square depending on aesthetic preferences. The key is to keep dimensions divisible by the size of a grid cell, usually 20 pixels, to get a clean grid without odd offsets.
The canvas 2D context then becomes the main drawing tool. It exposes methods like fillRect to draw filled rectangles, perfect for the snake's segments and the food. The simplicity of these graphic primitives is more than enough for a game like Snake, where the aesthetic stays intentionally minimalist.
Handling snake movement and keyboard controls
The snake lives in an internal representation that describes it as an array of coordinates: each segment occupies a specific grid cell, and the head drives the movement. At every game tick, a new position is added at the head (according to the current direction) and the last tail segment is removed. If the snake eats an apple, you simply skip the removal step: the body grows by one cell.
On the input side, listening for keyboard events is plenty. Arrow keys (or WASD depending on preference) change the current direction, with a simple check to prevent the snake from turning back on itself, which would cause a frustrating instant death. Storing the direction as a vector (dx, dy) rather than a string ("up", "down") greatly simplifies the math and makes the code more elegant.
Time management deserves special care. A game loop based on setInterval with a fixed delay gives a predictable rhythm, perfect for Snake where difficulty classically ramps up by shortening that delay as the score grows. A more modern approach using requestAnimationFrame remains possible but adds unnecessary complexity for this kind of discrete-movement game.
Preventing contradictory simultaneous inputs is a detail often overlooked. If the player taps right then down quickly while the snake is heading left, the first key could briefly reverse the direction and cause an unexpected game over. Buffering input and only applying it on the next tick solves this elegantly.
Pausing the game with the spacebar adds a welcome touch of comfort. Letting the player breathe for a few seconds, think about strategy or simply look away without losing the run improves the overall experience. Implementing pause only requires a boolean flag that suspends the main game loop.
How to handle collisions and game over
Collision detection in Snake breaks down into two distinct categories: the beneficial collisions with food, and the fatal ones with walls or with the snake itself. Checking whether the snake's head shares coordinates with another element is generally enough, and the game's discrete grid makes the math much simpler.
Wall collisions are trivial: if the new head position falls outside the canvas bounds, the game stops. An interesting variant is to make the grid wraparound, turning the snake into a spatial cobra that re-enters from the opposite edge. This design choice deeply alters difficulty and feel — it's up to you to decide which philosophy fits your vision.
Self-collision needs a little more thought. Looping through the snake segments and checking whether the new head position matches any of them works, but be careful to exclude the tail that's about to disappear on that tick (unless food was just eaten). This algorithmic subtlety prevents unfair game overs that frustrate players.
Generating food and keeping score
Placing food on the grid raises an interesting algorithmic question. The naive approach is to generate random coordinates, then check whether that position is already occupied by the snake. If so, regenerate until you find a free cell. This method works perfectly while the grid is mostly empty.
The edge case appears when the snake fills almost the whole grid, leaving only a few empty cells. Random generation could then take hundreds or thousands of iterations before finding a valid spot. A more robust approach is to list all free cells and pick one at random, guaranteeing constant generation time.
The visual treatment of the food adds to the retro feel. A simple red square works, but adding a subtle variation — maybe a smaller square centered in the cell, or a softly shifting color — brings a visual touch that sets the project apart without overcomplicating the code.
The scoring system stays simple: a counter that increments each time food is eaten, displayed permanently on screen. Where you put that counter matters more than you'd think. Drawing it inside the canvas gives it a presence in the game, rather than treating it as a side note pinned in a corner.
The high score persisted across sessions via localStorage turns each run into a quest to beat yourself. Seeing your best score on launch motivates you to do better, fueling that "one more game" addictiveness typical of great arcade titles. A few lines of code for a meaningful boost in engagement.
Why this project deliberately stays a prototype
The prototype status of this Snake isn't an admission of failure but a deliberate choice that fits the spirit of the project. Born from a spontaneous urge to code something fun without delivery pressure or deadlines, this game captures the essence of development for its own sake. No backlog of features, no ambitious roadmap, just the minimum viable to have fun.
Adding levels, power-ups, multiple game modes or a skin system would turn this simple project into something more complex that would need a more elaborate architecture. Sometimes knowing when to stop preserves the purity of an idea.
That said, the beauty of a project open to evolution is its capacity to grow if the urge returns. A specific request, a brilliant idea that surfaces in a conversation, or simply the desire to push the exercise further could nudge this prototype toward a more polished version. The code is sitting there, ready to welcome new features if the need arises.
In the meantime, this HTML JavaScript Snake plays its portfolio role perfectly, demonstrating mastery of web development fundamentals: DOM manipulation, event handling, game logic, canvas animation. Everything a recruiter or client might want to see condensed into a playful and accessible project.







0 Comments