Last active
May 6, 2024 07:30
-
-
Save rgarner/0f4f7810e0e9648acd6c6f2f451cd902 to your computer and use it in GitHub Desktop.
The Magical Machine: live coding the mandelbrot set in p5.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// A greyscale Mandelbrot set based on 100 iterations | |
// with a non-linear pow applied to make it Not Dark | |
// A complex number with an a (real) and b (imaginary) part. | |
// It can mutate itself by `add`ing itself to an `other`, or squaring itself in-place. | |
// It can also compare its own magnitude to an arbitrary given magnitude in `isBiggerThan`. | |
class Complex { | |
constructor(a,b) { | |
this.a = a; | |
this.b = b; | |
} | |
add(other) { | |
this.a = this.a + other.a | |
this.b = this.b + other.b | |
} | |
square() { | |
let new_a = this.a * this.a - this.b * this.b; | |
let new_b = 2 * this.a * this.b; | |
this.a = new_a; | |
this.b = new_b; | |
} | |
isBiggerThan(mag) { | |
return this.a * this.a + this.b * this.b > mag * mag; | |
} | |
} | |
function setup() { | |
createCanvas(800, 800); | |
// This is what's needed on my Macbook; you may need to alter it | |
pixelDensity(1); // https://p5js.org/reference/#/p5/pixelDensity | |
// use the 1-dimensional `pixels` array | |
loadPixels(); | |
} | |
function shadeAt(x, y) { | |
let z = new Complex(0, 0); | |
// Map our pixel's x and y to the -2/+2 Mandelbrot set bounds. | |
// for our imaginary number C, | |
// our x will form the real part, and | |
// our y will form the imaginary part | |
let c_a = map(x, 0, width, -2, 2); | |
let c_b = map(y, 0, height, -2, 2); | |
let c = new Complex(c_a, c_b); | |
// Now perform some iterations of squaring/adding to see if this C flies away to infinity or stays within 2! | |
let count; | |
let iterations=100; | |
for (count = 0; count < iterations; count++) { | |
z.square(); | |
z.add(c); | |
if (z.isBiggerThan(2)) { | |
break; // flies away, is not in the Mandelbrot set | |
} | |
} | |
count = map(count, 0, iterations, 0, 1); // Normalise the count it took to escape to a 0.0-1.0 value | |
return pow(count, 0.3) * 255; // non-linearise the count so that the shade transitions are smoother. Try 0.45 here for darker with tiny lightning bolts! | |
} | |
function setPixel(x,y,shade) { | |
// jump into the 1-dimensional `pixels` array at the right x/y point | |
let p_idx = (x + y * height) * 4; | |
pixels[p_idx + 0] = shade; // R | |
pixels[p_idx + 1] = shade; // G | |
pixels[p_idx + 2] = shade; // B | |
pixels[p_idx + 3] = 255; // A | |
} | |
function draw() { | |
// start with a light grey | |
background(240); | |
for(let x = 0; x < width; x++) { | |
for(let y = 0; y < height; y++) { | |
setPixel(x,y,shadeAt(x,y)) | |
} | |
} | |
// when using the `pixels` 1-dimensional array, p5js requires this to show anything at all | |
updatePixels(); | |
noLoop(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment