Skip to content

Instantly share code, notes, and snippets.

@rgarner
Last active May 6, 2024 07:30
Show Gist options
  • Save rgarner/0f4f7810e0e9648acd6c6f2f451cd902 to your computer and use it in GitHub Desktop.
Save rgarner/0f4f7810e0e9648acd6c6f2f451cd902 to your computer and use it in GitHub Desktop.
The Magical Machine: live coding the mandelbrot set in p5.js
// 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