Using noise to create looping GIFs on Processing
- 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.gifEt 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_




Before their recent journey, my mother and father wanted to make sure every travel arrangement was properly managed so they could avoid confusion and unnecessary stress before departure. Since traveling often includes many important procedures, they had several questions regarding baggage allowance, boarding instructions, airport formalities, travel documents, check-in timing, ticket confirmation, and flight-related details. To receive proper guidance before the trip, they decided to contact the Delta Airlines Edinburgh Office for assistance. From the very beginning, the customer support representatives treated them with patience, professionalism, and kindness while explaining every important travel detail in a clear and easy-to-understand manner, which immediately helped my parents feel more relaxed and confident about the journey.
Hi, surprising my mother with her first overseas vacation as a birthday present was one of the most poignant experiences for me. Seeing my parents get ready for a trip abroad seemed very special because they had always prioritized family obligations and hardly ever considered traveling for themselves. Guidance from the Lufthansa Airlines Office in Dulles, Washington made the procedure easier for them because it was their first time traveling overseas. Concerns about airport procedures, luggage restrictions, and travel arrangements were patiently addressed by the staff. My parents felt more confident and less anxious thanks to their expert advice. My parents had a great time exploring and making wonderful memories during the vacation, which added to the significance of the birthday…
Mình có lần lướt đọc mấy trao đổi trên mạng شيخ روحاني thì thấy nhắc nên cũng tò mò mở ra xem thử cho biết. Mình không tìm hiểu sâu rauhane chỉ xem qua trong thời gian ngắn để quan sát bố cục s3udy cách sắp xếp các mục và trình bày nội dung tổng thể. Cảm giác là các phần được trình bày khá gọn, các mục rõ ràng nên đọc lướt cũng không bị rối Berlinintim, với mình như vậy là đủ để nắm tin cơ bản rồi. q8yat
Fire force jacket reflects bravery and power. Inspired by fire squad aesthetics, it delivers modern style with anime authenticity and lasting quality.
This is an incredibly clear breakdown of how to move from a static grid to a fluid, looping animation! I've always struggled to wrap my head around using 4D noise for seamless loops, but the way you explained mapping the time variable to a circle using cos(TWO_PI*t) and sin(TWO_PI*t) finally made it click for me. I can definitely see how this technique could be used to generate dynamic, ambient background visuals for digital displays. Actually, I was just thinking these kinds of organic, ever-shifting patterns would look amazing projected onto walls or LED screens at a party or corporate gathering—definitely a unique concept to pitch to an event hire company looking for custom interactive visuals! Thanks for sharing the code snippets, I'm excited…