Run details:
Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:105.0) Gecko/20100101 Firefox/105.0
Firefox 105
Mac OS X 10.15
Desktop
2 years ago
Test name Executions per second
fillRect/concat 107.3 Ops/sec
fillRect/join 109.0 Ops/sec
1×1 Image Data 655.8 Ops/sec
fillRect/template literal 109.4 Ops/sec
getImageData/putImageData 363.0 Ops/sec
getImageData/putImageData - set px.rgba 344.7 Ops/sec
getImageData/putImageData - set [px.r, px.g, px.b, px.a] 338.2 Ops/sec
getImageData/putImageData - set - no getImageData 285.3 Ops/sec
HTML Preparation code:
AخA
 
1
<canvas id="c" width="800" height="600"></canvas>
Script Preparation code:
 
$c = document.getElementById('c');
$ctx = $c.getContext('2d');
$ctx.clearRect(0, 0, 800, 300);
$px = $ctx.createImageData(1, 1);
$id = $ctx.getImageData(0, 0, 800, 600);
$pxls = [];
for (var i = 0; i < 1000000; ++i) {
    const pxl = {
        x: Math.random() * 800 << 0,
        y: Math.random() * 300 << 0,
        r: Math.random() * 255 << 0,
        g: Math.random() * 255 << 0,
        b: Math.random() * 255 << 0,
        a: Math.random() * 128 << 0 + 128
    };
    pxl.rgba = [pxl.r, pxl.g, pxl.b, pxl.a];
    $pxls.push(pxl);
};
$i = 0;
$iterations = 5000;
/*
At 1500+ $iterations the putimagedata/getimagedata starts to win and at 5000 it slaughters.  Below that the other methods win
So it depends on how many you plan on drawing each frame
*/
Tests:
  • fillRect/concat

     
    for (var i=$iterations;i--;){
    var px = $pxls[$i++ % 10000];
    $ctx.fillStyle = 'rgba(' + px.r + ',' + px.g + ',' + px.b + ',' + (px.a / 255) + ')';
    $ctx.fillRect(px.x, px.y, 1, 1);
    }
  • fillRect/join

     
    for (var i=$iterations;i--;){
    var px = $pxls[$i++ % 10000];
    $ctx.fillStyle = 'rgba(' + [px.r,px.g,px.b,px.a/255].join() + ')';
    $ctx.fillRect(px.x, px.y, 1, 1);
    }
  • 1×1 Image Data

     
    for (var i=$iterations;i--;){
    var px=$pxls[$i++ % 10000], d=$px.data;
    d[0] = px.r;
    d[1] = px.g;
    d[2] = px.b;
    d[3] = px.a;
    $ctx.putImageData($px, px.x, px.y);
    }
  • fillRect/template literal

     
    for (var i=$iterations;i--;){
    var px = $pxls[$i++ % 10000];
    $ctx.fillStyle = `rgba(${px.r},${px.g},${px.b},${px.a/255}})`;
    $ctx.fillRect(px.x, px.y, 1, 1);
    }
  • getImageData/putImageData

    x
     
    var canvasWidth = $c.width;
    var canvasHeight = $c.height;
    var id = $ctx.getImageData(0, 0, canvasWidth, canvasHeight);
    var pixels = id.data;
    for (var i=$iterations;i--;){
      var px = $pxls[$i++ % 10000];
      var off = (px.y * canvasWidth + px.x) * 4;
      pixels[off] = px.r;
      pixels[off + 1] = px.g;
      pixels[off + 2] = px.b;
      pixels[off + 3] = px.a;
    }
    $ctx.putImageData(id, 0, 0);
  • getImageData/putImageData - set px.rgba

     
    var canvasWidth = $c.width;
    var canvasHeight = $c.height;
    var id = $ctx.getImageData(0, 0, canvasWidth, canvasHeight);
    var pixels = id.data;
    for (var i=$iterations;i--;){
      var px = $pxls[$i++ % 10000];
      var off = (px.y * canvasWidth + px.x) * 4;
      pixels.set(px.rgba,off);
    }
    $ctx.putImageData(id, 0, 0);
  • getImageData/putImageData - set [px.r, px.g, px.b, px.a]

     
    var canvasWidth = $c.width;
    var canvasHeight = $c.height;
    var id = $ctx.getImageData(0, 0, canvasWidth, canvasHeight);
    var pixels = id.data;
    for (var i=$iterations;i--;){
      var px = $pxls[$i++ % 10000];
      var off = (px.y * canvasWidth + px.x) * 4;
      pixels.set([px.r,px.g,px.b,px.a],off);
    }
    $ctx.putImageData(id, 0, 0);
  • getImageData/putImageData - set - no getImageData

     
    var canvasWidth = $c.width;
    var canvasHeight = $c.height;
    //var id = $ctx.getImageData(0, 0, canvasWidth, canvasHeight);
    var pixels = $id.data;
    pixels.fill();
    for (var i=$iterations;i--;){
      var px = $pxls[$i++ % 10000];
      var off = (px.y * canvasWidth + px.x) * 4;
      pixels.set(px.rgba,off);
    }
    $ctx.putImageData($id, 0, 0);