Updated 20/08/2025
Update - the game is now working and can be run on a mobile device at https://freeonlineretrogames.com/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 | // The game speed is directly tied to the screen's refresh rate |
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 | class FlappyBird { |
Next, I updated my gameLoop
function to calculate the delta time. requestAnimationFrame
helpfully provides the current timestamp, which makes this calculation easy.
1 | gameLoop(currentTime) { |
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 | // In the updateBird function |
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.