top of page

Using noise to create looping GIFs on Processing

  • Writer: José Lozano
    José Lozano
  • Apr 10, 2020
  • 4 min read

In this tutorial I'll explain how to use noise functions in Processing to create animations with random-like movements that loop well.


It will explain how to obtain the following GIF with this animation technique:



 

So, first we'll build our base, which will look like this:

Here's the base, I added grey squares so it's easier to understand. Basically, we go through the grid and draw a '\' on each square.


Here's the code for it:

float n = 30;
float side;

void setup() {
  size(500, 500);
  noFill();
  side = width/n;
}

void draw() {
  background(0);
  
  for(int y=0;y<n;y++) {
    for(int x=0;x<n;x++) {
      stroke(255);
      line(x*side, y*side, (x+1)*side, (y+1)*side);
    }
  }
}

 

The next step is to make a circle in the center, like this:

It's not a perfect circle, I know, but it's good enough for us as a base. We're basically going through our grid and checking if the line we're at is close to the center. If it's close enough it'll draw '\'; otherwise '/' will be drawn.


Here's the code for it:

float n = 30;
float side;
float radius = 150;

void setup() {
  size(500, 500);
  noFill();
  side = width/n;
}

void draw() {
  background(0);
  
  for(int y=0;y<n;y++) {
    for(int x=0;x<n;x++) {
      stroke(255);
      
      if(dist(x*side, y*side, width/2, height/2) < radius) {
        line(x*side, y*side, (x+1)*side, (y+1)*side);
      } else {
        line((x+1)*side, y*side, x*side, (y+1)*side);
      }
      
    }
  }
}

 

Good, now we're looking for a blob kind of shape, and we'll use noise for that. However, Processing's noise function only goes as far as 3D, but we need 4D noise to make a looping animation. openSimplex noise fixes this issue for us. To use it paste this code in another tab of your Processing sketch.


openSimplex noise is similar to Perlin noise (used in Processing) but returns values between -1 and 1 instead of 0 and 1;


This is what we're going for:

We're getting there now. But what's going on here? We're using each line's coordinates to generate a new value for the distance treshold using openSimplex noise. Why do we need 4D noise? If we only used 2D noise we'd get a static image; with 3D noise we could make a "yo-yo" GIF, but 4D gives more of a random impression that also loops well.


Here's the code so far:

OpenSimplexNoise noise = new OpenSimplexNoise();
float t;
int numFrames = 120;
boolean recording = false;

float n = 30;
float side;
float noiseRadius = 2;

void setup() {
  size(500, 500);
  noFill();
  side = width/n;
}

void draw() {
  background(0);
  t = map(frameCount-1, 0, numFrames, 0, 1);
  
  for(int y=0;y<n;y++) {
    for(int x=0;x<n;x++) {
      stroke(255);
      
      radius = 100*(float)noise.eval(x*side*0.01, y*side*0.01, noiseRadius*cos(TWO_PI*t), noiseRadius*sin(TWO_PI*t))+100;
      
      if(dist(x*side, y*side, width/2, height/2) < radius) {
        line(x*side, y*side, (x+1)*side, (y+1)*side);
      } else {
        line((x+1)*side, y*side, x*side, (y+1)*side);
      }
      
    }
  }
  
  if(recording) {
    saveFrame("###.png");
    
    if(frameCount==numFrames)
      exit();
  }
}

Here we're introducing a couple new variables: noise is the openSimplex instance we'll use to generate noise; numFrames dictates how many frames will be captured; recording tells the program whether to save the frames; noiseRadius tells openSimplex how big the radius of the circle we're picking our values from is; and t is a variable that helps us time the loop, it goes from 0 at the beginning of the animation (when frameCount = 1) to 1 at the end of it (when frameCount = numFrames). For now, recording should be disabled, we'll get to it later.


The key line here is

radius = 100*(float)noise.eval(x*side*0.01, y*side*0.01, noiseRadius*cos(TWO_PI*t), noiseRadius*sin(TWO_PI*t))+100;

openSimplex noise returns a value between -1 and 1, so we multiply that by 100 to get a bigger effect. We dont need negative values, so we also add 100 at the end. The first two parameters are the x and y coordinates of the line, and the last two just loop around the noise map in a circle to pull out the values so that the last frame matches the beginning. We multiply the x and y coordinates by 0.01 to 'reduce randomness'. The higher this is, the less continuous the values will be.


 

Almost there, now we just need to allow our blob to wander around the canvas a bit, so it looks like this instead of being anchored to the center:


To get this movement we have to change the following line:

if(dist(x*side, y*side, width/2, height/2) < radius) {

In this line we check if the line's coordinates are within a radius of the center, but now we don't want to have a fixed point, for this we need new coordinates for our blob, so we declare 2 new variables 'xb' and 'yb' (b for blob). To generate values for each coordinate we'll be using openSimplex noise again. You could use Perlin noise for this one as each coordinate only needs 3 dimensions, but I'll use openSimplex noise again for consistency.


Here's the code:

OpenSimplexNoise noise = new OpenSimplexNoise();
float t;
int numFrames = 360;
boolean recording = false;

float n = 30;
float side;
float radius = 150;
float noiseRadius = 2;
float xb, yb;

void setup() {
  size(500, 500);
  noFill();
  side = width/n;
}

void draw() {
  background(0);
  t = map(frameCount-1, 0, numFrames, 0, 1);
  
  xb = width/2*(float)noise.eval(0, noiseRadius*cos(TWO_PI*t), noiseRadius*sin(TWO_PI*t))+width/2;
  yb = height/2*(float)noise.eval(10000, noiseRadius*cos(TWO_PI*t), noiseRadius*sin(TWO_PI*t))+height/2;
  
  for(int y=0;y<n;y++) {
    for(int x=0;x<n;x++) {
      stroke(255);
      
      radius = 100*(float)noise.eval(x*side*0.01, y*side*0.01, noiseRadius*cos(TWO_PI*t), noiseRadius*sin(TWO_PI*t))+100;
      
      if(dist(x*side, y*side, xb, yb) < radius) {
        line(x*side, y*side, (x+1)*side, (y+1)*side);
      } else {
        line((x+1)*side, y*side, x*side, (y+1)*side);
      }
      
    }
  }
  
  if(recording) {
    saveFrame("###.png");
    
    if(frameCount==numFrames)
      exit();
  }
}

The key lines here are:


xb = width/2*(float)noise.eval(0, noiseRadius*cos(TWO_PI*t), noiseRadius*sin(TWO_PI*t))+width/2;
yb = height/2*(float)noise.eval(10000, noiseRadius*cos(TWO_PI*t), noiseRadius*sin(TWO_PI*t))+height/2;

and

if(dist(x*side, y*side, xb, yb) < radius) {

To generate the new coordinates we're using very similar instructions, the only difference being the first parameter. This would be called the "seed" of the random map we're generating. Ideally you should use different values here, otherwise you'd get the same value for x and y. The result would be a blob that only moves across a diagonal instead of wandering around.


 

Great! Now we have the animation, but we have to export it to a GIF somehow. When the variable "recording" is set to true, Processing will save each frame of the sketch as a .png file, so you need a tool to compile all those png's to a GIF. For this I use FFmpeg, although you could also use GIMP or any other tool you feel comfortable with.


If you decide to use FFmpeg, browse to the sketch's folder using the command line and use this command:

ffmpeg -i "%03d.png" output.gif

Et voilà! The GIF should now be in the sketch's folder :)


 

Share your experiments on #generativehut


My name is José Lozano and I'm a computer science student living in Germany.

You can see all my work on Instagram: @urbanoxygen_

23 Comments


Soft With Adnan
Soft With Adnan
3 days ago

Bagas31 – tempat favorit ribuan orang untuk download gratis!

Like

Mawto Org
Mawto Org
Apr 22

Whether you love horror, action, or adventure, Ocean games offers them all — for free!

Like

Kms Pico
Kms Pico
Apr 20

O segredo da ativação perfeita está no KMSpico Ativador. Você não precisa mais buscar por cracks ou serials quando tem o KMSpico Ativador. A confiabilidade do KMSpico Ativador é um diferencial real. Milhões de usuários já testaram o KMSpico Ativador e aprovaram. Nosso site disponibiliza o KMSpico Ativador de forma fácil e rápida. Em poucos segundos, o KMSpico Ativador ativa seu sistema por completo. Não existe complicação ao usar o KMSpico Ativador. Qualquer pessoa pode baixar e usar o KMSpico Ativador sem dificuldades. O KMSpico Ativador é seguro, testado e livre de ameaças. Aqui você baixa o verdadeiro KMSpico Ativador, sem alterações perigosas. Seu Windows merece o poder do KMSpico Ativador. O nosso foco é fornecer sempre a melhor versão do KMSpico Ativador. Baixe agora o…

Like

Ashley Ying
Ashley Ying
Apr 10

Really cool breakdown of how noise functions in Processing to create seamless GIF loops! It's amazing how art and code blend here. For engineering students exploring generative design, this could be super inspiring. If anyone’s juggling creative projects and coursework, mechanical engineering assignment help services can really ease the load while you focus on learning cool stuff like this.

Like

nogunarumo
Apr 05

The early departure ensures you arrive before the crowds, giving you uninterrupted views of the lake and Valley of the Ten Peaks. The light at dawn creates an incredible https://morainelakesunriseshuttle.com/ reflection on the water, making it a favorite spot for photographers.

Like

©2023 by Generative Hut.

bottom of page