Published on

A Fractal Playground with Processing

Authors

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 Screenshot
A basic drawing program in just a few lines of code

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?

Triangle de Sierpinski
Triangle de Sierpinski

A fractal is a simple pattern driven by recursion.

Consider the following "recursive image" of squares:

Recursive Squares Image

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.

Relative Recursive Origins

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:

Scaling Squares (One)
1 square (x: 0, y: 0, width: 200, height: 200)

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.

Scaling Squares (Multiple)
Recursive squares with size and positions relative to the one before it.

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:

Recursive Circles
  • Red: Top circles
  • Blue: Bottom circles
  • Green: Right circles
  • Black: Left circles

With origin markers:

Recursive Circles (With Origins)

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.