๐ How I Fixed the Delta Time Bug in Limn Engine (And How You Can Too)
A story about frame rates, Chromebooks, and the one bug that broke everything
๐ Introduction
Let me tell you about a bug that haunted Limn Engine for months.
Limn Engine was developed on a Chromebook โ but nobody knows that. It was tested on a Toshiba laptop with 4GB of RAM, and everything worked perfectly. Enemies moved at the right speed. The player controlled smoothly. The game ran at a crisp 60 frames per second.
But when I tested the same game on a Tecno Pop 4 (a low-end Android phone with 1GB of RAM and a weak processor), something strange happened.
The enemies moved at completely different speeds.
Sometimes they were too fast. Sometimes they were too slow. Sometimes they started fast and then slowed down for no reason.
The game was broken โ and the problem was hiding in plain sight: delta time.
๐ฏ What Is Delta Time (And Why Should You Care?)
Delta time (often written as dt) is the amount of time that has passed since the last frame. It's measured in seconds.
| Frame Rate | Delta Time |
|---|---|
| 60 FPS | ~0.0167 seconds |
| 30 FPS | ~0.0333 seconds |
| 15 FPS | ~0.0667 seconds |
Here's the rule: If you multiply your movement speed by delta time, your game runs at the same speed on all devices.
// โ
CORRECT: Frame-rate independent
player.x += 200 * dt; // Moves 200 pixels per second
// โ WRONG: Frame-rate dependent
player.x += 4; // Moves 4 pixels per frame (speed varies by FPS)
Without delta time, your game will run at different speeds on different devices. A fast computer (144 FPS) will feel like the game is in fast-forward, while a slow device (15 FPS) will feel like slow-motion.
๐ The Bug: How I Broke Delta Time
Limn Engine already had delta time โ but it was calculated using 1 / fps every second. Here's what the original code looked like:
setInterval(() => {
display.deltaTime = 1 / display.fps;
refresh = true;
}, 1000);
This worked... sort of. But there was a huge problem:
The FPS counter resets every second. At the start of each second, the FPS starts at 0 and builds up to 60. So for the first fraction of a second, 1 / fps was huge โ sometimes 0.5 or even 1.0. This made components move incredibly fast at the beginning of each second, then suddenly slow down to normal speed.
It was like the game was having a seizure every second.
๐ง The Fix: Two Different Approaches
I tested two different fixes:
Approach 1: The 1 / fps Method (Original)
setInterval(() => {
display.deltaTime = 1 / display.fps;
}, 1000);
Problem: FPS builds from 0 to 60, causing dt to spike.
Approach 2: The Pygame Method (What I Ultimately Used)
function ani(time) {
display.deltaTime = display.timeFromAllFrames - display.timeFromPreviousFrames;
display.timeFromPreviousFrames = time;
// Convert to seconds
update(dt = display.deltaTime / 1000);
}
๐งช The Test: HP Laptop (Behind My Sister's Back)
I tested both approaches on my sister's HP laptop when she wasn't looking (don't worry, she still doesn't know). Here's what I found:
| Method | Result |
|---|---|
1 / fps |
Worked, but had spikes at the start of each second |
| Pygame-style (difference between frames) | Worked perfectly, smooth movement |
The Pygame-style method gave similar results but was more reliable. It didn't spike at the beginning of each second because it calculates the actual time between frames, not an average over a second.
๐ The Results: Before and After
| Device | Before Fix | After Fix |
|---|---|---|
| Chromebook (dev machine) | Jagged movement | Perfect speed |
| Toshiba Laptop (4GB RAM) | Game too fast at start | Perfect speed |
| HP Laptop (tested behind sister's back) | Spikes every second | Perfect speed |
| Tecno Pop 4 (1GB RAM) | Game too slow | Perfect speed |
| Desktop (144 FPS) | Unplayable | Perfect speed |
The same game now runs at the same speed on every device.
๐ The Code
You can see the final working code here:
๐ Limn Engine Source Code (epic.js)
Look for the ani() function to see the delta time fix in action.
๐ The Documentation
For more information about Limn Engine, check out the complete documentation:
๐ Limn Engine Documentation on DEV.to
๐ Found a Bug? Report It Here!
If you encounter any issues with Limn Engine, please report them on GitHub:
๐ https://github.com/terracodes004/limn-engine-doc/issues
๐ How to Report a Bug (Helpful Template)
When you open an issue, please include:
- A clear description of the bug.
- Steps to reproduce it.
- What you expected to happen vs. what actually happened.
- Screenshots or error logs (if applicable).
- Which version of Limn Engine you're using.
Here's a template you can use:
**Bug Description:**
[Describe the problem clearly]
**Steps to Reproduce:**
1. Do this...
2. Then do this...
3. Observe the bug...
**Expected Behavior:**
[What should have happened]
**Actual Behavior:**
[What actually happened]
**Screenshots/Logs:**
[Attach if available]
**Limn Engine Version:**
[v0.x.x]
**Device/OS:**
[e.g., Tecno Pop 4, Android 10]
๐ก What I Learned
1 / fpsis not the best way to calculate delta time. It works, but it's unreliable at the start of each second.The Pygame method is better. Calculating the difference between frames is more accurate and doesn't spike.
Test on low-end devices. If it works on a Tecno Pop 4, it'll work on almost anything.
Test on different laptops. I tested on a Chromebook, Toshiba, and HP โ and they all showed different issues.
The dual-renderer saved me. Even with the delta time bug, the game was running at 60 FPS on the Toshiba laptop. If I had been using a simpler engine, the performance would have been terrible too.
Develop on a Chromebook. Nobody knows Limn Engine was developed on a Chromebook โ and now you know my secret.
๐ฏ The One-Line Summary
"I fixed a delta time bug that made my game spike every second โ and you can too." ๐ฎ๐
Draw your game into existence โ one frame at a time. ๐ฎ๐
Top comments (1)
Sorry for the story mix-up. I will explain everything later