Mandelbrot Renderer Update

Since my recent blog post on my Mandelbrot set renderer using JavaScript and Canvas I’ve been made aware of an extremely useful method to add image data to a canvas context; the putImageData() method. Thanks to Kevin Pickell for pointing this out, he made use of it in his own Mandelbrot renderer.

So I decided to update my renderer to use putImageData as it’s much quicker than drawing each pixel straight onto the canvas. Usage is simple:

1
2
3
4
5
6
7
8
9
10
11
//Grab our canvas and context
var canvasID = document.getElementById("canvasID");
var context = canvasID.getContext('2d');

//Create an empty image data object
var imageDataObject = context.createImageData(width, height);

//Add our image data to the object
imageDataObject.data = imageData;
//Draw our image
context.putImageData(imageDataObject, 0, 0);

I found the way the imageDataObject expected data quite surprising at first; I think I was expecting an array of objects or an array of arrays, one for each pixel, but it expects a single large array with the red, green, blue and alpha value of each pixel in the canvas:

1
2
3
4
5
//Empty array
var imageData = [];
//For each pixel (top left to bottom right moving horizontally)
imageData.push(red, green, blue, alpha);
//Result: [r1, g1, b1, a1, r2, g2, b2, a2, r3....]

The advantage this method has is you calculate all your image data first, then pass it to the canvas. Before I was calculating a whole vertical row and drawing onto the canvas, before moving onto the next. Not at all ideal when you are repeating the process 600+ times.

Updating the non Web Workers version improved the render time by around 20 – 25 seconds! See the v2 version here. Unfortunately there were issues rotating the canvas after using putImageData(), no matter what I tried, the rotate() method seemed to be ignored, so I removed it. Maybe it’s a limitation of the method, but i’m still looking for a solution.

I had another surprise when adding the new draw method to the version using Web Workers (not fully functional). Since the worker simply needs to process the image data then pass it back to the window I assumed there wouldn’t be an issue; but that wasn’t to be. When letting the worker thread do the calculations it was taking around 8 seconds (initial render) to do this, where as in the main window it was taking less than 1 second. The time increased even more as you zoomed in:

  • Initial render: ~8000ms
  • +1 zoom: ~12000ms
  • +2 zoom: ~20000ms
  • +3 zoom: didn’t render

I have yet to find a solution to this issue; I’m guessing it may be some sort of worker thread priority or the way worker threads handle nested loops with calculations. Maybe a calculation rounding issue? If anyone has any ideas leave a comment.