Create a gist now

Instantly share code, notes, and snippets.

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