Use web worker and setInterval to power a Javascript game loop

- 5 mins
Hello1
ViewSource code

Testing our game loop when tab is inactive

As mentioned in the previous post requestAnimationFrame stops running when the tab becomes inactive, setInterval shares this behaviour as well. This is okay if we design the game in a way that accommodates this, ie if it's a single player game one approach would be to just pause the game whenever the tab becomes inactive. However we'll see as this course progresses that we will need a specific way to handle pausing the game to work with our game loop properly as it progresses.

Despite this it may be desired that our game continues running regardless if it is active or not. This will take more resources and should be a conscious design choice but it may be required in a multiplayer peer 2 peer game where the clients are responsible for sending their respective game state to one another.

In this tutorial we will look at a method that allows us to have a more resilient game loop, in the event that the tab becomes inactive our game loop will continue to run. Before we make a change though lets test the current game loops behaviour.

Start the game and note where the square's current position is on the screen, switch tabs in your browser, wait a few seconds then switch back. When you switch back to the game where is the square? It will be exactly where it was when you switched tabs.

Introducing Javascript web workers

To enable our game loop to continue running we can use Javascript web workers. Web workers are processes that run on a different thread to our main application in the background.

Normally web workers are used to run some script in the background without interfering with the UI and they will continue to run when the tab becomes inactive. We can use web workers in a slightly different way to trigger the game loop.

Add a new web-worker.ts file to the src directory with the below code.

setInterval(() => {
  postMessage('test');
}, 1000 / 60.0);

This sets up a web worker that when run will trigger our familiar setInterval loop to post an arbitrary message approx 60 times per second which we will use.

In GameRuntime's setup method as the following code.

let intervalWorker = new Worker('web-worker.js');
intervalWorker.onmessage = GameRuntime.loop

Make sure to comment out the original game loop line in the setup method.

// setInterval(GameRuntime.loop, game1UpdateLoopsPerSecond)

We use the onmessage event handler to fire our game loop whenever the web worker posts a message.

Run the game and checkout the results. Try changing tabs and confirm that the square keeps moving on it's merry way!

Note: At the moment for me the setInterval timeout in the worker seems to be ignored, however the web worker is called at approximately 60 frames per second which is good enough for now.

For our draw loop we will continue to use requestAnimationFrame as the UI will update to reflect the current state immediately when we switch the tab back to active. We could use a web worker for the draw UI loop but it would be fairly resource intensive and unnecessary to update a canvas that no one is looking at.

Conclusion

As mentioned before it should be a conscious choice to continue running code when a tab is inactive, if not handled correctly it can lead to performance issues in the browser.

Also if the web worker called postMessage more or less frequently our square would speed up or slow down. We want to ideally prevent this change in behaviour which is a great segue onto our next tutorial variable game loops.