//Inspiration via Mathologer: https://www.youtube.com/watch?v=leFep9yt3JY&t=12m53s //Excitement via Two Minute Papers: https://www.youtube.com/watch?v=HvHZXPd0Bjs&list=PLujxSBD-JXgnqDD1n-V30pKtp6Q886x7e&index=93 const CANVAS_SIZE=1024 var canvas = document.body.appendChild(document.createElement('canvas')) canvas.width = CANVAS_SIZE canvas.height = CANVAS_SIZE var ctx = canvas.getContext('2d') //x^2 + c var f = function(z, c){ return [ z[0]*z[0] - z[1]*z[1] + c[0], 2*z[0]*z[1] + c[1] ] } //|z|_2 var h = function(z){return Math.sqrt(z[0]*z[0] + z[1]*z[1])} //iteration > 2 ? -> iteration var g = function(c, n){ var z = f([0,0], c) for(var i = 0; i < n; i++){ z = f(z, c) if(h(z) > 2) return i } return i } function clamp(value, begin, end) { if (value < begin) value = begin; if (value > end) value = end; return value; } function rescale(value, from_begin, from_end, to_begin, to_end) { let t = (value - from_begin) / (from_end - from_begin); return to_begin + (to_end - to_begin) * t; } let colorparams = { radius_center: 1/3, radius_fade: 10, bri_scale: 200, angle_start: 3, angle_scale: 6, cie_x: 1/3, cie_y: 1/3, } function color(count, limit){ let P = colorparams; let r = P.radius_center / ((P.radius_fade+count)/P.radius_fade); let bri = count == limit? 0 : Math.min(255, count/limit*P.bri_scale); let angle = P.angle_start + P.angle_scale * count / limit; let x = P.cie_x + r * Math.cos(angle), y = P.cie_y + r * Math.sin(angle); let [R, G, B] = cie_to_rgb(x, y, bri); R = clamp(R, 0, 255); G = clamp(G, 0, 255); B = clamp(B, 0, 255); return [R, G, B]; } function calculate(limit) { let counts = []; for (let y = 0; y < CANVAS_SIZE; y++) { counts[y] = []; for (let x = 0; x < CANVAS_SIZE; x++) { let zx = rescale(x, 0, CANVAS_SIZE, -2, +2); let zy = rescale(y, 0, CANVAS_SIZE, -2, +2); counts[y][x] = g([zx, zy], limit); } } return counts; } function draw(counts, limit) { console.log("REDRAW"); const imageData = ctx.getImageData(0, 0, CANVAS_SIZE, CANVAS_SIZE); const pixels = imageData.data; let colormap = []; for (let i = 0; i <= limit; i++) { colormap[i] = color(i, limit); } for (let x = 0; x < CANVAS_SIZE; x++) { for (let y = 0; y < CANVAS_SIZE; y++) { let index = 4 * (y * CANVAS_SIZE + x); let RGB = colormap[counts[y][x]]; for (let i = 0; i < 3; i++) { pixels[index+i] = RGB[i]; } pixels[index+3] = 255; } } ctx.putImageData(imageData, 0, 0); } const LIMIT = 25; var counts = calculate(LIMIT); function redraw() { draw(counts, LIMIT); } redraw(); var gui = new dat.GUI(); function w(name, min, max) { gui.add(colorparams, name, min, max).onChange(redraw); } w('radius_center', 0, 1); w('radius_fade', 0, 50); w('bri_scale', 0, 1000); w('angle_start', 0, 6.14); w('angle_scale', 0, 30); w('cie_x', 0, 1); w('cie_y', 0, 1);