DEV Community

loading...
Cover image for Mandelbrot Set in JS

Mandelbrot Set in JS

foqc profile image Fabian Quijosaca Updated on ・5 min read

In this entry I want to share with you, a little of my knowledge to show you how to plot a Mandelbrot Set without any library just JS, so go ahead!.

Note: If you are not familiar with the mathematical terms used here, I recommend you to read about complex numbers and fractals.

First of all, What a heck is a Mandelbrot Set?, according to mathworld:

A Mandelbrot Set, is used to refer both to a general class of fractal sets and to a particular instance of such a set. In general, a Mandelbrot set marks the set of points in the complex plane such that the corresponding Julia set is connected and not computable.

In a nutshell, a mandelbrot set is one of the most beautiful and famous fractal, that is defined by the set of complex numbers.

In the next picture you can see what I mean.
Alt Text
Figure 1: Mandelbrot set - black and white

In this entry you will learn how to plot in JS, the previous image.

But before, let's see a little more theory about how this set works.

Definition

As we mention before, the mandelbrot set is defined in the set of complex numbers c for which the function,
mandlebrot set
does not diverge when iterated from z = 0.

Below, the previous equation is broken down:

As you can see, the Mandelbrot set is obtained from the quadratic recursive equation where, z and c represents a complex number.

To represent visually this set we need to find, for each point c
of a part of the complex plane, if Zn is bounded. When the modulus of Zn is greater than 2, means that the number does not belong to Mandelbrot set, otherwise it is part of the set. The number of iterations to reach a modulus greater than 2 can be used to determine the color to use when we are plotting.

A complex plane can be represented in a normal 2D plane, where the X axis, will represent the real number, meanwhile the Y axis will represent the imaginary number, for instance, x = 3; y= 5i.

For a better understanding please refer to this video.

Plot of the Mandelbrot Set

Finally, after understand a bit of the Mandelbrot equation, it's time to implement it:
mandlebrot set

const MAX_ITERATION = 80
function mandelbrot(c) {
    let z = { x: 0, y: 0 }, n = 0, p, d;
    do {
        p = {
            x: Math.pow(z.x, 2) - Math.pow(z.y, 2),
            y: 2 * z.x * z.y
        }
        z = {
            x: p.x + c.x,
            y: p.y + c.y
        }
        d = Math.sqrt(Math.pow(z.x, 2) + Math.pow(z.y, 2))
        n += 1
    } while (d <= 2 && n < MAX_ITERATION)
    return [n, d <= 2]
}
Enter fullscreen mode Exit fullscreen mode
  1. The mandelbrot function receives a complex number, it has real and imaginary part, here we are representing those components as x and y.
  2. The p variable contains the computing of the square of z, which is also a complex number, please refer to this, to understand how basic operations such as addition, subtraction and multiplication works in complex numbers.
  3. z is recalculated and now it contains the addition of the received complex number (c) and the previous z.
  4. d is the modulus of the new complex number (z), computed before.
  5. Add one when each iteration is completed (n += 1).
  6. The whole process is repeated while the modulus of z belongs to Mandelbrot set and the iteration number is less than 80.
  7. Finally, the function returns a tuple, with the iteration number that it took to reach a modulus greater than 2, and whether the complex number passed to it, belongs to Mandelbrot set.

And that's it!, we have implemented a mandelbrot equation.
Now is time to plot.

var canvas = document.getElementById('myCanvas')
var ctx = canvas.getContext('2d')

const WIDTH = window.innerWidth
const HEIGHT = window.innerHeight
ctx.canvas.width = WIDTH
ctx.canvas.height = HEIGHT

const REAL_SET = { start: -2, end: 1 }
const IMAGINARY_SET = { start: -1, end: 1 }

const colors = new Array(16).fill(0).map((_, i) => i === 0 ? '#000' : `#${((1 << 24) * Math.random() | 0).toString(16)}`)

function draw() {
    for (let i = 0; i < WIDTH; i++) {
        for (let j = 0; j < HEIGHT; j++) {
            complex = {
                x: REAL_SET.start + (i / WIDTH) * (REAL_SET.end - REAL_SET.start),
                y: IMAGINARY_SET.start + (j / HEIGHT) * (IMAGINARY_SET.end - IMAGINARY_SET.start)
            }

            const [m, isMandelbrotSet] = mandelbrot(complex)
            ctx.fillStyle = colors[isMandelbrotSet ? 0 : (m % colors.length - 1) + 1]
            ctx.fillRect(i, j, 1, 1)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. In the first lines, the canvas element is found and its context, then the window width and height is assigned to the canvas.
  2. REAL_SET constant represent the real numbers of Mandlebrot set, as you saw the Figure 1 of this entry, the domain in x axis goes from -2 to 1.
  3. IMAGINARY_SET represent, the imaginary numbers in y axis, the domain in y axis goes from -1 to 1.
  4. colors, store a list of 16 random hexadecimal colors.
  5. draw function:
    • The entire width and height of the canvas are iterated.
    • The "complex" variable store a complex number; x and y, are calculated getting a relative value, of the width and height of the canvas, plus the values ​​that belong to the mandelbrot set.
    • mandelbrot function is called
    • A color is assigned to canvas pixel, if the complex number passed to mandelbrot function belongs to set, the black color is chosen, otherwise the color depends of the number of iteration made by mandelbrot function.
    • A pixel (rectangle of 1 width and height) is plotted in the position i, j.

That's all, we've finished, it was so easy! isn't it?

If we run the code, it shows the next figures.
Alt Text
Figure 2. Mandelbrot set, example 1.

Alt Text
Figure 3. Mandelbrot set, example 2.

Alt Text
Figure 4. Mandelbrot set, example 3.

Alt Text
Figure 5. Mandelbrot set, example 4.

Alt Text
Figure 6. Mandelbrot set, example 5.

I hope you've enjoyed!.

Live demo
Full code

By the way, It's my first blog, written entirely in English (I'm not a native speaker), maybe you've already realized, so sorry for the misspellings!, please If you have any recommendation, or comment you can leave in the comments section.

Next steps

Our fractal is plotted, so in the next entry I will implement the zoom, when a part of the image is clicked.

Stay safe and thanks for reading!

Discussion (2)

pic
Editor guide
Collapse
a08m31 profile image
A08M31

Thanks for the great post Fabian it was really helpful.
I had a quick question :
at line 12 where you are defining an array of random colors, why is 1 shifted 24 times?
more specifically I would like to know how does that bitwise logic really work?
I tried 16 and 32 and got different color schemes but I couldn't figure out why.
I know it probably sounds like a stupid question but I am totally new to bitwise operations :)

Collapse
foqc profile image
Fabian Quijosaca Author

A08M31, the only stupid question is the one that you don't ask.

When you play with values ​​8, 16, 24 ..., it generates a set of colors; with 16 bits, obviously the number of colors generated is small (65,535 colors) unlike the 24 bits which can represent 16,777,216 different colors (by the way the human eye can only distinguish 10 million colors), if you change it to 32 bits it probably can not be represented on certain computers. Thanks to your comment I decided to analyze this line of code in depth, and I realized that sometimes it does not generate a valid color, because the code used here is copy/paste ( :D ), from stackoverflow, the solution to this problem is in the following link, codegolf.stackexchange.com/questio...

Do not forget to read the next part of this blog, because you will find more interesting things!

Thank You!