Canvas code snippets

Add-ons using the techniques described in this document are considered a legacy technology in Firefox. Don't use these techniques to develop new add-ons. Use WebExtensions instead. If you maintain an add-on which uses the techniques described here, consider migrating it to use WebExtensions.

Starting from Firefox 53, no new legacy add-ons will be accepted on addons.mozilla.org (AMO) for desktop Firefox and Firefox for Android.

Starting from Firefox 57, WebExtensions will be the only supported extension type. Desktop Firefox and Firefox for Android will not load other extension types.

Even before Firefox 57, changes coming up in the Firefox platform will break many legacy extensions. These changes include multiprocess Firefox (e10s), sandboxing, and multiple content processes. Legacy extensions that are affected by these changes should migrate to WebExtensions if they can. See the "Compatibility Milestones" document for more information.

A wiki page containing resources, migration paths, office hours, and more, is available to help developers transition to the new technologies.

For general information about using <canvas> see the canvas topic page.

Code usable from Web content

Getting the number of pixels of a certain color in a canvas

The following function will return the number of pixels in a canvas that have the RGB color of r, g and b. This can be very useful to compare for example if a user has painted over another area as explained in this blog post.

function getpixelamount(canvas, r, g, b) {
  var cx = canvas.getContext('2d');
  var pixels = cx.getImageData(0, 0, canvas.width, canvas.height);
  var all = pixels.data.length;
  var amount = 0;
  for (i = 0; i < all; i += 4) {
    if (pixels.data[i] === r &&
        pixels.data[i + 1] === g &&
        pixels.data[i + 2] === b) {
      amount++;
    }
  }
  return amount;
};

Getting the color of a pixel in a canvas

This following snippet returns an object with the RGBA values of the pixel at position x and y of the canvas. This can be used to determine if the mouse cursor is inside a certain shape or not.

function getpixelcolour(canvas, x, y) {
  var cx = canvas.getContext('2d');
  var pixel = cx.getImageData(x, y, 1, 1);
  return {
    r: pixel.data[0],
    g: pixel.data[1],
    b: pixel.data[2],
    a: pixel.data[3]
  };
}

Chaining methods

This class provides jQuery-style chained access to 2D context methods and properties.

function Canvas2DContext(canvas) {
  if (typeof canvas === 'string') {
    canvas = document.getElementById(canvas);
  }
  if (!(this instanceof Canvas2DContext)) {
    return new Canvas2DContext(canvas);
  }
  this.context = this.ctx = canvas.getContext('2d');
  if (!Canvas2DContext.prototype.arc) {
    Canvas2DContext.setup.call(this, this.ctx);
  }
}
Canvas2DContext.setup = function() {
  var methods = ['arc', 'arcTo', 'beginPath', 'bezierCurveTo', 'clearRect', 'clip',
    'closePath', 'drawImage', 'fill', 'fillRect', 'fillText', 'lineTo', 'moveTo',
    'quadraticCurveTo', 'rect', 'restore', 'rotate', 'save', 'scale', 'setTransform',
    'stroke', 'strokeRect', 'strokeText', 'transform', 'translate'];
  var getterMethods = ['createPattern', 'drawFocusRing', 'isPointInPath', 'measureText', // drawFocusRing not currently supported
    // The following might instead be wrapped to be able to chain their child objects
    'createImageData', 'createLinearGradient',
    'createRadialGradient', 'getImageData', 'putImageData'
  ];
  var props = ['canvas', 'fillStyle', 'font', 'globalAlpha', 'globalCompositeOperation',
    'lineCap', 'lineJoin', 'lineWidth', 'miterLimit', 'shadowOffsetX', 'shadowOffsetY',
    'shadowBlur', 'shadowColor', 'strokeStyle', 'textAlign', 'textBaseline'];
  for (let m of methods) {
    let method = m;
    Canvas2DContext.prototype[method] = function() {
      this.ctx[method].apply(this.ctx, arguments);
      return this;
    };
  }
  for (let m of getterMethods) {
    let method = m;
    Canvas2DContext.prototype[method] = function() {
      return this.ctx[method].apply(this.ctx, arguments);
    };
  }
  for (let p of props) {
    let prop = p;
    Canvas2DContext.prototype[prop] = function(value) {
      if (value === undefined)
        return this.ctx[prop];
      this.ctx[prop] = value;
      return this;
    };
  }
};
var canvas = document.getElementById('canvas');
// Use context to get access to underlying context
var ctx = Canvas2DContext(canvas)
  .strokeStyle('rgb(30, 110, 210)')
  .transform(10, 3, 4, 5, 1, 0)
  .strokeRect(2, 10, 15, 20)
  .context;
// Use property name as a function (but without arguments) to get the value
var strokeStyle = Canvas2DContext(canvas)
  .strokeStyle('rgb(50, 110, 210)')
  .strokeStyle();

Code usable only from privileged code

These snippets are only useful from privileged code, such as extensions or privileged apps.

Saving a canvas image to a file

The following function accepts a canvas object and a destination file path string. The canvas is converted to a PNG file and saved to the specified location. The function returns a promise which resolves when the file has been completely saved.

function saveCanvas(canvas, path, type, options) {
    return Task.spawn(function *() {
        var reader = new FileReader;
        var blob = yield new Promise(accept => canvas.toBlob(accept, type, options));
        reader.readAsArrayBuffer(blob);
        yield new Promise(accept => { reader.onloadend = accept });
        return yield OS.File.writeAtomic(path, new Uint8Array(reader.result),
                                         { tmpPath: path + '.tmp' });
    });
}

Loading a remote page onto a canvas element

The following class first creates a hidden iframe element and attaches a listener to the frame's load event. Once the remote page has loaded, the remotePageLoaded method fires. This method gets a reference to the iframe's window and draws this window to a canvas object.

Note that this only works if you are running the page from chrome. If you try running the code as a plain webpage, you will get a 'Security error" code: "1000' error.

RemoteCanvas = function() {
    this.url = 'http://developer.mozilla.org';
};
RemoteCanvas.CANVAS_WIDTH = 300;
RemoteCanvas.CANVAS_HEIGHT = 300;
RemoteCanvas.prototype.load = function() {
    var windowWidth = window.innerWidth - 25;
    var iframe;
    iframe = document.createElement('iframe');
    iframe.id = 'test-iframe';
    iframe.height = '10px';
    iframe.width = windowWidth + 'px';
    iframe.style.visibility = 'hidden';
    iframe.src = this.url;
    // Here is where the magic happens... add a listener to the
    // frame's onload event
    iframe.addEventListener('load', this.remotePageLoaded, true);
    //append to the end of the page
    window.document.body.appendChild(iframe);
    return;    
};
RemoteCanvas.prototype.remotePageLoaded = function() {
    // Look back up the iframe by id
    var ldrFrame = document.getElementById('test-iframe');
    // Get a reference to the window object you need for the canvas
    // drawWindow method
    var remoteWindow = ldrFrame.contentWindow;
    //Draw canvas
    var canvas = document.createElement('canvas');
    canvas.style.width = RemoteCanvas.CANVAS_WIDTH + 'px';
    canvas.style.height = RemoteCanvas.CANVAS_HEIGHT + 'px';
    canvas.width = RemoteCanvas.CANVAS_WIDTH;
    canvas.height = RemoteCanvas.CANVAS_HEIGHT;
    var windowWidth = window.innerWidth - 25;
    var windowHeight = window.innerHeight;
    var ctx = canvas.getContext('2d');
    ctx.clearRect(0, 0,
                  RemoteCanvas.CANVAS_WIDTH,
                  RemoteCanvas.CANVAS_HEIGHT);
    ctx.save();
    ctx.scale(RemoteCanvas.CANVAS_WIDTH / windowWidth,
              RemoteCanvas.CANVAS_HEIGHT / windowHeight);
    ctx.drawWindow(remoteWindow,
                   0, 0,
                   windowWidth, windowHeight,
                   'rgb(255, 255, 255)');
    ctx.restore();
};

Usage:

var remoteCanvas = new RemoteCanvas();
remoteCanvas.load();

Convert image files to base64 strings

The following code gets a remote image and converts its content to Data URI scheme.

var canvas = document.createElement('canvas');
var ctxt = canvas.getContext('2d');
function loadImageFile(url, callback) {
  var image = new Image();
  image.src = url;
  return new Promise((accept, reject) => {
    image.onload = accept;
    image.onerror = reject;
  }).then(accept => {
    canvas.width = this.width;
    canvas.height = this.height;
    ctxt.clearRect(0, 0, this.width, this.height);
    ctxt.drawImage(this, 0, 0);
    accept(canvas.toDataURL());
  });
}

Usage:

loadImageFile('myimage.jpg').then(string64 => { alert(string64); });

If you want to get instead the base64 content of a local file using the file <input> element, you must use the FileReader object.

Document Tags and Contributors

 Last updated by: bunnybooboo,