Performance issue when drawing on large canvas after loadPixels()

I encountered a performance issue (especially on Chrome) when drawing on a large canvas after executing loadPixels().

Here’s an example

The example code:

const canvasSize = 2048; // try 4096
const density = 1;
let diameter = 1;

function setup() {
  createCanvas(canvasSize, canvasSize, P2D);
  pixelDensity(density);
  drawCircles();
  /**
   * NOTE: Comment this out to increase performance (especially on Chrome).
   */
  loadPixels();
}

function draw() {
  const x = floor(random()*width);
  const y = floor(random()*height);
  const ix = (y * width + x) * density * 4;
  fill(pixels[ix], pixels[ix+1], pixels[ix+2]);
  circle(0, 0, diameter);
  diameter += 8;
  if (diameter > width) {
    noLoop();
  }
}

function drawCircles() {
  for (let i = 0; i < 100; i++) {
    const r = floor(random()*200)+40;
    const g = floor(random()*200)+40;
    const b = floor(random()*200)+40;
    fill(r, g, b, 80);
    const x = floor(random()*canvasSize);
    const y = floor(random()*canvasSize);
    const d = floor(random()*canvasSize);
    circle(x, y, d);
  }
}

If you execute the example in Safari there seems to be no performance issues (with canvasSize = 2048). However, executing the same example in Chrome, drawing is extremely slow.

If you increase canvasSize to 4096 (which is what I would like to use), the example still works fine in Safari up to a certain point, after which the browser starts having issues drawing the large circle. In Chrome it becomes even slower.

Would be interesting to know why this occurs and why Safari outperforms Chrome (I would expect the opposite).

1 Like

Dave Pagurek recommended using an offscreen graphics buffer. That worked for me.

I’ve created a modified version of the example above which uses a graphics buffer.

Here’s the fixed example

The fixed example code:

const canvasSize = 4096;
const density = 1;
let diameter = 1;
let pg;

function setup() {
  createCanvas(canvasSize, canvasSize, P2D);
  pixelDensity(density);
  pg = createGraphics(canvasSize, canvasSize);
  pg.pixelDensity(density);
  drawCircles();
  pg.loadPixels();
}

function draw() {
  const x = floor(random()*width);
  const y = floor(random()*height);
  const ix = (y * width + x) * density * 4;
  fill(pg.pixels[ix], pg.pixels[ix+1], pg.pixels[ix+2]);
  circle(0, 0, diameter);
  diameter += 8;
  if (diameter > width) {
    noLoop();
  }
}

function drawCircles() {
  for (let i = 0; i < 100; i++) {
    const r = floor(random()*200)+40;
    const g = floor(random()*200)+40;
    const b = floor(random()*200)+40;
    pg.fill(r, g, b, 80);
    const x = floor(random()*canvasSize);
    const y = floor(random()*canvasSize);
    const d = floor(random()*canvasSize);
    pg.circle(x, y, d);
  }
  image(pg, 0, 0, canvasSize, canvasSize);
}

Thanks Dave!

1 Like