Question about timing in Phaser

I have a question about timing in Phaser using scene timers. Let’s say I have two events, A and B that should be 4 seconds apart. The code looks like this

this.scene.events.emit('eventA');
this.scene.time.addEvent({delay: 4000, loop: false, 
             callback: (event) => {this.scene.events.emit('eventB')}
                         )

so the event should take 4000 ms (4s). When I added the timers using performance.now() after eventA and before eventB for a couple of times, I found that most of those were exactly 4s down to ms. However, other instances were off by around 16ms in either direction. I know that Phaser uses 60 fps making each frame last about 16.67ms. What happens was that a frame was missing (or added) in those instances where I got a difference of ~16ms.

My question: is there a way to force the update function to use a higher resolution timing when I create an event?

Thanks all for the help and support developing my games in Phaser!

The event is aligned to the game loop, which runs using requestAnimationFrame. This means it’s synchronized with the monitor’s refresh rate - which, in your case, is 60 FPS. To get control over the time step, you’d have to force the game loop to use setTimeout instead. However, it wouldn’t help in your particular case because setTimeout is not always accurate. Realistically, I’ve seen short timeouts going off after 10 ms - not even the 4 that the spec describes.

If you want an event to take exactly 4 seconds, use setTimeout directly. Note that it would be separate from your game loop, so it wouldn’t respect slowdown (if the CPU can’t keep up with the framerate) or pauses (if the tab is not focused). Is there a particular reason why you need the countdown to be so accurate? If your events are only used to manipulate data inside the game, the player wouldn’t be able to see the changes anyway because the canvas updates at 60 FPS (or whatever the monitor’s refresh rate is).

Thanks for your detailed reply.

I want my timing to be as accurate as possible because I am using external recording devices that record physiological measures of the player, and those need to be in sync with the events in the game. Things might not need to be a sub-millisecond accuracy but I do need the events to be accurate and synchronous within an error rate of few milliseconds (I am fine with 20 actually but I just want to optimize) – so that I can use the markers I am creating with performance.now() to split the physiological data as accurately as possible (they both start at 0 ms).

My only problem with using setTimeout as is to dispatch events is that it actually drifts a little by little over time. This drift may compound to 1s or more after a few minutes.

The thing I liked about forcing the game loop from the config by adding this code:

  fps: {
    forceSetTimeOut: true
  },

is that now at least I am getting a nice variation around the target value (4000ms) with a mean deviation of 10ms and a standard deviation of 7ms – without a single value being exactly 4000 ms. It is not a big deal here as long as it does not compound (or drift) as the game goes on. I can simply correct those times after the game is played by subtracting the mean deviation from all the timestamps.

I would appreciate it if you, or other readers, have other performance tips.

1 Like

For those who might find this useful, I finally found a solution to maximize timing accuracy. First, add this to your config:

  render: {
    powerPreference: 'high-performance'
  },

Please disregard my previous reply about setting forceSetTimeOut to true.

Then, for your timing, you should tell Phaser a time that is slightly less than your target. For example, I want to wait for 4000ms, but I actually put 3995ms instead. This is just a trick as Phaser among many other programs don’t seem to take the very last frame into consideration.

I ran 32 iterations (of actual gameplay, and not just in for-loops), and here are the recorded times using performance.now():

array([4000., 3998., 4000., 4000., 4000., 4000., 4000., 3999., 4000.,
       3999., 4000., 4000., 4000., 4000., 4000., 4000., 3999., 4000.,
       4000., 4001., 3998., 4001., 4000., 4001., 4000., 4000., 4001.,
       4000., 4000., 3999., 3999., 3999.])

The average and standard deviation of deviations are both less than 1 ms.

One thing I haven’t tested is if this behavior will persist throughout the game. I’d assume it does but if it doesn’t I will update this reply.

3 Likes