Snake is the perfect first game project. It's complex enough to be interesting, simple enough to be achievable, and teaches you almost everything fundamental about game programming. Let's break down how it works.
The Game Loop
Every game, at its heart, is a loop: update state → render state → wait → repeat. For Snake, we use requestAnimationFrame as the loop driver, with an accumulator to throttle our update speed independently from the render speed. This gives us smooth rendering at 60fps while the snake moves at a controlled 9–22 moves per second.
Representing the Snake
The snake is simply an array of objects, each containing an x and y grid coordinate. The head is always at index 0. Movement works by inserting a new head at the front and removing the tail from the end — unless food was eaten, in which case we skip the removal, growing the snake by one cell.
Collision Detection
Wall collision is a simple bounds check. Self-collision is a scan of the entire snake array to see if the new head position matches any existing segment. For large snakes this is O(n) — fine for our purposes, but worth noting as a performance consideration in more complex games.
Food Spawning
Food must never spawn on the snake. We build a Set of all occupied coordinates, then randomly sample grid positions until we find an unoccupied one. For very long snakes, we could use an exclusion list approach for efficiency, but random sampling works fine up to grid-filling lengths.
The Canvas API
We draw everything with the HTML5 Canvas 2D API. The grid background, snake segments, food dot, and particle effects are all rendered each frame. Key tricks include using shadowBlur for the glow effect and roundRect for the slightly rounded snake segments.
- Keep game state (positions, score) completely separate from rendering
- Use a delta-time accumulator for frame-rate independent speed
- Web Audio API synthesises sounds without any audio files
- Touch events need a minimum swipe threshold to avoid false triggers