How to Fix the ‘Too Much Recursion’ Error in a JavaScript Snake Game

I’ve recently been working on building a simple Snake Game in JavaScript just a fun way to brush up on logic and game loops. While the game logic was mostly working, I ran into a frustrating error that halted everything “Uncaught RangeError: Maximum call stack size exceeded.” After some debugging, I realized it was caused by a classic mistake too much recursion.

Here’s how it happened, how I fixed it, and a few fun improvements I added along the way.

The Setup

Here’s a simplified version of how I started things:

let headX = 10; // Snake starting position
let headY = 10;

let appleX = 5; // Food starting position
let appleY = 5;

Whenever the snake eats an apple, I increase the tail and score, then generate a new food location:

function checkAppleCollision() {
if (appleX === headX && appleY === headY) {
generateApplePosition();
tailLength++;
score++;
}
}

The Problem “Too Much Recursion” Error

This is the original function I wrote to spawn new apples:

function generateApplePosition() {
let collisionDetect = false;
let newAppleX = Math.floor(Math.random() * tileCount);
let newAppleY = Math.floor(Math.random() * tileCount);

for (let i = 0; i < snakeTail.length; i++) {
let segment = snakeTail[i];
if (newAppleX === segment.x && newAppleY === segment.y) {
collisionDetect = true;
}
}

while (collisionDetect === true) {
generateApplePosition(); // Problem line
}

appleX = newAppleX;
appleY = newAppleY;
}

After a few successful apple pickups, I started getting a “Too much recursion” error. What went wrong

What’s Causing It

The culprit was this piece of logic:

while (collisionDetect === true) {
generateApplePosition(); // Recursive call
}

It’s supposed to regenerate the apple’s position if it spawns on the snake’s tail. But the issue is that once a collision is detected, the function recursively calls itself without resetting the state. Every new recursive call creates another layer on the call stack. If this keeps happening especially when the snake gets long it eventually crashes the browser with a stack overflow.

The Fix Replace Recursion with a Loop

Here’s how I fixed the logic using a safe and efficient while loop instead:

function generateApplePosition() {
let validPosition = false;

while (!validPosition) {
let newAppleX = Math.floor(Math.random() * tileCount);
let newAppleY = Math.floor(Math.random() * tileCount);

let collision = false;

for (let i = 0; i < snakeTail.length; i++) {
let segment = snakeTail[i];
if (
newAppleX === segment.x &&
newAppleY === segment.y
) {
collision = true;
break;
}
}

if (newAppleX === headX && newAppleY === headY) {
collision = true; // Also prevent spawning on the snake head
}

if (!collision) {
validPosition = true;
appleX = newAppleX;
appleY = newAppleY;
}
}
}

Why This Works

  • No recursion = no risk of exceeding the call stack.
  • The while loop keeps trying until a safe location is found.
  • Efficient: uses break to stop the search once a collision is detected.

Bonus Feature I Added for Practice

After fixing the bug, I decided to expand the game with a few practice features. These are great exercises if you’re learning JavaScript and want to play with basic game mechanics.

Scoreboard Display

Simple way to track the score visually:

function drawScoreboard() {
ctx.fillStyle = "white";
ctx.font = "20px Arial";
ctx.fillText("Score: " + score, 10, 20);
}

Call this function inside your main game loop to update the score display.

Prevent Apple from Spawning on the Snake’s Head

I added this extra check to generateApplePosition:

if (newAppleX === headX && newAppleY === headY) {
collision = true;
}

Limit Tail Growth

If the snake becomes too long, things can get a bit hectic. So I added an optional max length:

const maxTailLength = 50;
if (tailLength < maxTailLength) {
tailLength++;
}

Bonus Apple Every 5 Points

I added logic to spawn a special bonus apple:

if (score > 0 && score % 5 === 0) {
spawnBonusApple();
}

function spawnBonusApple() {
// Add special apple with double score or a timer
}

Final Thought

This small project turned out to be a great opportunity to revisit JavaScript fundamentals like recursion, loops, and basic collision detection. The “too much recursion” error was a good reminder that recursion isn’t always the best choice especially when you’re not controlling the depth carefully.

If you’re working on a Snake Game or something similar, I highly recommend:

  • Using loops over recursion for repeated logic.
  • Thinking about edge cases (like overlapping game elements).
  • Gradually adding new features to stretch your skills.

Related blog posts