This page looks best with JavaScript enabled

Improve Graphics Performance with OffscreenCanvas & Web Workers

 ·  ☕ 4 min read  ·  ✍️ Iskander Samatov

improve-performance-offscreencanvas-web-workers


Canvas API is used to run graphics, animations, and other exciting things on your website. But running these operations can be resource-intensive and slow down the responsiveness of the user interface.

Canvas operations used to be executed strictly on the main thread since they relied on the canvas HTML element, which required access to the DOM. Fortunately, that’s no longer the case. In this tutorial, we’ll examine how OffScreenCanvas and Web Workers can help us improve canvas performance.

What is Web Workers API

Web workers are the means of achieving parallel execution in the browser. With them, you can run scripts in background threads.

Web workers allow you to run JavaScript code in the background, separately from the main UI thread. You can use them to improve your app’s performance and run tasks even if the browser is not focused on it.

The main thread and web workers can communicate by setting up event handlers and posting messages using the postMessage method.

To learn more about web workers, check out this documentation page .

What is OffscreenCanvas

Web workers are great but have limited access to browser APIs. For example, you cannot directly manipulate DOM elements from within Web workers - it is only possible from the main thread.

Fortunately, that is where OffScreenCanvas comes in! The OffscreenCanvas interface is a canvas that can be rendered off-screen, separating it from the DOM.

OffScreenCanvas is a transferable object, which means you can safely pass it to the web workers and run canvas operations on background threads. In order to send an OffScreenCanvas instance to a worker, you use postMessage.

The OffScreenCanvas can be synced with a regular canvas using the transferControlToOffscreen function. Once you do that, the operations performed on the OffScreenCanvas will automatically transfer to the original canvas elements.

How To Set Up OffScreenCanvas with web workers

Now let’s see how we can use OffScreenCanvas with web workers to boost performance.

Our project will have an index.js file that kicks off the animation. Here’s what it looks like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
function runAnimation() {
  const canvas = document
    .getElementById("myCanvas")
    .transferControlToOffscreen();
  const worker = new Worker("script.js");
  worker.postMessage({ canvas }, [canvas]);
  worker.postMessage({});
}
runAnimation();

Pretty straightforward: when the script loads, it gets a reference to the <canvas/> element in our index.html file and creates an instance of the OffScreenCanvas using the transferControlToOffscreen function.

As you can see, it also initiates our worker and calls the postMessage on it twice: the first time to transfer the OffScreenCanvas instance to the worker and the second time to trigger the animation.

Now let’s take a look at what our worker script looks like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
let canvas;
function animate(ctx) {
  ctx.beginPath();
  ctx.moveTo(100, 0);
  ctx.lineTo(100, 200);
  ctx.stroke();
  function drawCircle(x) {
    ctx.beginPath();
    ctx.arc(x, 100, 10, 0, 2 * Math.PI);
    ctx.fillStyle = "red";
    ctx.fill();
  }
  let x = 0;
  setInterval(function () {
    ctx.clearRect(0, 0, 600, 600);
    drawCircle(x % 200);
    x++;
  }, 25);
}

onmessage = function (e) {
  console.log("Worker received a message from the main thread");
  const { canvas: canvasMessage } = e.data;
  if (canvasMessage) {
    canvas = canvasMessage;
  } else {
    const context = canvas.getContext("2d");
    animate(context);
  }
};

Our worker script sets up a listener using the onmessage function. When our worker receives messages from the main thread, it assigns canvasMessage to its global canvas variable. The second time it receives a message, it calls the animate function to run the canvas animation.

Now, even if our main thread was to become busy and less responsive, that would not affect our worker’s execution. It would still run the animation and update our canvas. And vice-versa, the animation running in the web workers will not stop the UI thread from smoothly responding to user input.

Using it with other libraries

Another nice thing about OffScreenCanvas is that you can use it to improve the performance of third-party graphics libraries that rely on canvas, like three.js . Since the OffscreenCanvas API is generally compatible with the regular canvas element.

But one thing to be aware of is that OffScreen canvas doesn’t have style.width and style.height properties that some of those libraries might need. In those cases, you can inject those properties in your OffScreenCanvas instance before passing it to the library.

Conclusion

In this post, we covered how to use OffScreenCanvas to improve the performance of your canvas operations. OffScreenCanvas can be a great way to avoid some of these problems, and it’s simple to use.

To see the full code for this tutorial check out this codesandbox .

If you’d like to get more web development, React and TypeScript tips consider following me on Twitter, where I share things as I learn them.
Happy coding!

Share on

Software Development Tutorials
WRITTEN BY
Iskander Samatov
The best up-to-date tutorials on React, JavaScript and web development.