Creating Generative Art Using Cellular Automata

  Programming

As a hobby algorist, I’m always looking for new algorithms that can help me create interesting pieces of art. Recently, I came across a paper written by Brian P. Hoke. It was written 22 years ago, but there’s a lot of useful information in it. The paper, among other things, talks about using a simple 2D cellular automata rule named “stepping stone” to modify photos such that they look like paintings. The thing I like the most about the rule is that you can pass random noise to it and it will turn it into something beautiful after enough iterations.

I’ll now briefly describe how I implemented the stepping stone rule using P5.js.

As usual, inside the setup() function, I created a canvas of size 400x400.

createCanvas(400, 400);

Then, I filled it up with random colors using the following code:

for(var i=0;i<width;i++){
    for(var j=0;j<height;j++) {
        set(i,j, color(random(0,255),
                       random(0,255), 
                       random(0,255)));
    }
}
updatePixels();

With the above code, the canvas looks like this:

Random noise

The idea behind the stepping stone rule is extremely simple: For each pixel X on the canvas, toss a coin. If it’s heads, randomly pick one of its four neighbors and change its color such that it matches the color of pixel X.

A more visual description would be that, at any given time, every pixel has a 50% chance of consuming one of its neighbors.

Coding the above rule takes just a few minutes. Here’s what my draw() function looks like:

function draw() {
  for(var i=0;i<width;i++) {
    for(var j=0;j<height;j++) {
      var currentColor = get(i,j);
      if(random(['H', 'T']) == 'H') { // Toss and check if heads

        var r = random(['T', 'R', 'B', 'L']); // Pick random neighbor

        if(r == 'L') // Left neighbor
          set((i-1+width)%width,j,currentColor);
        else if (r == 'R') // Right neighbor
          set((i+1)%width, j, currentColor);
        else if (r == 'B') // Bottom neighbor
          set(i, (j+1)%height, currentColor);
        else if (r == 'T') { // Top neighbor
            set(i,(j-1+height)%height,currentColor);
        }

      }      
    }                  
  }
  updatePixels();
}

To save time, I’m using the set() function. For better performance, you could try manipulating the pixels array directly.

Also note that I’m using the % operator while finding neighbors. This helps me wrap around edges very easily and concisely.

With the above code, I get a piece of art that looks like this:

Scattered colors

That was all too simple. I wanted more. So, while searching for other rules that are similar to the stepping stone rule, I came across papers written by David Griffeath, a professor at the University of Wisconsin. He, and a few of his students, have detailed several different approaches you can follow to create art with cellular automata. Some of them create art that’s truly mesmerising. For now, I’ll focus on cyclic cellular automata. Implementing them is slightly more difficult, but the results are definitely worth the effort.

The idea behind a cyclic cellular automaton is that you have a large grid containing hundreds of cells. Each cell has a state associated with it. The state is essentially just a number–something between 3 and 16 usually. At any given point, a cell can increment its state if and only if one of its neighbors is at a higher state than it–usually by just 1 point.

For example, if cell A’s state is 3, and it has at least one neighbor whose state is 4, then A’s state too becomes 4.

You’re going to need a few global variables to implement this logic. Let’s say we have a variable named matrix to represent the large grid. To store temporary copies of the grid, we have another variable named tmpMatrix.

Next, let’s say the state of a cell can be a number between 0 and 14. You are free to change the maximum state number.

Lastly, we can have an array of colors, which we use to represent a cell as a pixel.

var matrix = [];
var tmpMatrix = [];
var n = 14;
var colors = [];

Here’s how I initialized the above variables inside the setup() function:

function setup() {
  createCanvas(450,450);
  
  for(var i=0;i<n;i++){
    colors.push(color(random(0,255), random(0,255), random(0,255)));
  }

  for(var i=0;i<height;i++) {
    var row = [];
    for(var j=0;j<width;j++) {
      var r = int(random(0, n));
      row.push(r);
      var c = colors[r];
      set(j, i, c);
    }
    matrix.push(row);
  }
  
  updatePixels();
}

As you can probably tell, I’ve assigned random states and colors to the cells and pixels.

Next, inside the draw() function, I have one large if condition that checks if a cell has at least one neighbor with state difference of +1. If that’s true, I update the state. This needs to be done with a copy of the original grid to make sure that the if condition is not using intermediate values.

Once the grid has been updated, I update the colors of the pixels in the canvas using the set() function as usual.

function draw() {  
  tmpMatrix = cloneMatrix(matrix);
  for(var i=0;i<height;i++) {
    for(var j=0;j<width;j++) {

      var nextValue = (matrix[i][j] + 1)%n;

      if(nextValue == matrix[i][(j+1)%width] || 
         nextValue == matrix[(i+1)%height][j] ||
         nextValue == matrix[(i-1+height)%height][j] ||
         nextValue == matrix[i][(j-1+width)%width]) 
            tmpMatrix[i][j] = nextValue;        
    }    
  }  

  matrix = cloneMatrix(tmpMatrix);
  for(var i=0;i<height;i++){
    for(var j=0;j<width;j++) {
        set(j,i,colors[matrix[i][j]]);
    }
  }
  updatePixels();
}

function cloneMatrix(m) {
    return m.map(function(arr) {
        return arr.slice();
    });
}

Note that the cloneMatrix() function is necessary because array methods like Array.from() and slice() don’t create copies of sub-arrays.

With the above code, you should see spirals being generated within seconds.

cyclic cellular automata spirals

If you let it run for some more time, your canvas will be full of large spirals!

cyclic cellular automata, more spirals

Isn’t it amazing how such simple rules can lead to so much complexity? I, for one, am very impressed.

If you like procedural art generation with JavaScript, do take a look at my classes on Skillshare. I go into a lot more detail there and you can see me live coding.

Emergent art
Julia Set
Peter de Jong attractor

If you found this article useful, please share it with your friends and colleagues!