itch.io is community of indie game creators and players

Devlogs

๐Ÿš€ Devlog #10: Velocity Puck's Major Frame-Rate Performance Bug Fix (Animation Loop Issue)

Velocity Puck
A browser game made in HTML5

Hi everyone!

I wanted to share an important update about a performance bug in Velocity Puck that some players reported:
after restarting the game, the frame rate would suddenly shoot way higher than normal, making the gameplay feel unusually fast or inconsistent.

This turned out to be caused by something sneaky happening behind the scenes:
multiple animation loops were running at the same time.

Let me explain what was going on — in simple terms — and how I fixed it.


โ— What Was the Problem?

Velocity Puck uses a function called animate() that updates the 3D scene, moves objects, checks collisions, etc.
To run this function 60 times per second, the game used this setup:

animationInterval = setInterval(animate, 1000 / 60); 

However… every time you restarted the game, a new interval was created.
The issue was:

โœ”๏ธ The old interval wasnโ€™t always cleared

โœ”๏ธ The game would keep adding more and more animation loops

โœ”๏ธ And each loop increased the frame rate even further

So after a few restarts, instead of having one animate loop, the game might be running 3, 5, or even 10 loops at once.

That caused the game to speed up like crazy.


๐Ÿ” How I Found the Issue

Inside your original index.html, the animation system worked like this:

โŒ Problematic pattern

let animationInterval = null;  
function startGame() {     
    gameStarted = true;     
    animationInterval = setInterval(animate, 1000 / 60);  
}  
function endGame() {     
    gameStarted = false;     
    clearInterval(animationInterval); 
} 

At first glance, this looks fine.
But the restart flow looked like:

  1. Player finishes a match โ†’ endGame()

  2. Game Over screen โ†’ restartGame()

  3. startGame() is called again โ†’ new animation loop created

  4. But sometimes the old interval was not cleared in time or at all

The result:
Two animate loops running at the same time… then three… then four…
Each one stacking more draw calls onto the browser.


๐Ÿ› ๏ธ The Fix

To solve the issue, I implemented three key improvements:

โœ… 1. Always clear the animation loop before starting a new one

Before starting the game, we now force-stop any existing interval:

function startGameLoop() {     
    if (animationInterval !== null) {         
        clearInterval(animationInterval);         
        animationInterval = null;     
    }     
    animationInterval = setInterval(animate, 1000 / 60); 
} 

This guarantees only one active loop at all times.

โœ… 2. Cleanup all Three.js objects when exiting or restarting

Three.js stores objects in GPU memory.
If an old scene stays in memory, it can slow things down.

Now, before starting a new game:

function cleanupScene(scene, renderer) {     
    scene.traverse(obj => {         
        if (obj.geometry) obj.geometry.dispose();         
        if (obj.material) {             
            if (obj.material.map) obj.material.map.dispose();             
            obj.material.dispose();         
        }     
    });      
    renderer.dispose(); 
} 

This makes sure nothing carries over to the next run.

โœ… 3. Remove if (!gameStarted) return; from animate() and rely on proper loop control

This line was causing confusion:

if (!gameStarted) return; 

It stopped the logic, but not the interval itself.
Now the loop stops correctly by clearing the interval.


๐ŸŽ‰ The Result

The frame rate no longer skyrockets after restarting, and the game behaves consistently no matter how many times the player returns to the main menu or starts a new match.

Restarting is now:

  • Smooth

  • Memory-safe

  • Predictable

  • FPS-stable

And the Jungle Map clipping fix from the previous patch now looks great combined with this performance update.


๐Ÿ“ฆ Example of the Final Loop Control System

Hereโ€™s the improved, robust version now used in Velocity Puck:

let animationInterval = null;  
function startGame() {     
    stopGameLoop();      // Make sure no loop exists     
    initializeScene();   // Load models, lighting, etc.     
    startGameLoop(); 
}  
function startGameLoop() {     
    stopGameLoop();     
    animationInterval = setInterval(animate, 1000 / 60); 
}  
function stopGameLoop() {     
    if (animationInterval) {         
        clearInterval(animationInterval);         
        animationInterval = null;     
    } 
}  
function restartGame() {     
    cleanupScene(scene, renderer);     
    startGame(); 
} 

๐Ÿงก Thanks for the feedback!

This bug was only discovered because players reported unusual behavior — so thank you!
Your reports help make Velocity Puck better with each update.

More improvements are coming, and new maps + characters are currently in development ๐Ÿ’โšก

Leave a comment