made with proce55ing


// CA_2D_9T
//
// cellular automata animation
//
// initial random noise is processed by a CA for a period of time,
// then the CA rule shifts and continues to process the canvas.
//
// Seb Chevrel | www.seb.cc
// ———————————————————————————————————————————————————————————————————————————————————————————————————

int WIDTH=320;
int HEIGHT=240;
int gen=0;
int playhead=0;
int[] rules=   { 47,175,135,175,31,254,135,31};
int[] change={   50,250,300,200,60,200,200,60};
int duration=rules.length;

CellularAutomaton CA;

void setup()
{
  size(WIDTH,HEIGHT);
  CA=new CellularAutomaton(WIDTH,HEIGHT,rules[playhead],8); // width, height, CA rule, history depth
  noBackground();
  CA.init();
}

void loop()
{
  CA.run();
  CA.render();
  if (gen++ > change[playhead]) {
    gen=0;
    playhead=(playhead+1)%duration;
    CA.rule=rules[playhead];
  }
}


// ———————————————————————————————————————————————————————————————————————————————————————————————————
// class CellularAutomaton
//
// a 2-dimentional 9-neighbor totalistic cellular automaton
// initialized with pseudo-random noise, there are 1024 possible rules
// some good ones: 47, 513, 123, 135, 159, 111, 254, 175, 321
// a history buffer can average pixel values over succesive iterations to produce a smoother image
// ———————————————————————————————————————————————————————————————————————————————————————————————————
class CellularAutomaton {
  int sizeX=0;
  int sizeY=0;
  public int rule=0;
  int total=0;
  int[][] buffer;
  int currentBuffer=0;
  int nextBuffer=1;
  int history=0;
  int buffers=0;

  // CONSTRUCTOR ————————————————————————————————————————————————————
  CellularAutomaton(int x,int y,int r,int h) {
    sizeX=x;
    sizeY=y;
    rule=r;
    history=h;
    buffers=2+history;
    total=sizeX*sizeY;
    buffer=new int[buffers][total];
  }

  // INIT CA ————————————————————————————————————————————————————————
  void init() {
    for(int i=0;i<total;i++) {
      if(random(100)>50) buffer[currentBuffer][i]=1;
      else buffer[currentBuffer][i]=0;
    }
  }

  // RUN CA —————————————————————————————————————————————————————————
  void run() {
    // Process the automaton a times
    for (int a=0;a<2;a++) {

      // For each cell in the grid
      for(int i=0; i<sizeX; i++) {
        for(int j=0; j<sizeY; j++) {

          // Wraparound offsets
          int right=(i+1) % sizeX;
          int bottom=(j+1) % sizeY;
          int left=i-1; if (left==-1) left=sizeX-1;
          int top=j-1; if (top==-1) top=sizeY-1;

          // Calculate the sum of 9 neighbors
          int sum=buffer[currentBuffer][left+j*sizeX]; // left middle
          sum+=buffer[currentBuffer][left+top*sizeX]; // left top
          sum+=buffer[currentBuffer][left+bottom*sizeX]; // left bottom
          sum+=buffer[currentBuffer][right+j*sizeX]; // right middle
          sum+=buffer[currentBuffer][right+top*sizeX]; // right top
          sum+=buffer[currentBuffer][right+bottom*sizeX]; // right bottom
          sum+=buffer[currentBuffer][i+j*sizeX]; // middle middle
          sum+=buffer[currentBuffer][i+top*sizeX]; // middle top
          sum+=buffer[currentBuffer][i+bottom*sizeX]; // middle bottom

          // Update the nextBuffer
          if ((rule & (1<<sum)) >0) buffer[nextBuffer][i+j*sizeX]=1;
          else buffer[nextBuffer][i+j*sizeX]=0;
        }
      }
      // Transfer copy to map
      System.arraycopy(buffer[nextBuffer],0,buffer[currentBuffer],0,total);
    }
  }
  
  // RENDER CA ——————————————————————————————————————————————————————
  void render() {
      float sum=0;
      int col=0;
      for(int i=0;i<total;i++) {
        sum=0;
        for(int d=0;d<buffers;d++) {
          sum+=buffer[d][i];
        }
        col=int(sum/buffers*255);
        pixels[i]=col+(col<<8)+(col<<16);
      }
      currentBuffer=(currentBuffer+1)%buffers;
      nextBuffer=(nextBuffer+1)%buffers;
  }
}
// ———————————————————————————————————————————————————————————————————————————————————————————————————