Register for this year’s #ChromeDevSummit happening on Nov. 11-12 in San Francisco to learn about the latest features and tools coming to the Web. Request an invite on the Chrome Dev Summit 2019 website

OffscreenCanvas — Speed up Your Canvas Operations with a Web Worker

Tl;dr; Now you can render your graphics off the main thread with OffscreenCanvas!

Canvas is a popular way
of drawing all kinds of graphics on the screen and an entry point to the world of WebGL.
It can be used to draw shapes, images, run animations, or even display and process video content.
It is often used to create beautiful user experiences in media-rich web applications and
online games.

It is scriptable, which means that the content drawn on canvas can be created programmatically,
e.g., in JavaScript. This gives canvas great flexibility.

At the same time, in modern websites, script execution is one of the most frequent
sources of user responsiveness issues.
Because canvas logic and rendering happens on the same thread as user interaction,
the (sometimes heavy) computations involved in animations can harm the app’s real
and perceived performance.

Until now, canvas drawing capabilities were tied to the <canvas> element,
which meant it was directly depending on the DOM. OffscreenCanvas, as the name implies,
decouples the DOM and the Canvas API by moving it off-screen.

Thanks to this decoupling, rendering of OffscreenCanvas is fully detached from the DOM and
therefore offers some speed improvements over the regular canvas as there is no synchronization
between the two.
What is more, though, is that it can be used in a Web Worker, even though there is no
DOM available. This enables all kinds of interesting use cases.

Use OffscreenCanvas in a worker

Workers
are the web’s version of threads — they allow you to run tasks in the background.
Moving some of your scripting to a worker gives your app more headroom to perform user-critical
tasks on the main thread. Until now, there was no way to use the Canvas API in a worker, as there
is no DOM available.

OffscreenCanvas does not depend on the DOM, so it can be used instead. Here I use OffscreenCanvas
to calculate a gradient color in a worker:

Unblock main thread

It gets more interesting when moving heavy calculation to a worker allows you to free up
significant resources on the main thread. We can use the transferControlToOffscreen
method to mirror the regular canvas to an OffscreenCanvas instance. Operations applied to
OffscreenCanvas will be rendered on the source canvas automatically.

OffscreenCanvas is transferable.
Apart from specifying it as a field in the message, you need to also pass it as a second argument
in postMessage (a transfer) so that it can be used in the worker context.

In the example below, the “heavy calculation” happens when the color theme is changing — it should
take a few milliseconds even on a fast desktop. You can choose to run animations on the main thread
or in the worker. In case of the main thread, you cannot interact with the button while the heavy
task is running — the thread is blocked. In case of the worker, there is no impact on
UI responsiveness.

It works the other way too: the busy main thread does not influence the animation running on
a worker. You can use this feature to avoid visual jank and guarantee a smooth animation
despite main thread traffic:

The one gotcha here is that Three.js expects canvas to have a style.width and style.height property.
OffscreenCanvas, as fully detached from DOM, does not have it, so you need to provide it yourself,
either by stubbing it out or providing logic that ties these values to the original
canvas dimensions.

Bear in mind that some of the DOM related APIs are not readily available in a worker, so if you
want to use more advanced Three.js features like textures, you might need more workarounds.
For some ideas on how to start experimenting with these, take a look at the
video from Google I/O 2017.

Examples in this video use the deprecated commit() call. Use worker.requestAnimationFrame instead.

Conclusion

If you’re making heavy use of the graphical capabilities of canvas, OffscreenCanvas can positively
influence your app’s performance. Making canvas rendering contexts available to workers increases
parallelism in web applications and makes better use of multi-core systems.

OffscreenCanvas is available without a flag in Chrome 69. It is also
in development in Firefox.
Because its API is very aligned with the regular canvas element, you can easily feature-detect it
and use it as a progressive enhancement, without breaking existing app or library logic.
It offers performance advantage in all cases where the graphics and animations are not tied closely
to the DOM surrounding the canvas.