This example is a Mandelbrot Set Generation Playground making use of Atomic Operations, enabling multiple threads to modify a single piece of data in parallel without expensive memory copies.
The Mandelbrot set is a collection of complex numbers that forms a fractal, named after mathematician Benoît B. Mandelbrot. It is defined by iterating the function 𝑓𝑐(𝑧)=𝑧2+𝑐fc (z)=z2 +c, where 𝑧z starts at zero, and 𝑐c is a complex parameter. The set consists of all complex values of 𝑐c for which the sequence does not escape to infinity. Visually, the Mandelbrot set displays an intricate, infinitely detailed boundary with self-similar patterns, making it a key example in the study of chaotic systems and complex dynamics.
Provided example will generate 10 frames of the mandelbrot set at different zoom levels using the number of selected threads, and display the average rendering time of all 10 frames.
You can play around with the different settings to see how those inpact the performance and results of the simulation, the full source code is available on the github repository page.
const mandelbrotOperator = function () {
const sharedArray = params.sharedArray;
const maxIterations = params.maxIterations;
const width = params.width;
const height = params.height;
const xMin = params.xMin;
const xMax = params.xMax;
const yMin = params.yMin;
const yMax = params.yMax;
const map = (value, low1, high1, low2, high2) => low2 + (high2 - low2) * (value - low1) / (high1 - low1);
const start = params.index.start || 0;
const end = params.index.end;
for (let index = start; index <= end; index++) {
const x = index % width;
const y = Math.floor(index / width);
let a = map(x, 0, width, xMin, xMax);
let b = map(y, 0, height, yMin, yMax);
let n = 0;
let z = 0;
let zi = 0;
while (n < maxIterations) {
let aa = z * z - zi * zi + a;
let bb = 2 * z * zi + b;
z = aa;
zi = bb;
if (Math.abs(z + zi) > 16) {
break;
}
n++;
}
Atomics.store(sharedArray, index, n);
}
};
function renderNextZoom(mandelbrotSet) {
if (currentZoom < zoomLevels.length && isRendering) {
const params = {
sharedArray: mandelbrotSet,
maxIterations: maxIterations,
width: width,
height: height,
dataType: 'Uint32',
threads: parseInt(document.getElementById('threadCount').value, 10) || 1 // Number of threads
};
const startTime = performance.now();
hamsters.promise(params, mandelbrotOperator).then((newMandelbrotSet) => {
if (!isRendering) return; // Exit if rendering was stopped
drawMandelbrot(newMandelbrotSet);
currentZoom++;
setTimeout(() => renderNextZoom(newMandelbrotSet), 4); // Wait for 4ms before rendering the next zoom level
});
}
}
function drawMandelbrot(mandelbrotSet) {
loadPixels();
for (let index = 0; index < width * height; index++) {
let n = mandelbrotSet[index];
let x = index % width;
let y = Math.floor(index / width);
let brightness = map(n, 0, maxIterations, 0, 255);
let pixelIndex = (x + y * width) * 4;
pixels[pixelIndex] = brightness; // Red
pixels[pixelIndex + 1] = brightness; // Green
pixels[pixelIndex + 2] = brightness; // Blue
pixels[pixelIndex + 3] = 255; // Alpha
}
updatePixels();
}
Frame: 0/10
Average Rendering Time: 0 ms