Skip to content

Instantly share code, notes, and snippets.

@Garciat Garciat/mandelbrot.html
Last active Nov 1, 2015

Embed
What would you like to do?
<body style="margin:0"></body>
<script>
var CLICKZOOM = 3;
var NUMWORKERS = 8;
var workers = (function () {
var w = [];
for (var i = 0; i < NUMWORKERS; ++i)
w.push(new Worker('worker.js'));
return w;
})();
function sendWorkerDraw(i /* args */) {
var args = [].slice.call(arguments, 1);
return new Promise(function (resolve) {
var worker = workers[i];
worker.addEventListener('message', function done(ev) {
worker.removeEventListener('message', done);
resolve(ev.data.buf);
});
worker.postMessage({
args: args
});
});
}
function draw(i, ctx, drawX, drawY, screenX, screenY, screenW, screenH, scale, maxiter) {
sendWorkerDraw(i, screenX, screenY, screenW, screenH, scale, maxiter)
.then(function (buf) {
var arr = new Uint8ClampedArray(buf);
var data = new ImageData(arr, screenW, screenH);
ctx.putImageData(data, drawX, drawY);
});
}
function mandelbrot(canvas, planeX, planeY, scale, maxiter) {
var SCALE = scale;
var MAXITER = maxiter;
var ISCALE = 1 / scale;
var PLANEX = planeX;
var PLANEY = planeY;
var SCREENX = PLANEX * SCALE;
var SCREENY = PLANEY * SCALE;
var SCREENW = canvas.width;
var SCREENH = canvas.height;
var PLANEW = SCREENW * ISCALE;
var PLANEH = SCREENH * ISCALE;
var ctx = canvas.getContext('2d');
canvas.addEventListener('click', function zoom(ev) {
canvas.removeEventListener('click', zoom);
var px = ev.offsetX;
var py = ev.offsetY;
var scale_ = CLICKZOOM * SCALE;
var maxiters_ = 1.4 * MAXITER;
var planeW_ = SCREENW / scale_;
var planeH_ = SCREENH / scale_;
var planeX_ = PLANEX + px * ISCALE - planeW_ / 2;
var planeY_ = PLANEY + py * ISCALE - planeH_ / 2;
planeX_ += (SCREENW / 2 - px) / scale_;
planeY_ += (SCREENH / 2 - py) / scale_;
var copyW = SCREENW / CLICKZOOM;
var copyH = SCREENH / CLICKZOOM;
var copyX = px - copyW / 2;
var copyY = py - copyH / 2;
copyX += (SCREENW / 2 - px) / CLICKZOOM;
copyY += (SCREENH / 2 - py) / CLICKZOOM;
ctx.drawImage(
canvas,
copyX, copyY, copyW, copyH,
0, 0, SCREENW, SCREENH
);
mandelbrot(canvas, planeX_, planeY_, scale_, maxiters_);
});
var BLOCKY = Math.ceil(SCREENH / NUMWORKERS);
for (var i = 0; i < NUMWORKERS; ++i) {
draw(
i,
ctx,
0,
i * SCREENH / NUMWORKERS,
SCREENX,
SCREENY + i * BLOCKY,
SCREENW,
BLOCKY,
SCALE,
MAXITER
);
}
return canvas;
}
window.addEventListener('load', function () {
var SCREENW = document.body.clientWidth
var SCREENH = document.body.clientHeight;
var canvas = document.createElement('canvas');
canvas.width = SCREENW;
canvas.height = SCREENH;
canvas.style.background = 'black';
document.body.appendChild(canvas);
var DOMAINX = -2.5;
var DOMAINY = -1.1;
var DOMAINW = 3.5;
var DOMAINH = 2.2;
var scale = Math.min(
SCREENW / DOMAINW,
SCREENH / DOMAINH
);
var startX = DOMAINX - (SCREENW - DOMAINW * scale) / 2 / scale;
var startY = DOMAINY - (SCREENH - DOMAINH * scale) / 2 / scale;
mandelbrot(canvas, startX, startY, scale, 100);
});
</script>
var last = 0;
onmessage = function (ev) {
var result = draw.apply(null, ev.data.args);
var buf = result.buffer;
postMessage({
buf: buf
}, [buf]);
};
// inspired by https://www.shadertoy.com/view/4df3Rn
function draw(screenX, screenY, screenW, screenH, scale, maxiter) {
var SCALE = scale;
var MAXITER = maxiter;
var ISCALE = 1 / scale;
var SCREENX = screenX;
var SCREENY = screenY;
var SCREENW = screenW;
var SCREENH = screenH;
var PLANEX = screenX * ISCALE;
var PLANEY = screenY * ISCALE;
var PLANEW = screenW * ISCALE;
var PLANEH = screenH * ISCALE;
var output = new Uint8ClampedArray(4 * SCREENW * SCREENH);
for (var py = 0; py < SCREENH; ++py) {
for (var px = 0; px < SCREENW; ++px) {
var x0 = PLANEX + px * ISCALE;
var y0 = PLANEY + py * ISCALE;
var x = 0;
var y = 0;
var it = 0;
var runaway = false;
var dot = 0;
for (var i = 0; i < MAXITER; ++i) {
var x2 = x * x;
var y2 = y * y;
dot = x2 + y2;
if (dot > 4.0) {
it = i;
runaway = true;
break;
}
var xtmp = x2 - y2 + x0;
y = 2*x*y + y0;
x = xtmp;
}
var ix = 4 * (py * SCREENW + px);
if (runaway) {
var ip = i - Math.log2(Math.log2(dot * dot)) + 4.0;
output[ix + 0] = 255 * (0.5 + 0.5 * Math.cos(3.0 + ip*0.15 + 0.0));
output[ix + 1] = 255 * (0.5 + 0.5 * Math.cos(3.0 + ip*0.15 + 0.6));
output[ix + 2] = 255 * (0.5 + 0.5 * Math.cos(3.0 + ip*0.15 + 1.0));
output[ix + 3] = 255;
} else {
output[ix + 0] = 0;
output[ix + 1] = 0;
output[ix + 2] = 0;
output[ix + 3] = 255;
}
}
}
return output;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.