A Novel Mandelbrot Generation?

David Banks
4 min readApr 22, 2023

--

Animation of successive iterations

Introduction

Writing Visualising Mandelbrot’s Equation made me realise that there’s must be interesting things happening inside the Mandelbrot set. Usually the Mandelbrot fractal is interested in the numbers outside the set and the set itself is just coloured black.

One thing that interested me was looking at the rate of change of the angle of the iterations, I had no idea what to expect, but it was interesting and easy enough to create a visualisation of it.

Method

Using the same mandelbrot function I used in Mandelbrot in an SVG and Visualising Mandelbrot’s Equation and adding a theta function to the Complex class I created in Complex Numbers in JavaScript:

/***
* Calculate angle portion of the polar representation.
* <code>tan(θ) = imaginary/real</code>
* @returns {number}
*/
theta() {
return Math.atan(this.imaginary / this.real);
}

I coloured each pixel by its value of θ (converted to degrees) by adjusting its hue. Hue is perfect for this as it is a colour wheel whereby specific colours are referenced by their angle around the wheel.

To create all the pixels was a slow process so I created all the pixels first and coloured them white, then I just changed their colour as needed.

Results

Results from the zeroth iteration

The initial result just confirmed things were working, this is just map of the θ for each pixel. It gets more interesting.

Looking at the 1st iteration imediately shows more.

1st iteration
2nd Iteration
4th Iteration
8th Iteration

At this point we can start seeing the shape of the Mandelbrot fractal appearing.

16th Iteration
32nd Iteration

By now we can clearly tell that what we’re seeing is a Mandelbrot fractal.

The emerging black pixels are actually a lucky accident. The numbers on the diverging coordinates get very large and exceed Number.MAX_VALUE which causes the value of the hue to be NaN. I didn’t plan this, but it is helping right now.

By the 1000th iteration we have something that looks very definite.

1000th Iteration

Pushing much further than this is more than the browser can cope with and causes it to crash.

Code

import { NS } from '../../../helpers/svg.js';
import { Complex } from '../../../helpers/Complex.js';
import { mandelbrot } from '../../fractals.js';

const SVG = document.getElementById('mandelbrot-gradients');

const PIXEL_SIZE = 0.01;

const VIEW_BOX = (() => {
const { x, y, width, height } = SVG.viewBox.baseVal;
return {
x,
y,
width,
height,
};
})();

const DEGREES_COEFFICIENT = 360 / (2 * Math.PI);

const pixelGroup = document.createElementNS(NS, 'g');
pixelGroup.id = 'pixel-group';

(function generatePixels() {
for (let i = VIEW_BOX.x; i < VIEW_BOX.x + VIEW_BOX.width; i += PIXEL_SIZE) {
const rowPixelGroup = document.createElementNS(NS, 'g');
rowPixelGroup.id = `pixels-row-${i}`;

for (
let j = VIEW_BOX.y;
j < VIEW_BOX.y + VIEW_BOX.height;
j += PIXEL_SIZE
) {
const pixel = document.createElementNS(NS, 'rect');
pixel.setAttribute('x', i.toString());
pixel.setAttribute('y', j.toString());
pixel.setAttribute('width', PIXEL_SIZE.toString());
pixel.setAttribute('height', PIXEL_SIZE.toString());
pixel.setAttribute('stroke-width', '0');
pixel.setAttribute('fill', `hsl(0, 100%, 100%`);

pixel.complex = new Complex(i, j);

rowPixelGroup.appendChild(pixel);
}
pixelGroup.appendChild(rowPixelGroup);
}
})();
SVG.appendChild(pixelGroup);

let results = new Array(pixelGroup.childElementCount);

function calculateMandelbrot(iterations) {
console.log(`Calculating Mandelbrot: ${iterations}`);
for (let i = 0; i < pixelGroup.childElementCount; i++) {
const rowResults = [];
const rowGroup = pixelGroup.children[i];
for (let j = 0; j < rowGroup.childElementCount; j++) {
const pixel = rowGroup.children[j];
pixel.mandelbrot = mandelbrot(pixel.complex, iterations);
}
results.push(rowResults);
}
}

function paintFractal(iteration) {
console.log(`Painting fractal: ${iteration}`);
for (let i = 0; i < pixelGroup.childElementCount; i++) {
const rowGroup = pixelGroup.children[i];
for (let j = 0; j < rowGroup.childElementCount; j++) {
const pixel = rowGroup.children[j];

const hue =
pixel.mandelbrot[iteration].theta() * DEGREES_COEFFICIENT;
pixel.setAttribute('fill', `hsl(${hue}, 100%, 50%`);
}
}
}

const iterations = 1000;
calculateMandelbrot(iterations);

paintFractal(iterations - 1);
console.log('Done');

Conclusion

I want to push this further, so I think I need to move to a different technology. Maybe moving to a canvas will let me improve the speed and resolution but I’m finding that the time to calculate the values is actually taking longer than I had expected.

Memory profile for 1000 iterations.

One possibility is to move to WebGl. I assume (i.e. I don’t know) that WebGl delegates computation to the GPU which could mean that combining the mandelbrot calculations with the graphic computation as one pass would be a major speed boost. Failing this, maybe trying to do this in a web page is just a bad idea.

I haven’t seen this as an analysis of the Mandelbrot set before so it might be a novel technique. If I’m wrong at all then please let me know.

I’ll experiment and let you know.

Thanks for reading.

--

--