Drawing Mandelbrot Orbits with Web Workers 2019-04-22 mandelbrot_client.js import * as SVG from 'svg.js'; function showText(msg) { let newTextNode = document.createElement('div'); let textNode = document.createTextNode(msg) newTextNode.appendChild(textNode) let entry = document.getElementById("entry") entry.appendChild(newTextNode) } function startWorker() { let worker = new Worker("/assets/js/mandelbrot_worker.bundle.js"); let mandelbrot = document.getElementById("mandelbrot") mandelbrot.setAttribute("style", "width:100%;") mandelbrot.style.height = Math.floor(mandelbrot.offsetWidth) + 'px'; mandelbrot.style.backgroundColor = "rgba(0,0,0, 0.04)"; let draw = SVG('mandelbrot').size(mandelbrot.offsetWidth, mandelbrot.offsetWidth) let mWidth = mandelbrot.clientWidth let mHeight = mandelbrot.clientHeight var mouseIsDown = false function handleMouseEvent(event) { let relativeX = (event.pageX - window.pageXOffset - mandelbrot.getBoundingClientRect().left); let relativeY = (event.pageY - window.pageYOffset - mandelbrot.getBoundingClientRect().top); let scaledX = relativeX / mWidth * 4.0 - 2.0 let scaledY = relativeY / mHeight * 4.0 - 2.0 worker.postMessage({x: scaledX, y: scaledY}) event.preventDefault() } function handleTouchEvent(event) { let touch = event.touches[0] let relativeX = touch.pageX - window.pageXOffset - mandelbrot.getBoundingClientRect().left let relativeY = touch.pageY - window.pageYOffset - mandelbrot.getBoundingClientRect().top let scaledX = relativeX / mWidth * 4.0 - 2.0 let scaledY = relativeY / mHeight * 4.0 - 2.0 worker.postMessage({x: scaledX, y: scaledY}) event.preventDefault() } mandelbrot.onclick = function(event) { handleMouseEvent(event) } mandelbrot.onmousedown = function(event) { mouseIsDown = true draw.clear() event.preventDefault() } mandelbrot.onmouseup = function(event) { mouseIsDown = false //draw.clear() event.preventDefault() } mandelbrot.onmousemove = function(event) { if (mouseIsDown) { handleMouseEvent(event) } event.preventDefault() } document.addEventListener('touchmove', handleTouchEvent, { passive:false }); let sequence = 0 worker.onmessage = function(event) { if (event.data["sequence"] >= sequence) { sequence = event.data["sequence"] } else { return } if (event.data["orbits"]) { draw.clear() let xs = event.data["orbits"][0] let ys = event.data["orbits"][1] let iterations = event.data["iterations"] for (var i = 1; i < iterations; i++) { let xOffset = (xs[i] + 2.0) / 4.0 * mWidth let yOffset = (ys[i] + 2.0) / 4.0 * mHeight if (xOffset > 0 && xOffset < mWidth && yOffset > 0 && yOffset < mHeight) { draw.rect(3, 3).fill('#000000').translate(xOffset, yOffset) } } } else if (event.data["message"]) { showText(event.data["message"]); } }; } if (typeof(Worker) !== "undefined") { startWorker() } else { showText("Sorry, Web Worker is not supported in your browser. :(") } mandelbrot_worker.ts declare function postMessage(message: any):any var sequence = 0 let max_iterations = 256 let bailout = 16 let iterationBufferZR = new Float32Array(max_iterations) let iterationBufferZI = new Float32Array(max_iterations) function mandelbrot(xx: number, yy: number) { let cr = xx let ci = yy let zi = 0.0 let zr = 0.0 let i = 0 while (i++ < max_iterations) { let temp = zr * zi let zr2 = zr * zr let zi2 = zi * zi zr = zr2 - zi2 + cr zi = 2.0 * temp + ci if (zi2 + zr2 > bailout) { return i } iterationBufferZR[i] = zr iterationBufferZI[i] = zi } return i } self.onmessage = function (msg: any) { let x = msg.data["x"] let y = msg.data["y"] let iterations = mandelbrot(x, y) sequence = sequence + 1 postMessage({ orbits: [iterationBufferZR, iterationBufferZI], iterations: iterations, sequence: sequence }) } postMessage({ message: "Click or Tap in the gray square.", sequence: sequence }); mandelbrot web-worker fractal