1

I'm trying to make a web-based game and I use setInterval to update the game (positions, collision checks, etc...) 60 times a second.

It works fine in most browsers, but for some reason Firefox sometimes throttles it to about 35 times a second. Yes, sometimes. If DevTools is closed, it will throttle, but if you open DevTools and start recording performance, it jumps to 60 and will stay that way until DevTools is closed.

If you're using Firefox, run the snippet and try it:

var cycles = 0;
setInterval(()=>{
  cycles++;
}, 1000/60);
setInterval(()=>{
  console.log("Update cycles per second: %i", cycles);
  cycles = 0;
}, 1000);

Is this a bug? If not, why does this happen and how can I work around it?

Edit: I'm not using setInterval to render. For that I use requestAnimationFrame. I'm using setInterval to update the game at a constant time interval, because requestAnimationFrame fires before repainting, and is heavily dependent on the refresh rate of the user's screen.

D. Pardal
  • 6,173
  • 1
  • 17
  • 37
  • Yes, don't use seInterval for animations. Use requestAnnimatonFrame instead. https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame – Keith Dec 22 '19 at 18:20
  • *`heavily dependent on the refresh rate of the user's screen`* - why would you bother updating faster than that? – vsync Dec 22 '19 at 18:39
  • 1
    Related: https://softwareengineering.stackexchange.com/q/175934/206310 – vsync Dec 22 '19 at 18:41
  • I could also update the game as many times as needed in the `requestAnimationFrame` callback (maybe I will), but then the user inputs would be delayed until the next repaint. For example, a square moves 1 px/update while a key is being held down. If the user has a 30Hz screen, holding the key down for 1/6th of a second would either not move the square at all (if `requestAnimationFrame` didn't fire while the key was being held down) or move it 2px (if it did). (Btw sorry for my bad English) – D. Pardal Dec 22 '19 at 18:48

1 Answers1

0

You should not rely on timers being accurate. They often aren't. Instead, rely on time being accurate:

 var cycles = 0, start = Date.now();
 setInterval(() => {
  cycles = Math.floor((Date.now() - start) / 1000 * 60);
 }, 1000/60);

That said, requestAnimationFrame is probably a better choice for your use than setInterval, as it gets called on every rerender.

Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
  • @D.Pardal well 30fps is enough for that, isn't it? Also have a look at this [Wikipedia section](https://en.wikipedia.org/wiki/Collision_detection#Video_games) – Jonas Wilms Dec 22 '19 at 19:17