Javascript Games Time And Frames

Updated 20/08/2025

Update - the game is now working and can be run on a mobile device at https://freeonlineretrogames.com/flappybird/

flappybird

The Case of the Speedy Flappy Bird: A Tale of Time and Frames

The Case of the Speedy Flappy Bird: A Tale of Time and Frames
I recently ran into a classic head-scratcher while working on a little side project—a Flappy Bird clone built with pure HTML, CSS, and JavaScript. On my desktop, the game was perfect. The physics felt right, the bird’s flight was predictable, and I could navigate the pipes with a decent rhythm. I was feeling pretty good about it.

Then, I opened it on my phone.

The game was an absolute nightmare. The bird plummeted at an alarming speed, and the pipes whizzed by in a blur. The entire experience felt like it was playing on fast-forward. It was completely unplayable.

My first thought was, “Why is my mobile browser so much faster than my desktop?”

I had a hunch the problem was tied to the device’s refresh rate. Desktop monitors typically run at 60Hz, meaning they refresh the screen 60 times per second. Many modern smartphones, however, have high refresh rate screens, often 90Hz or 120Hz. If my game’s update loop was tied to the screen’s refresh, it would be running almost twice as fast on my phone.

A quick look at my code confirmed my suspicion. My game’s main logic was housed within a gameLoop() function, which was called by requestAnimationFrame. This is the standard way to create smooth animations in the browser, but it has one critical side effect: it synchronizes with the display’s refresh rate.

Here’s a snippet of my original, flawed code:

1
2
3
4
5
6
7
8
// The game speed is directly tied to the screen's refresh rate
gameLoop() {
this.updateBird();
this.updatePipes();
this.render();

requestAnimationFrame(() => this.gameLoop());
}

The issue here is that the functions updateBird() and updatePipes() don’t care how much time has passed since the last frame; they just update the bird’s position and the pipes’ position by a fixed amount. If the gameLoop runs more frequently, these updates happen more often, and the game appears to speed up.

The solution, as it turns out, is a fundamental concept in game development called Delta Time. Instead of moving objects by a fixed amount each frame, you calculate the time that has passed since the last frame and scale your movements accordingly. This way, whether the game is running at 60 FPS or 120 FPS, the total distance an object travels per second remains the same.

To implement this, I made a few key changes. First, I added a lastTime variable to my class to track the time of the previous frame.

1
2
3
4
5
6
7
class FlappyBird {
constructor() {
// ... other properties
this.lastTime = 0;
}
// ...
}

Next, I updated my gameLoop function to calculate the delta time. requestAnimationFrame helpfully provides the current timestamp, which makes this calculation easy.

1
2
3
4
5
6
7
8
9
10
11
12
gameLoop(currentTime) {
// Calculate delta time in seconds
const dt = (currentTime - this.lastTime) / 1000;
this.lastTime = currentTime;

// Pass delta time to the update functions
this.updateBird(dt);
this.updatePipes(dt);
this.render();

requestAnimationFrame(this.gameLoop.bind(this));
}

Finally, and most importantly, I had to modify my update functions to use this new dt value. I multiplied all my movements (gravity and pipe speed) by dt and a constant value of 60, which represents my desired baseline speed in frames per second. This made the physics and motion consistent across all devices.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// In the updateBird function
updateBird(dt) {
// The bird's velocity is now based on time
this.bird.velocity += this.bird.gravity * dt * 60;
this.bird.y += this.bird.velocity * dt * 60;
// ...
}

// In the updatePipes function
updatePipes(dt) {
for (let i = this.pipes.length - 1; i >= 0; i--) {
const pipe = this.pipes[i];
// The pipes' speed is now based on time
pipe.x -= this.pipeSpeed * dt * 60;
// ...
}
// ...
}

After these changes, my Flappy Bird game was finally playable on both my desktop and my phone. The bird’s descent was at the same pace, and the pipes moved at a consistent speed. The experience was identical, just as it should be.

The lesson here is a good one for any budding game developer: don’t let your game’s logic be tied to the frame rate. By using delta time, you can create a game that performs consistently and predictably on any device, no matter how fast or slow it is. It’s a simple change with a massive impact on the player experience.