How to Generate Mandelbrot Set Fractals in JavaScript

  Programming

After reading about the Mandelbrot set, I’ve found myself a brand new hobby: generating fractal images. If you don’t know what the Mandelbrot set is, you can think of it as just a bunch of complex numbers. Those numbers, however, can be used to generate colorful, alien patterns that can truly leave you spellbound. In fact, images of the Mandelbrot set can be so detailed that you can stare at them for hours and still keep finding interesting and unique patterns.

In this tutorial, I’ll show you how to generate the Mandelbrot set using just JavaScript, and plot it on a canvas.

Setup

Create a new HTML document and add the following boilerplate code to it:

<!DOCTYPE html>
<html>
    <body>
        <script>
        </script>
    </body>
</html>

If it’s not obvious already, we’ll be writing all the JavaScript code inside the <script> tag.

Create and initialize a new canvas now, whose width and height are both 600 px.

(function() {
    // Create Canvas
    var myCanvas = document.createElement("canvas");
    myCanvas.width=600;
    myCanvas.height=600;
    document.body.appendChild(myCanvas);
    var ctx = myCanvas.getContext("2d");

    // Start drawing
})();

A Little Bit of Theory

At this point, we can start working on the Mandelbrot set. It’s a very simple set that consists of complex numbers. How do you decide if a complex number belongs to the set? Well, you first try using it in the following equation:

Z = C*C + C // C is the complex number

You then use the result in the following equation:

Z' = Z * Z + C // Z is the previous result and C is the
                  // same complex number

You repeat the above step again:

Z'' = Z' * Z' + C // Z' is the previous result and C is the
                  // same complex number

Keep repeating the above steps dozens of times. If the result never exceeds a small fixed value, say 5 or 10, you can assume that the complex number belongs to the Mandelbrot set. Conversely, if the result exceeds the fixed value, and becomes a large number, you can be sure that the complex number doesn’t belong to the set.

Implementation

You might now be wondering how do I choose the value of the complex number? Well, that depends on what you want to do with the Mandelbrot set. If you want to generate a fractal image with it, you can use the X and Y co-ordinates of points on your HTML5 canvas as the real and imaginary components of the complex number:

C = X + iY

However, you can’t use the X and Y co-ordinates directly. I mean, you can, but you’d end up with an image of the Mandelbrot set that is so far zoomed out that it would barely be visible. By dividing the coordinates by a large value, you zoom in to the image.

Furthermore, you can move around the fractal by subtracting small numbers from the coordinates.

Accordingly, you can add the following code to your file:

function checkIfBelongsToMandelbrotSet(x, y) {
    // TO DO
}

var magnificationFactor = 600;
var panX = 0;
var panY = 0;
for(var x=0; x < myCanvas.width; x++) {
   for(var y=0; y < myCanvas.height; y++) {
       var belongsToSet = 
            checkIfBelongsToMandelbrotSet(x/magnificationFactor - panX, 
                                          y/magnificationFactor - panY);
       if(belongsToSet) {
            ctx.fillRect(x,y, 1,1); // Draw a black pixel
       }                
   } 
}

Of course, we must define the checkIfBelongsToMandelbrotSet() function now, which returns a true or false value. Here’s the code you can add to it:

var realComponentOfResult = x;
var imaginaryComponentOfResult = y;

for(var i = 0; i < 10; i++) {
     // Calculate the real and imaginary components of the result
     // separately
     var tempRealComponent = realComponentOfResult * realComponentOfResult
                             - imaginaryComponentOfResult * imaginaryComponentOfResult
                             + x;

     var tempImaginaryComponent = 2 * realComponentOfResult * imaginaryComponentOfResult
                             + y;

     realComponentOfResult = tempRealComponent;
     imaginaryComponentOfResult = tempImaginaryComponent;
}

if (realComponentOfResult * imaginaryComponentOfResult < 5)
    return true; // In the Mandelbrot set

return false; // Not in the set

If you run the code now, you’d see something like this:

Mandelbrot set in HTML5 canvas

A very weird image indeed, but not unexpected. That’s what the Mandelbrot set fractal looks like when you are zoomed in. By changing the values of the magnificationFactor to 200, the panX to 2 and panY to 1.5, you’ll see this:

Mandelbrot set in HTML5 canvas

If you’ve seen the Mandelbrot set fractal earlier, the above image might seem familiar. But there’s still a lot of noise in it. By increasing the number of iterations in the checkIfBelongsToMandelbrotSet() function to about 100, you’ll generate the following image, which looks much better:

Mandelbrot set in HTML5 canvas

How about adding some color now? To do so, we must change our checkIfBelongsToMandelbrotSet() function such that it returns a number instead of a boolean value. For now, let’s return a number based on the number of iterations it takes to exceed our limit:

function checkIfBelongsToMandelbrotSet(x,y) {
    var realComponentOfResult = x;
    var imaginaryComponentOfResult = y;
    var maxIterations = 100;
    for(var i = 0; i < maxIterations; i++) {
         var tempRealComponent = realComponentOfResult * realComponentOfResult
                                 - imaginaryComponentOfResult * imaginaryComponentOfResult
                                 + x;
         var tempImaginaryComponent = 2 * realComponentOfResult * imaginaryComponentOfResult
                                 + y;
         realComponentOfResult = tempRealComponent;
         imaginaryComponentOfResult = tempImaginaryComponent;

         // Return a number as a percentage
         if(realComponentOfResult * imaginaryComponentOfResult > 5) 
            return (i/maxIterations * 100);
    }
    return 0;   // Return zero if in set        
}          

You must also update the rendering logic to use the color. The easiest way to do so is to use the CSS hsl() function:

if(belongsToSet == 0) {
    ctx.fillStyle = '#000';
    ctx.fillRect(x,y, 1,1); // Draw a black pixel
} else {
    ctx.fillStyle = 'hsl(0, 100%, ' + belongsToSet + '%)';
    ctx.fillRect(x,y, 1,1); // Draw a colorful pixel
}

If you refresh your page, you should this now:

Mandelbrot set in HTML5 canvas

If that’s not impressive enough, try zooming in with the following values:

var magnificationFactor = 2900;
var panX = 0.7;
var panY = 0.6;

You’ll get something like this:

Mandelbrot set in HTML5 canvas

You can keep zooming in until you hit the precision limit of JavaScript’s variables. However, you must remember to increase the number of iterations in the checkIfBelongsToMandelbrotSet() function proportionately. Otherwise, the patterns in the fractal won’t be sharp and distinct. Here’s the same fractal as the above image, at 300 iterations.

Mandelbrot set in HTML5 canvas

Conclusion

You now know how to create fractal images with the Mandelbrot set using just JavaScript and the HTML5 Canvas API. If you found anything confusing, or wrong, please tell me by leaving a comment.

If you found this article useful, please share it with your friends and colleagues!