Chaos game (different versions)

Hello everyone. Some months ago I made a Processing sketch to experiment with the game of chaos. I coded different versions of it and managed to create lots of interesting images with them. In this post I am going to publish some of the versions along with images created with each.

What is the game of chaos?

The chaos game is essentially letting a point inside a convex polygon move under certain rules. In its most basic form, the game goes as follows.

In each iteration, chose a polygon's vertex at random and move towards it. The distance to travel is always a fixed fraction of the distance between the picked vertex and the point.

By trying different polygons, different 'fixed fractions', playing with colours and adding restrictions to how to choose the vertices; you can create very interesting pictures. As a teaser, if you look at my profile picture, it was made this way.

First version (bare basics).

Here I share the most basic version of my code, in case someone wants to build something from it. Nothing very interesting can be drawn with it beyond the Sierpinsky's triangle though.

Main sketch class
/*
The chaos game.
 A bare basic implementation of the chaos game.
 
 The game of chaos boils down to drawing a sequence of points
 inside a CONVEX polygon that are generated following certain rules.
 
 The rules are the following.
 Given a convex polygon, a point P inside it and a number r
 between 0 and 1; these steps are carried out:
 
 1. Choose at random one of the polygon's vertices (v).
 2. Generate the next point P' by this formula  P' = P + r * (v - P).
 3. Draw P'.
 4. Repeat the previous steps with P'.
 
 
 Under suitable choices for r and the polygon, the sequence of points
 depicts a fractal set.
 
 For more information on the chaos game and take ideas for tinkering,
 you can have a look at https://en.wikipedia.org/wiki/Chaos_game
 
 *--------------------* 
 * Keyboard controls: *
 *--------------------*
 - 'p' to pause the execution.
 - 'r' to resume the execution.
 - 's' when execution is paused, saves the canvas to a png image
 whose name's held by the variable 'img_name'.
 */
Point p; // point to iterate inside the polygon
float adv_ratio; // advance ratio
int max_iter; // number of iterations to quit the sketch at
boolean is_exec_paused; // state of the sketch
String img_name = "prova.png"; // name of the image to save the canvas to


void setup() {
  fullScreen();
  frameRate(60);

  background(0);
  stroke(255); // colour of the point
  strokeWeight(1.5);
  strokeCap(PROJECT);

  max_iter = int(frameRate) * 60 * 10; // 10 minutes
  is_exec_paused = false;

  /*adv_ratio = .5f in a triangle yields Sierpinski's triangle*/
  adv_ratio = .5f;
  initial_setting();
}

void draw() {
  p.iterate();
  if (frameCount > frameRate * 20) { // dismiss some initial points (20 sec)
    p.draw();
  }

  // when reached the maximum of iterations, save the canvas and quit
  if (frameCount == max_iter) {
    println("Maximum number of iterations reached: " + max_iter);
    save_and_quit();
  }
}

/**
 Create convex polygon and initialize the point.
 This function holds all the code for creating the polygon
 and the point. It prevents the setup() function from becoming a mess.
 
 Comment or uncomment the lines of the polygon you want to use;
 or add your own.
 */
void initial_setting() {
  PVector[] polygon; // convex polygon's vertices


  /*triangle*/
  polygon = new PVector[3];
  polygon[0] = new PVector(width / 2, 0);
  polygon[1] = new PVector(0, height);
  polygon[2] = new PVector(width, height);

  // initialize the point, make sure it lies inside the polygon!
  p = new Point(0, 0, adv_ratio);
  p.x = width / 2 + random(- 50, 50);
  p.y = random(height / 2, height);
  p.polygon = polygon;
  /*--end triangle--*/

  /*rectangle (whole screen)*/
  //polygon = new PVector[4];
  //polygon[0] = new PVector(0, 0);
  //polygon[1] = new PVector(width, 0);
  //polygon[2] = new PVector(width, height);
  //polygon[3] = new PVector(0, height);

  //// initialize the point, make sure it lies inside the polygon!
  //p = new Point(0, 0, adv_ratio);
  //p.x = random(width);
  //p.y = random(height);
  //p.polygon = polygon;
  /*--end rectangle--*/

  /*regular polygon*/
  //int q_edges = 5; // what regular polygon to create
  //float cx = width / 2, cy = height / 2; // polygon's center
  //float radius = 3 * width / 5; // change this to resize the polygon
  //float step_angle = 2 * PI / q_edges;
  //float ini_angle = PI / 2; // initial vertex on top

  //polygon = new PVector[q_edges];
  //for (int i = 0; i < polygon.length; i++) {
  //  polygon[i] = new PVector(
  //    cx + radius * cos(i * step_angle + ini_angle), 
  //    cy - radius * sin(i * step_angle + ini_angle));
  //}

  //// initialize the point, make sure it lies inside the polygon!
  //p = new Point(0, 0, adv_ratio);
  //p.x = width / 2 + random(- 50, 50);
  //p.y = height / 2 + random(-50, 50);
  //p.polygon = polygon;
  /*--end regular polygon--*/
}

/**Save the canvas to a file.
 The name of the file is held by the variable 'img_name'.
 */
void save_and_quit() {
  println("Saving the canvas to \"" + img_name + "\"");
  save(img_name);
  noLoop();
  exit();
}

/**Keyboard controls for the sketch.
 - 'p' to pause the execution.
 - 'r' to resume the execution.
 - 's' when execution is paused, saves the canvas to a png image
 whose name's held by the variable 'img_name'.
 */
void keyPressed() {
  if (is_exec_paused) {
    if (key == 'r' || key == 'R') {
      is_exec_paused = false;
      println("Execution resumed");
      loop();
    } else if (key == 's' || key == 'S') {
      println("Saving the canvas to \"" + img_name + "\"");
      save(img_name);
    }
  } else {
    if (key == 'p' || key == 'P') {
      is_exec_paused = true;
      println("Execution paused");
      noLoop();
    }
  }
}
Point class
/**
 Class representing a point as in the chaos game.
 
 In the game of chaos a point moves inside a convex polygon
 by moving towards a randomly selected vertex in each iteration.
 The distance the point travels to the vertex is a fixed fraction
 of the distance they are apart.
 
 We will call that fixed fraction 'advance ratio'.
 In each iteration, the new position of the point
 is computed as follows:
 
 P' = P + r * (v - P)
 
 Where:
 -P' are the new point's coordinates.
 -P are the current point coordinates.
 -r is 'advance ratio', 0 < r < 1.
 -v are the coordinates of the randomly chosen vertex.
 
 The game is played by repeatedly calling 'iterate()' and 'draw()'.
 */
class Point {
  float x, y; // coordinates of the point
  float r; // advance ratio
  PVector[] polygon; // polygon's vertices


  Point(float x, float y, float adv_ratio) {
    this.x = x;
    this.y = y;
    r = adv_ratio;
  }

  /**Generates the new coordinates of the point.*/
  void iterate() {
    int i;

    // choose a vertex at random
    i = int(random(polygon.length));

    // compute the next coordinates
    x += (polygon[i].x - x) * r;
    y += (polygon[i].y - y) * r;
  }

  void draw() {
    point(x, y);
  }
}

With this you can create the Sierpinsky's triangle

Sierpinsky's triangle

Tweak ideas

Instead of drawing the point like this

class Point{
...
void draw() {
    point(x, y);
  }
}

we can try drawing small rectangles with some suitable transparency value, so the image looks slightly smoother.

class Point{
  ...
  /**Assume rectMode(CENTER) is enabled.*/
  void draw() {
    fill(255, 100);
    rect(x, y, 4, 4);
  }
}
Tweak resulting image

Also we could spice things up with colours and get images like this

Colourful version

Which was obtained by assigning a colour to each vertex of the triangle and drawing the point with the colour of the vertex that was picked up in each iteration.

Second version

Coming soon.
2 Likes

Thank you for sharing this – very interesting! Looking forward to the second version.

One potential direction in the future might be class-based – for example, a class ChaosGame, which contains a class Polygon and a canvas image PGraphics pg. Then you could create a new ChaosGame, define its Polygon (a triangle, a square, etc), and use methods to iterate points on it.

Hello jeremy, that’s definitely the way to go, organize the code in classes and not into a huge mess of scattered pieces. At the same time, I wanted to keep it as simple as possible (though it can be further simplified). Versions 2 and 3 are more structured :slight_smile:.

I was not thinking of PGraphics, but I looked it up in the reference; do you think it will run faster or is it because you can handle the alpha channel? Sorry, I have never used that class and never thought I needed it. Could you elaborate more on it?

Thanks for the feedback :slight_smile:

I love to the graphic Sierpinsky’s triangle in 3D

The PGraphics isn’t necessary – it is just a way of drawing your output to a buffer other than the canvas. Then you can update your canvas – for example, dragging the output view around – without having to re-render the fractal. You can leave the pixels of the PGraphics alone, and just wipe the main canvas and recopy them into a new position with image(myPGraphics). Also, if each ChaosGame has its own PGraphics then you can create several and render each to the canvas – image(mysquare), image(mytriangle) – but the pixels stored in each ChaosGame PGraphics relate only to that object, instead of getting mixed together on the canvas space.

However, don’t take things on that make a fun project too overwhelming – if you don’t need any of that, definitely don’t do it. Just add aspects to a refactor so that they improve the code style or add features in ways that you care about, one step at a time…

1 Like