JavaScript Canvas - OffscreenCanvas
Written by Ian Elliot   
Monday, 16 September 2019
Article Index
JavaScript Canvas - OffscreenCanvas
Using A Worker
transferControlToOffScreen

OffscreenCanvas is relatively new but it can make your graphics smooth without blocking the UI thread. In this extract from a chapter in my new book on JavaScript Graphics we look at how it works.

Now available as a paperback or ebook from Amazon.

JavaScript Bitmap Graphics
With Canvas

largecover360

 

Contents

  1. JavaScript Graphics
  2. Getting Started With Canvas
  3. Drawing Paths
      Extract: Basic Paths
      Extract: SVG Paths
      Extract: Bezier Curves
  4. Stroke and Fill
      Extract: Stroke Properties 
      Extract: Fill and Holes
      Extract: Gradient & Pattern Fills
  5. Transformations
      Extract: Transformations
      Extract: Custom Coordinates 
      Extract  Graphics State
  6. Text
      Extract: Text, Typography & SVG 
      Extract: Unicode
  7. Clipping, Compositing and Effects
      Extract: Clipping & Basic Compositing
  8. Generating Bitmaps
      Extract:  Introduction To Bitmaps
      Extract :  Animation 
  9. WebWorkers & OffscreenCanvas
      Extract: Web Workers
      Extract: OffscreenCanvas
  10. Bit Manipulation In JavaScript
      Extract: Bit Manipulation
  11. Typed Arrays
      Extract: Typed Arrays 
  12. Files, blobs, URLs & Fetch
      Extract: Blobs & Files
      Extract: Read/Writing Local Files
      Extract: Fetch API **NEW!
  13. Image Processing
      Extract: ImageData
      Extract:The Filter API
  14. 3D WebGL
      Extract: WebGL 3D
  15. 2D WebGL
    Extract: WebGL Convolutions

<ASIN:B07XJQDS4Z>

<ASIN:1871962579>

<ASIN:1871962560>

Even if you can make your animation run fast enough, you still have the problem of the pauses that occur whenever the UI thread has to deal with something else. There are situations when your animation can freeze for a considerable time. The solution is to move the animation from the UI thread to a different thread.

Until recently there was no way that a JavaScript programmer could take advantage of the OS to schedule threads or multiple cores to implement true parallelism, but now we have web workers which implement background processing on a non-UI thread. The only problem is that a web worker cannot access the DOM and the canvas object is part of the DOM. To make it possible to implement graphics processing off the UI thread, the OffscreenCanvas object was introduced. It is still reasonably new and at the time of writing Chrome is the only browser to fully support it. While supported in Firefox, it is disabled by default and you need to set a flag to enable it, and even then it only supports the WebGL graphics context.

It is worth making clear that while OffscreenCanvas has been introduced as something to make animation smoother, almost any intensive graphics operation is better implemented as a web worker. Indeed any intensive operation of any kind is best implemented in this way.

Before we look at how to use OffscreenCanvas we need to find out the basics of using a web worker. This is explained in detail in the chapter but here is a summary:

  • Communication between the UI and worker thread is via events fired by one thread and received by the other.

  • Each thread only processes events when not occupied with running code.

  • This means that events may not be dealt with promptly.

  • Data can be transferred between threads using the event object that is made available to the event handler.

  • Data is not shared – a copy is made for the receiving thread.

  • The UI thread is generally set up to respond to events promptly, but the worker thread isn’t.

  • A graphics-oriented worker thread is the exception to the rule as it generally gives up its thread with each call to requestAnimationFrame.

Sections in book but not in this extract:

  • Basic Web Worker
  • The Trouble With Threads
  • Basic Communication Methods
  • UI Thread to Worker Thread
  • Worker Thread to UI Thread
  • Transferable Objects

OffscreenCanvas

An OffscreenCanvas is a canvas object that isn’t part of the screen display. It is simply an area of memory that you can draw into using all of the familiar methods. The fact that it isn’t part of the display, and isn’t part of the DOM, means that it can be used from the UI thread and from a worker thread. When used from the UI thread it behaves a lot like a canvas object that you haven’t added to the DOM, but it also has some additional methods.

You create an OffscreenCanvas object using:

var offCanvas=new OffscreenCanvas(width,height);

Notice that as this is not part of the DOM it doesn’t have a style width and height like a canvas object. It does have width and height properties, however. It also has a getContext method that you can use to draw on it and a pair of new methods:

  • convertToBlob() converts the image to a binary blob using the format of any of the supported graphics file types – jpg, png. For lossy compression you can also specify a quality parameter.

  • transferToImageBitmap() returns an ImageBitmap object based on the current contents of the OffscreenCanvas.

OffscreenCanvas in the UI Thread

As already mentioned, you can use an OffscreenCanvas wherever you would otherwise use a canvas object not added to the DOM and, as long as you are only targeting browsers that support OffscreenCanvas, it is sensible to use it instead. However, at the time of writing only Chrome supports it in the 2d context.

For example, the bitmap for the ball in the example at the end of the previous chapter can be created using, in the main program;

var ctx2 = new OffscreenCanvas(40, 40).getContext("2d");
var path = new Path2D();
var r = 20;
path.arc(20, 20, r, 0, 2 * Math.PI);
ctx2.fill(path);

And to make this visible you simply use, in render:

ctx.drawImage(ctx2.canvas, this.pos.x - r,
this.pos.y – r);

which is exactly how you would do it with a standard canvas object.

You can see the complete modified program at www.iopress.info.

Alternatively you could create an ImageBitmap and use it in place of the canvas:

var ballImage=ctx2.canvas.transferToImageBitmap();

You would then render the ball using:

ctx.drawImage(ballImage, this.pos.x - r, this.pos.y – r);

which is in principle faster.



Last Updated ( Monday, 16 September 2019 )