Skip to content

Instantly share code, notes, and snippets.

@lilgreenland
Last active December 21, 2017 20:27
Show Gist options
  • Save lilgreenland/a7fec746201ce2f3dcf200a4b65cc942 to your computer and use it in GitHub Desktop.
Save lilgreenland/a7fec746201ce2f3dcf200a4b65cc942 to your computer and use it in GitHub Desktop.
Mandelbrot (click to zoom)
<!--
https://en.wikipedia.org/wiki/Mandelbrot_set
http://warp.povusers.org/Mandelbrot/
http://tilde.club/~david/m/#zoom=6.450592885375493,3.4&lookAt=-0.6,0&iterations=85&superSamples=1&escapeRadius=10.0&colorScheme=pickColorGrayscale
https://codepen.io/handsomeone/pen/grByoL?q=Mandelbrot&order=popularity&depth=everything&show_forks=false
https://codepen.io/yukulele/pen/oXYLZR?editors=0010
-->

Mandelbrot (click to zoom)

click the window to zoom in click render to redraw after changing

A Pen by Landgreen on CodePen.

License.

var canvas = document.createElement("canvas");
var ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
canvas.onclick = function(e) {
s.x -= (e.clientX / canvas.width - 0.5) * s.range * 2;
s.y += (e.clientY / canvas.height - 0.5) * s.range * 2;
s.range /= s.zoomOnClick;
s.render();
};
const s = {
iterations: 50,
colorDepth: 12,
x: -0.0000000001,
y: 0.0000000001,
range: 0.0000000001,
zoomOnClick: 2,
render: function() {
//force canvas to square
if (window.innerWidth > window.innerHeight) {
canvas.width = window.innerHeight;
canvas.height = window.innerHeight;
} else {
canvas.width = window.innerWidth;
canvas.height = window.innerWidth;
}
ctx.clearRect(0, 0, canvas.width, canvas.height);
const width = canvas.width;
const height = canvas.height;
const MinRe = s.x + s.range;
const MaxRe = s.x - s.range;
const MinIm = s.y + s.range;
const MaxIm = s.y - s.range;
const factor = 2 * s.range / (width - 1);
for (let y = 0; y < height; y += 1) {
let c_im = MaxIm + y * factor;
for (let x = 0; x < width; x += 1) {
let c_re = MinRe - x * factor;
let Z_re = c_re;
let Z_im = c_im;
let isInside = true;
let n;
for (n = 0; n < s.iterations; ++n) {
let Z_re2 = Z_re * Z_re;
let Z_im2 = Z_im * Z_im;
if (Z_re2 + Z_im2 > 4) {
isInside = false;
break;
}
Z_im = 2 * Z_re * Z_im + c_im;
Z_re = Z_re2 - Z_im2 + c_re;
}
if (!isInside) {
ctx.fillStyle = "hsl(" + n * s.colorDepth + ", 100%, 50%)";
ctx.fillRect(x, y, 1, 1);
}
}
}
},
colorCycleRate: 0.005,
colorCycle: function() {
if (s.colorCycleRate) {
var imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
var data = imageData.data;
for (var i = 0; i < data.length; i += 4) {
let hue = rgbToHsl(data[i], data[i + 1], data[i + 2]);
hue = hslToRgb(hue[0] + s.colorCycleRate, hue[1], hue[2]);
data[i] = hue[0]; // red
data[i + 1] = hue[1]; // green
data[i + 2] = hue[2]; // blue
}
ctx.putImageData(imageData, 0, 0);
}
}
};
const JSON = {
closed: false,
preset: "zoomed out",
remembered: {
"zoomed out": {
"0": {
iterations: 50,
zoomOnClick: 2,
colorDepth: 12,
colorCycleRate: 0.005,
x: -0.5,
y: 0,
range: 1.5
}
},
curls: {
"0": {
iterations: 100,
zoomOnClick: 1,
colorDepth: 7,
colorCycleRate: 0.005,
x: 0.26745400000775205,
y: -0.003559803037652291,
range: 0.001500001
}
},
roadmap: {
"0": {
iterations: 300,
zoomOnClick: 1,
colorDepth: 2,
colorCycleRate: 0.007589671556896615,
x: -1.7639344533485766,
y: -0.02813237032462254,
range: 0.000031920886001252426
}
},
dendrites: {
"0": {
iterations: 100,
zoomOnClick: 1,
colorDepth: 5,
colorCycleRate: 0.004500763014601327,
x: -0.5622398824131151,
y: -0.6428966829256364,
range: 0.026649029764262932
}
},
nest: {
"0": {
iterations: 282,
zoomOnClick: 2,
colorDepth: 2.5,
colorCycleRate: 0.007,
x: -0.4824552197260896,
y: 0.6254977217181471,
range: 0.00004577639770507812
}
},
coral: {
"0": {
iterations: 2000,
zoomOnClick: 1,
colorDepth: 1.5,
colorCycleRate: 0.003,
x: 0.34930538309255793,
y: -0.36812128659562,
range: 0.003711914035661803
}
},
fireworks: {
"0": {
iterations: 1322,
zoomOnClick: 2,
colorDepth: 2.5,
colorCycleRate: 0.003,
x: -0.4013527464804106,
y: 0.6483662580022113,
range: 0.0000980359090981452
}
},
"tie dye": {
"0": {
iterations: 44,
zoomOnClick: 1,
colorDepth: 10,
colorCycleRate: 0.003,
x: -1.3723012459856034,
y: -0.003945185354374348,
range: 0.187500125
}
},
pinwheel: {
"0": {
iterations: 1000,
zoomOnClick: 2,
colorDepth: 7,
colorCycleRate: 0.003,
x: -0.16197358174188,
y: 0.6550365703323483,
range: 0.001745118697599116
}
},
spiral: {
"0": {
iterations: 1400,
zoomOnClick: 2,
colorDepth: 0.5,
colorCycleRate: 0.0086,
x: -0.1619484952732689,
y: 0.6550556007474406,
range: 0.00009115253076523027
}
},
vortex: {
"0": {
iterations: 1400,
zoomOnClick: 2,
colorDepth: 7.4,
colorCycleRate: 0.02,
x: -0.16238183902301823,
y: 0.6556487146816999,
range: 0.00014899244395867044
}
},
cell: {
"0": {
iterations: 1000,
zoomOnClick: 2,
colorDepth: 5,
colorCycleRate: 0.004,
x: -0.401284318101899,
y: 0.6484025999133984,
range: 2.3698865718e-10
}
},
tunnel: {
"0": {
iterations: 1200,
zoomOnClick: 2,
colorDepth: 0.3,
colorCycleRate: 0.006,
x: -0.20055203232841917,
y: 0.6502596882817046,
range: 0.00002749293585475576
}
},
octogon: {
"0": {
iterations: 2500,
zoomOnClick: 2,
colorDepth: 0.3,
colorCycleRate: 0.006,
x: -0.20053695849677514,
y: 0.6502429504752787,
range: 1.115764765279e-8
}
},
pools: {
"0": {
iterations: 2000,
zoomOnClick: 1,
colorDepth: 15,
colorCycleRate: 0.008,
x: 0.2681983668243779,
y: -0.0036277515053630104,
range: 0.00002150983861069779
}
},
crack: {
"0": {
iterations: 50,
zoomOnClick: 1,
colorDepth: 12,
colorCycleRate: 0.001,
x: -1.278011316016057,
y: -0.4253948018687699,
range: 0.018750012499999996
}
},
tip: {
"0": {
iterations: 2000,
zoomOnClick: 1,
colorDepth: 30,
colorCycleRate: 0.002,
x: -1.999924702455152,
y: 0,
range: 0.00011872991364720651
}
},
marching: {
"0": {
iterations: 100,
zoomOnClick: 1,
colorDepth: 3,
colorCycleRate: 0.00367705406998925,
x: 0.2943838352367109,
y: -0.00025955166805089597,
range: 0.04687503125
}
}
},
folders: {
position: {
closed: false,
folders: {}
}
}
};
function pickRandomProperty(obj) {
var result;
var count = 0;
for (var prop in obj) if (Math.random() < 1 / ++count) result = prop;
return result;
}
var gui = new dat.GUI({
load: JSON,
preset: pickRandomProperty(JSON.remembered)
});
gui.remember(s);
var f1 = gui.addFolder("position");
f1.close();
f1.add(s, "x").listen();
f1.add(s, "y").listen();
f1.add(s, "range").listen();
gui.add(s, "render");
gui.add(s, "iterations", 1, 2500);
gui.add(s, "colorDepth", 0.1, 30);
gui.add(s, "colorCycleRate", 0, 0.02);
gui.add(s, "zoomOnClick", 0.1, 10);
s.render();
const cycle = function() {
s.colorCycle();
window.requestAnimationFrame(cycle);
};
window.requestAnimationFrame(cycle);
/**
* Converts an RGB color value to HSL. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes r, g, and b are contained in the set [0, 255] and
* returns h, s, and l in the set [0, 1].
*
* @param Number r The red color value
* @param Number g The green color value
* @param Number b The blue color value
* @return Array The HSL representation
*/
function rgbToHsl(r, g, b) {
(r /= 255), (g /= 255), (b /= 255);
var max = Math.max(r, g, b),
min = Math.min(r, g, b);
var h,
s,
l = (max + min) / 2;
if (max == min) {
h = s = 0; // achromatic
} else {
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return [h, s, l];
}
/**
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* @param Number h The hue
* @param Number s The saturation
* @param Number l The lightness
* @return Array The RGB representation
*/
function hslToRgb(h, s, l) {
var r, g, b;
if (s == 0) {
r = g = b = l; // achromatic
} else {
function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return [r * 255, g * 255, b * 255];
}
<script src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/464612/dat.gui.min.js"></script>
body{
overflow:hidden;
background-color: #000;
}
canvas {
position: absolute;
top: 0;
left: 0;
z-index: 0;
/* forces Anti-aliasing */
/* transform: scale(1.001); */
}
/* "x": -0.1619484952732689,
"y": 0.6550556007474406,
"range": 0.00009115253076523027 */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment