A Fractal Playground with Processing
2018-02-05
Est. 4m read
Processing is pretty cool. I first came across it when Daniel Shiffman was recommended in my YouTube feed and I’ve had a lot of fun with it since. In short: it’s a Java sketchbook that allows anyone to create interactive and visual sketches in 2D/3D space. It’s super powerful, and super easy to get started with.
Processing has a lot more to offer, but in this post I’m going to walk you through making fractals from simple shapes.
But before we start fractaling, let’s talk about how a fractal is made.
What is a Fractal?
A fractal is a simple pattern driven by recursion.
Consider the following “recursive image” of squares:
Take a second to think about the ways you might implement something like this. What determines the next step in the sequence? How can we make sure the squares don’t overlap?
There a certainly a lot of ways you could implement this, but the trick to recursive shapes in Processing is to translate the “origin” of the canvas to the shape you’re drawing. This way, you can draw shapes relative to each other’s size and position.
Here’s the same sketch from before, but with a red circle to display the origin’s location at every step.
In Processing, the function to move the origin is translate(...)
.
translate(...)
shifts the origin relative to its previous position. For
example: translate(3, 9)
would move the origin 3 to the right and 9 down.
Let’s Code
The scaling squares example from before is a reasonable first feat, so that’s where we’ll start.
void setup() {
size(1000, 250);
}
void draw() {
scalingSquares(200); // we'll define this function next
// it'll be called once per frame
}
The setup()
and draw()
functions are the heart and soul of Processing
sketches, so try to keep them clean and performant.
We’re going to put the code responsible for drawing the squares into its own
function called scalingSquares(...)
.
void scalingSquares(float n) {
rect(0, 0, n, n);
translate(n + 5, n / 8);
}
If you tried this with 200 as the argument in draw()
, expect to see:
We have everything needed to complete the scaling squares except the main ingredient… recursion!
By simply adding these two lines:
void scalingSquares(float n) {
+ if (n < 30) return; // don't draw squares that are less than 30x30
rect(0, 0, n, n); //
translate(n + 5, n / 8); // offset each square by itself + 5px
// and move each square down 1/8 to vertically align
//
+ scalingSquares(n * 0.75); // each square is 75% the size of the one before it
}
We’ve got our scaling squares functionality using a recursive function.
An important thing to note is the value passed into scalingSquares(...)
within scalingSquares(...)
. It’s the original argument multiplied by 0.75
to reduce the scale factor going into the recursive function until it’s less
than 30 pixels. Once it’s less than 30 pixels, the function returns and no
more rect(...)
calls are made.
Better Fractals?
If you’ve spent any amount of time on the Internet, you know that this is
hardly a fractal. To create more complex fractals, adding pushMatrix()
and
popMatrix()
to your arsenal would be wise.
pushMatrix()
saves the current origin and the properties of the grid to an
off-screen stack and popMatrix()
restores the origin and grid to the most
recently stored. Think of pushMatrix()
as a Save function and popMatrix()
as the Load function for the grid.
void setup() {
size(700, 700);
}
void draw() {
background(255);
noFill();
translate(width / 2, height / 2); // move origin to middle of canvas
draw_fractal(500);
}
void draw_fractal(float r) {
ellipse(0, 0, r, r); // draw origin circle
if (r < 100) // don't draw circles smaller than 100x100
return;
stroke(255, 0, 0); // red circles
pushMatrix(); // save the origin, we're about to change it
translate(0, -r / 4); // move the origin straight down to the 1/4 mark
draw_fractal(r / 2); // restart the process at half the scale
popMatrix();
stroke(0, 0, 255); // blue circles
pushMatrix();
translate(0, r / 4);
draw_fractal(r / 2);
popMatrix();
stroke(0, 255, 0); // green circles
pushMatrix();
translate(r / 4, 0);
draw_fractal(r / 2);
popMatrix();
stroke(0); // black circles
pushMatrix();
translate(-r / 4, 0);
draw_fractal(r / 2);
popMatrix();
}
This recursive code has the following output:
- Red: Top circles
- Blue: Bottom circles
- Green: Right circles
- Black: Left circles
With origin markers:
Conclusion
I hope this was an interesting read and a good introduction to visualization in code. Processing is good for more than just trippy fractals, it’s also a tool to visualize and interact with data in cool and new ways.
There are plenty of fractal snippets freely available online for Processing,
but feel motivated to create your own and share them here if you come up with
something cool! The strokeWeight(...)
, stroke(...)
, noFill()
, fill(...)
,
and background(...)
will be useful for adding more color.
For further documentation, check out Processing’s website.