Explanation for the algorithm behind this generative art piece

I recently came across this beautiful generative art piece, ’Trunk’ by Kjetil Golid (source code).

I went through the code and understand what’s being done, yet I’m unable to grasp why the algorithm works the way it does. Can someone explain it to me?

I even ported it to Java from Javascript, in case any of you would like to run it without having to set up an html page. I 've also annotated all the variables that made sense to me.

int rings = 50; // number of rings
float dim_init = 50; // size of innermost ring
float dim_delta = 4; // distance between two adjacent rings

float chaos_init = 0.2; // noise on innermost ring
float chaos_delta = 0.12; // change in ring noise as you move outwards
int chaos_mag = 20; // magnitude of noise

float ox = random(10000);
float oy = random(10000);
float oz = random(10000);

void setup() {
  size(700, 700);

void draw() {
  background(255, 255, 255);
  translate(width/2, height/2);
  //ox += 0.04;
  oy -= 0.02;
  oz += 0.01;
  for (int i=0; i<rings; i++) {
    for(int angle=0; angle<360; angle++){
      float radian = radians(angle);
      //stroke(map(angle, 0, 360, 255, 0), map(i, 0, rings, 255, 0 ), map(angle*i, 0, 360*rings, 255, 0));
      float radius = (chaos_mag*getNoiseWithTime(radian, chaos_delta*i+chaos_init, oz)) + (dim_delta*i+dim_init);
      vertex(radius*cos(radian), radius*sin(radian));

float getNoiseWithTime(float radian, float dim, float time) {
  float r = radian % TWO_PI; // r repeats every 360 degrees
  if (r<0) {r += TWO_PI;}
  return noise(ox+cos(r)*dim, oy+sin(r)*dim, oz+time);

Thanks! Do you have an editor.p5js.org or openprocessing.org link for that? The forum also accepts direct embeds.

EDIT: Ah, to Java from JS. Nevermind!

I don’t know which part you don’t understand, so I can’t try to help explain whatever part that is. Can you be more specific about what effect or what operation you are curious about?

Thank you for responding!

These are the bits I don’t understand - the computation of the radius, noise and vertex. The math is easy to read, but I can’t get an intuitive grasp over how it affects the final output.

1 Like

Look at this in draw:

  //ox += 0.04;
  oy -= 0.02;
  oz += 0.01;

Try disabling oy, and oz, and both, and rerunning.

Do this in combination with replacing this line:

return noise(ox+cos(r)*dim, oy+sin(r)*dim, oz+time);

with one of these instead:

  return noise(ox+cos(r)*dim, 0, oz+time);
  return noise(0, oy+sin(r)*dim, oz+time);
  return noise(ox+cos(r)*dim, 0, 0);
  return noise(0, oy+sin(r)*dim, 0);

This should help you quickly break down the 5 dimensions involved – x, y, z, and changes in time to y and z.


You can also understand what

float getNoiseWithTime(float radian, float dim, float time) {

is doing by replacing it with return random(1); or (0) (disabling).

1 Like

So, for each vertex in each circle, we want to add a bit of noise. We sample that noise from a corresponding location on 2D slice of a perlin noise field – we can think of those values as like a topological map, locally correlated into mountain ranges and valleys. Now we have rings with perlin wobbles, and the wobbles on the rings line up with the wobbles on the 2D field they were sampled from.

But we want the wobbles to “drip” – so we change oy over time, moving across the perlin surface (slide the mountains) and sampling from a gradually different place.

But we also want the wobbles to change as they drip. Instead of sampling the same slice, we are sinking bobbing up and down in a 3D perlin space like a submarine with oz, and the mountains slowly change their contours.


Hi, sorry for taking a while to reply!

Your suggestions were very helpful, and while tinkering around with the variables I realized that I had no clue how Perlin noise worked. I did my research though, and now that I have the necessary context I can understand the code and the idea behind it.

Thank you for the help. :slight_smile:

1 Like