2D Canvas Wide Color Gamut Example

In this page we demonstrate how to use a 2D canvas to display wide color gamut colors.

Enabling Support

In order to enable wide color gamut support on Chrome, download Chrome Canary. Because this support is experimental, you will need to explicitly enable this. To do this, navigate to chrome://flags and enable "Experimental Web Platform features".

Alternatively, this can be enabled at the command line with the following argument.

  $ /Applications/Google\ Chrome\ Canary.app/Contents/MacOS/Google\ Chrome\ Canary \
      --enable-experimental-web-platform-features

An Example 8-Bit Rec. 2020 Canvas

To create a wide color gamut canvas, set the colorSpace key to the desired color space. The current list of color spaces supported is srgb, display-p3, and rec2020. The following code will create an 8-bit canvas with the Rec. 2020 color space.

  ctx = document.getElementById('myRec2020Canvas').getContext('2d', {colorSpace:'rec2020'});

We can see if this has succeeded by querying the colorSpace member of the context attributes.

  var attributes = ctx.getContextAttributes();
  var succeeded = 'colorSpace' in attributes &&
                  'rec2020' == attributes.colorSpace;

The only ways to get wide color gamut content on the screen is from images and from ImageData. External images are automatically displayed in their full wide color gamut. In the below example, we draw this image at the top of the canvas. If displayed using wide color gamut, the image shows the WebKit logo. Otherwise, no logo is visible.

  var image = new Image();
  image.onload = function() { ctx.drawImage(image, 0, 0, 256, 256); }
  image.src = "https://webkit.org/blog-files/color-gamut/Webkit-logo-P3.png";

The below canvas is the result of the above code.

Using ImageData

As mentioned earlier, the other way to get wide color gamut content on a canvas is by using ImageData. Similarly to getContext, the ImageData constructors and the functions createImageData and getImageData now accepts a settings argument in which a color space may be specified.

  var imageDataSrgb = new ImageData(32, 32, {colorSpace:'srgb'});
  var imageDataRec2020 = new ImageData(32, 32, {colorSpace:'rec2020'});

When we draw these to the canvas using putImageData, they will be converted appropriately. This code sets both ImageDatas to be the maximum red-ness that they can express.

  for (i = 0; i < imageData.data.length; i += 4) {
    imageDataSrgb.data[i+0]    = imageDataSrgb.data[i+3]    = 255;
    imageDataRec2020.data[i+0] = imageDataRec2020.data[i+3] = 255;
  }
  ctx.putImageData(imageDataSrgb, 0, 0);
  ctx.putImageData(imageDataRec2020, 32, 0);

The below canvas is the result of the above code.

High Bit Depth Canvases and ImageData

The data in a canvas or an ImageData is no longer restricted to being 8 bits. For canvas, the options are uint8 and float16. For ImageData, the options are uint8, uint16, and float32.

The following code will create a 16-bit floating-point backed canvas and color it sRGB-red.

  ctx = document.getElementById('myHalfFloatCanvas').getContext(
      '2d', {colorSpace:'srgb', pixelFormat:'float16'});
  ctx.fillStyle = 'red';
  ctx.fillRect(0, 0, 96, 96);

The following code will create a 16-bit unsigned integer Rec. 2020 ImageData from a pre-existing Uint16Array.

  var data = new Uint16Array(4*64*64);
  for (i = 0; i < data.length; i += 4)
    data[i+0] = data[i+3] = 65535;
  var imageData = new ImageData(
      data, 64, 64, {colorSpace:'rec2020', storageFormat:'uint16'});
  ctx.putImageData(imageData, 16, 16);

The below canvas is the result of the above code. Note that despite the fact that the colorSpace of the canvas is srgb, we are able to display the input color, up to the fidelity of the monitor. This is because canvas values outside of the range of [0, 1] are not clamped.