Skip to main content

Mandelbrot Set with p5.js

The Mandelbrot set, named after Benoit Mandelbrot, is the set of complex numbers $c$ for which the function $f_c(z)=z^2+c$ does not diverge to infinity when iterated from $z=0$.

I’m using the pseudocode from the Wikipedia article Plotting algorithms for the Mandelbrot set as base, and applying different coloring algorithms.

Grayscale

Mandelbrot set with an escape radius of 2.0, and a maximum of 40 iterations.

In this version we’re mapping the number of iterations for each pixel (in the range [0, maxIterations]) to a brightness value (in the range [0, 255]).

With maxIterations set to 40 we get very visible bands. We can increase that value to be closer to 255 to reduce banding, but then computing each pixel becomes more expensive. Also, the edges of the set become too sharp, and the resulting image is less appealing overall.

Histogram

Mandelbrot set with an escape radius of 2.0, a maximum of 40 iterations, and a palette of 64 colors.

In this version we’re mapping the number of iterations for each pixel (in the range [0, maxIterations]) to a color in a certain palette (in the range [0, numColors]).

The palette tries to mimic the default one from UltraFractal:

gradient.addColorStop(0.0,    "rgb(  0,   7, 100)");
gradient.addColorStop(0.16,   "rgb( 32, 107, 203)");
gradient.addColorStop(0.42,   "rgb(237, 255, 255)");
gradient.addColorStop(0.6425, "rgb(255, 170,   0)");
gradient.addColorStop(0.8575, "rgb(  0,   2,   0)");

With maxIterations set to 40 the bands are as obvious as in the previous version. Increasing the size of the palette with numColors doesn’t make things any better, because we’re still dealing with a stair-step function.

Continuous

Mandelbrot set with an escape radius of 2.0, a maximum of 40 iterations, 8 extra iterations for quick escapes, and a palette of 2048 colors.

This version uses an algorithm known as normalized iteration count to smooth the transition between colors. The formula for obtaining that fractional count is:

$$ n + 1 - {\frac {log(log(|z_n|))}{log(2)}} $$

Applying some logarithm rules (change of base, power), we can turn it into what you see in my code:

$$ n + 1 - log_2({\frac {log(x^2+y^2)}{2}}) $$

With maxIterations set to 40, extraIterations set to 8, and numColors set to 2048, we get very impressive results.

Resources

See also