Getting started with making Processing GIFs, and using the beesandbombs template.

I want to make a quite simple tutorial that will help people to get started with the basic GIF making techniques and templates I use.

This tutorial is mainly aimed at people who have never made any perfectly looping GIF with Processing, but already know how to draw something with Processing. However there may be interesting stuff for people who are not beginners, so skip parts when it is too basic for you.

If you don’t already know how to draw things with Processing, check out The Coding Train channel on Youtube by Daniel Shiffman. If you already know how to program in one language, the Coding Challenges will be especially interesting to quickly learn how to use Processing to draw some cool stuff (that’s how I started). If not there are also videos that start from zero, you don’t need to have programmed before. I think it starts with this video (I have never watched those because I had already programmed a lot before).

How to make a basic perfectly looping GIF

This first part will show how to make a GIF of a white dot looping in a circle over a black background.

Here is the code, I will comment it later.

int numFrames = 50;

void setup(){
 size(500,500);
}

void draw(){
  background(0);

  float t = 1.0*(frameCount-1)/numFrames;

  stroke(255);
  strokeWeight(10);

  point(250+50*cos(TWO_PI*t),250+50*sin(TWO_PI*t));

  if(frameCount<=numFrames){
    saveFrame("fr###.png");
  }
}

This code generates the frames of the GIF when you launch it, and you can see the animation at the same time.

The variable numFrames obviously contains the number of frames of the GIF.

The variable t represents time. The idea is that it goes from 0 (at the beginning of the animation), to 1 at the end. This is a convention that I took from the beginning and I observed that beesandbombs (who is a famous Processing gif maker) also uses it, so it is probably a good idea. “frameCount” is a Processing variable that starts at 1 the first time “draw” is called and that is incremented each time “draw” is called. Here t won’t reach 1 when frameCount will be equal to numFrames (last frame), but that’s to avoid the repetition with the first frame : we will draw the same thing with t=0 and t=1, and to have a smooth perfect loop, we need to save the t=0 or t=1 frame only one time. Here t will be greater than 1 after the frames are saved, if you ever want it to go back to 0 you can use “(1.0*(frameCount-1)/numFrames)%1” instead.

“point(250+50*cos(TWO_PI*t),250+50*sin(TWO_PI*t));” draws a point using the parametric equation of a circle of radius 50 pixels centered on the pixel of location (250,250). cos and sin are two pi periodic, so we have correctly the same position for t=0 and t=1 to have a perfect loop.

“saveFrame(“fr###.png”);” automatically saves a frame in the folder of the sketch, with a number equal to the frame count.

Now that you have your frames saved, there are lots of way to create your final GIF. For example you can use GIMP, Photoshop or Gifsicle. I use GIMP, it’s probably not the best way to do it but it works well for me. You just have to import the frames as layers, and then export as .gif. Then you have to say that you want your GIF to be an animation, and choose the delay between frames. I often choose 35 ms. Here is the result of the previous code with 20 ms delay between frames :

agif2.gif

Motion blur with template from beesandbombs

You can already make nice things with the previous technique, but now I most often use the technique from the code template of beesandbombs (that he shares publicly). I talked with him about it, and he has no problem if I use it a lot, and now he even told me that I could make a tutorial about it. His code is about having two types of execution when you launch yur sketch. Either you change the time variable with your mouse, or you render your frames with motion blur. I think that motion blur can greatly increase the beauty of GIFs and that’s why I keep using it. His code also contains little functions and constants that can be useful.

What does motion blur mean algorithmically? It’s just an average on many drawings that are close in time.

Here is the code to render the previous white dot in beesandbombs style with motion blur :

int[][] result;
float t, c;

float ease(float p) {
  return 3*p*p - 2*p*p*p;
}

float ease(float p, float g) {
  if (p < 0.5) 
    return 0.5 * pow(2*p, g);
  else
    return 1 - 0.5 * pow(2*(1 - p), g);
}

float mn = .5*sqrt(3), ia = atan(sqrt(.5));

void push() {
  pushMatrix();
  pushStyle();
}

void pop() {
  popStyle();
  popMatrix();
}

void draw() {

  if (!recording) {
    t = mouseX*1.0/width;
    c = mouseY*1.0/height;
    if (mousePressed)
      println(c);
    draw_();
  } else {
    for (int i=0; i<width*height; i++)
      for (int a=0; a<3; a++)
        result[i][a] = 0;

    c = 0;
    for (int sa=0; sa<samplesPerFrame; sa++) {
      t = map(frameCount-1 + sa*shutterAngle/samplesPerFrame, 0, numFrames, 0, 1);
      draw_();
      loadPixels();
      for (int i=0; i> 16 & 0xff;
        result[i][1] += pixels[i] >> 8 & 0xff;
        result[i][2] += pixels[i] & 0xff;
      }
    }

    loadPixels();
    for (int i=0; i<pixels.length; i++)
      pixels[i] = 0xff << 24 | 
        int(result[i][0]*1.0/samplesPerFrame) << 16 | 
        int(result[i][1]*1.0/samplesPerFrame) << 8 | 
        int(result[i][2]*1.0/samplesPerFrame);
    updatePixels();

    saveFrame("fr###.png");
    println(frameCount,"/",numFrames);
    if (frameCount==numFrames)
      exit();
  }
}

//////////////////////////////////////////////////////////////////////////////

int samplesPerFrame = 5;
int numFrames = 50;        
float shutterAngle = 1.5;

boolean recording = true;

void setup(){
  size(500,500);
  result = new int[width*height][3];
  
}

void draw_(){
  background(0);

  stroke(255);
  strokeWeight(10);

  point(250+50*cos(TWO_PI*t),250+50*sin(TWO_PI*t));
  
}

IMPORTANT UPDATE : wordpress can't display the code well, probably because of the weird symbols, here is a code that works : templateexample.pde

This can look scary but what it does is actually not complicated, I will explain it, but first let’s have a look at our new result :

agif.gif

Now let’s explain the code.
First of all you don’t need to understand the code above the “//////” to use it, just understand what it does.

The function “draw_” has to be used like you would normally use “draw”, and you have to draw something depending on t which is the time variable. t goes from 0 to 1 like previously and is set by the code above the “//////”.

boolean recording = true;

This variable is very important : if you set it to true, you will record the gif frames with motion blur. If you set it to false, you will control time with your mouse (0 on one side of the canvas, 1 on the other side), and there won’t be motion blur : it’s for testing your sketch without rendering the frames.

Now come the other important variables :

int samplesPerFrame = 5;
int numFrames = 50;        
float shutterAngle = 1.5;

“numFrames” is the number of frames of the gif, like previously.
“samplesPerFrame” is the number of drawings that are used to produce a single final frame that will be an average of them for motion blur. Increasing it increases the quality of motion blur but costs more rendring time.
“shutterAngle” controls how spaced in time the samples of each frames are. If shutterAngle is equal to 1.0, the last sample of a frame will be just before the first sample of the next frame. If it is greater than 1.0, the samples of each frame will overlap in time with the samples of the neighboring frames. If it is smaller than 1.0 it will be more separated… If you want an explanation with code it is contained in this line (sa is the index of sample) :

t = map(frameCount-1 + sa*shutterAngle/samplesPerFrame, 0, numFrames, 0, 1);

I took shutterAngle equal to 1.5 to exagerrate motion blur, but usually beesandbombs and me use values smaller than 1.0.

The array “result” contains the sums of the samples and is used to compute the motion blur average.

Take care that when “draw_()” is called once again, the coordinate changes that you may have done with something like “translate” are not reset because it’s not the real “draw()”. You have to use “push()” and “pop()” around your coordinate changes.

I now want to prove my point that motion blur can change a lot the effect of a gif. Here is one I made, without and with motion blur. Notice how shades of grey appeared with motion blur.
agif5_2.gif
agif5.gif

I’ll come back to the important “ease” function from this template later in this tutorial : I want to talk about less technical stuff first.

Using an array of objects

The previous gif was very boring, right? Let’s try do to something more interesting from there without so much code, by using a technique that made me produce lots of gifs at the beginning : using an array of objects. So let’s have many dots moving at random positions with random parameters. It may look like quite a lot of new code but nothing really technical and everything is really worth understanding. This kind of code is well introduced by Daniel Shiffman’s videos.

//////////////////////////////////////////////////////////////////////////////

int samplesPerFrame = 5;
int numFrames = 50;        
float shutterAngle = 1.5;

boolean recording = false;

int n = 3000;

class Thing{
  float x = random(100, 400);
  float y = random(100, 400);
  float radius = random(2,15);
  float size = random(1,2.5);
  float offset = random(0, TWO_PI);
  
  void show(){
    stroke(255);
    strokeWeight(size);
    point(x + radius*cos(TWO_PI*t + offset), y + radius*sin(TWO_PI*t + offset));
  }
}

Thing[] array = new Thing[n];

void setup(){
  size(500, 500, P3D);
  result = new int[width*height][3];
  
  for(int i=0;i<n;i++){
    array[i] = new Thing();
  }
}

void draw_(){
  background(0);

  for(int i=0;i<n;i++){
    array[i].show();
  }
}

Result :
agif2.gif
The offset attribute is there to avoid having all the dots at the same angle at the same time.

Now a trick is to use the “noise” function of Processing instead of “random” : it is a kind of continuous randomness and it gives more structure.

I just change those lines :

  float offset = 9*noise(0.02*x, 0.02*y);

and that one to use a little of transparency (not very pedagogical, just felt liek doing this haha) :

    stroke(255,200);

Result :
agif3.gif
Daniel Shiffman has videos that are great to introduce you to this noise function (Perlin noise) if you don’t know it. I also have tutorials about it on this blog.

Now let’s talk about one last thing that can be nice with this kind of gif : using a constructor. It can lead to less randomness and more structure. Here is an example :

//////////////////////////////////////////////////////////////////////////////

int samplesPerFrame = 5;
int numFrames = 75;        
float shutterAngle = 1.0;

boolean recording = true;

int n = 100;

class Thing{
  float size = random(1,5);
  float offset = random(0, 0.6);
  float theta;
  
  Thing(float theta_){
    theta = theta_;
  }
  
  void show(){
    stroke(255,200);
    strokeWeight(size);
    float radius = 150 + 80*sin(TWO_PI*t + offset);
    float x = 250 + radius*cos(theta);
    float y = 250 + radius*sin(theta);
    point(x, y);
  }
}

Thing[] array = new Thing[n];

void setup(){
  size(500, 500, P3D);
  result = new int[width*height][3];
  
  for(int i=0;i<n;i++){
    float theta = TWO_PI*i/n;
    array[i] = new Thing(theta);
  }
}

void draw_(){
  background(0);

  for(int i=0;i<n;i++){
    array[i].show();
  }
}

Result :
agif.gif
Notice how the dots are regularly spaced with their angle. Nothing outstanding, it was just to show the use of a constructor.

I just wanted to introduce you to this code structure, that is also some kind of template.

About the easing function in the template

There are two functions called “ease” in the template. I only use the second one but use it a lot. I’m going to explain it a little.

float ease(float p, float g) {
  if (p < 0.5) 
    return 0.5 * pow(2*p, g);
  else
    return 1 - 0.5 * pow(2*(1 - p), g);
}

p is basically a parameter between 0 and 1 (like t), that will be distorted by the easing function "ease", with the intensity g. It's very useful to get smooth transitions. When I didn't know this precise function, I used something similar for the same purpose, and I'm going to share it because the formula is easier to understand :

float ease(float p) {
return (1-cos(PI*p))/2;
}

Here is a gif showing the curve of the easing function with the parameter g oscillating between 1 and 5. (abscissa and ordinate shown from 0 to 1).
agif.gif
You can notice that the derivative is 0 at p=0 and p=1 (excepted when g = 1.0), that’s why it is so useful to make smooth transitions.
(Code to generate this gif : here)
This function may be a little abstract but it’s just incredibly useful.

For example easing was very useful to make smooth transitions in that gif :
tumblr_olii4cUawi1w3y4ilo2_r1_500.gif

Let’s use “ease” in the previous circle gif code (g = 3.0):

//////////////////////////////////////////////////////////////////////////////

int samplesPerFrame = 5;
int numFrames = 50;        
float shutterAngle = 1.5;

boolean recording = true;

void setup(){
  size(500,500);
  result = new int[width*height][3];
  
}

void draw_(){
  background(0);

  stroke(255);
  strokeWeight(10);
  
  float t2 = ease(t, 3.0);

  point(250+50*cos(TWO_PI*t2),250+50*sin(TWO_PI*t2));
  
}

Result :
agif.gif

Patakk, who has made awesome Processing gifs made a nice post about this “ease” function on his tumblr :

Update : that ease function is useful for much more things than what I show here, I may add other examples later.

Now that we’re talking about a very useful function : map, lerp and constrain (explained in the Processing reference) are also often very useful.

GIF compression

Finally just in case you want to upload some gifs on tumblr, you may at some point have troubles fitting into the 3 MB file size limit.
Here are some tricks to reduce file size :
– Use https://ezgif.com/optimize
– Have less frames
– Have less differences between successive frames
– Have less colors
– Have a smaller resolution

I hope this will helpful and that you learnt something, thanks for reading. The usual code template I use is actually different from the one I present here because I usually use openSimplex noise (I present it in this tutorial).

Advertisements

A trick to get looping curves with lerp and delay

Intro, goal, examples

The following gifs use the same trick to draw some curves in Processing and I will do my best to try explaining it here. I’ll link their code and describe them at the end.

(I put them here in chronological order of creation)

manymovingonefixed.gif

10allconnected.gif

lotslittleconnections.gif

approxsun.gif

propafight.gif

rotatedstuff.gif

movinginthemiddle.gif

Some of them look quite complex but you’ll see the maths behind them isn’t complicated, and I’ll try to explain everything slowly.

Some basic code to start with

Here is some code to generate the frames of a gif showing two disks moving in circles (one twice faster than the other one) :

int numFrames = 100;

void setup(){
  size(500,500,P3D);

  stroke(255);
  fill(255);
}

float x1(float t){
  return 0.25*width + 50*cos(TWO_PI*t);
}
float y1(float t){
  return 0.5*height + 50*sin(TWO_PI*t);
}

float x2(float t){
  return 0.75*width + 50*cos(2*TWO_PI*t);
}
float y2(float t){
  return 0.5*height + 50*sin(2*TWO_PI*t);
}

void draw(){
  float t = 1.0*(frameCount - 1)/numFrames;

  background(0);

  ellipse(x1(t),y1(t),6,6);
  ellipse(x2(t),y2(t),6,6);

  println("saving frame " + frameCount + "/" + numFrames);
  if(frameCount<=numFrames) saveFrame("fr###.png");
  if(frameCount == numFrames) stop();
}

Here is the result :

agif1.gif

It might be quite a lot of code already but there won’t be much more code do draw the curves. I use P3D because it renders things more smoothly in some cases.

For all the rest of this tutorial, the disks must have any trajectory that loops well.

The lerp function

A fundamental function/thing in the curve trick is the use of the function lerp of Processing, that does linear interpolation. Skip this part if you already know it !

In case you have not heard about it, lerp(a,b,t) = (1-t)*a + t*b. when t=0 it returns a, when t=1 it returns b, when t is between 0 and 1 it returns a value between a and b linearly.

If you still don’t feel familiar with that, here is a video of Daniel Shiffman explaining it : https://www.youtube.com/watch?v=8uLVnM36XUc
I haven’t even watched it but I know he’s great at explaining stuff.

Drawing a line between two points with little points and lerp

Let’s draw lots of little transparent white points between the two disks. This trick seems useless so far but then when the curve won’t be a line, it will give cool shades of grey, that you can’t get easily with beginShape() endShape() (I think).

Added code :

int m = 1000;

(number of little points)
and :

  pushStyle();
  strokeWeight(2);
  stroke(255,100);
  for(int i=0;i<=m;i++){
    float tt = 1.0*i/m;

    float x = lerp(x1(t),x2(t),tt);
    float y = lerp(y1(t),y2(t),tt);

    point(x,y);
  }
  popStyle();

(inside draw())
If you understood lerp there isn't much more to understand there. I use the variable name "tt" because I want to call it "t" but it's already taken by time. Really I don't know what more to say about this code.
Link to full code

Result :
agif2.gif

Seeing the disks with a delay

The idea is that the disk will be seen with a delay kind of proportional with the distance to the disk. Let’s begin to use this delay trick for the first disk only.

Let’s add this to the code :
This parameter :

float delay_factor = 2.0;

and :

    float x = lerp(x1(t - delay_factor*tt),x2(t),tt);
    float y = lerp(y1(t - delay_factor*tt),y2(t),tt);

(x1 and y1 seen with a greater delay when tt is greater, no delay when tt = 0)

Here is the result :
agif3.gif

With delay_factor = 1.0 :
agif3_1.gif

With delay_factor = 5.0 :
agif3_5.gif

Link to full code

Seeing both disks with a delay

Now let’s have a delay on both disks.

The only change to make is :

    float x = lerp(x1(t - delay_factor*tt),x2(t - delay_factor*(1-tt)),tt);
    float y = lerp(y1(t - delay_factor*tt),y2(t - delay_factor*(1-tt)),tt);

You can use different delay factors if you want…

Result with delay_factor = 1.0 :
agif4.gif

Link to full code

Using openSimplex noise for smooth random curves

Let’s have a perfectly looping random trajectory for the first disk. To understand this I advise to check out my previous tutorial “Drawing from noise, and then making animated loopy GIFs from there”, but you may understand anyway if you don’t have time/motivation for that.

To use openSimplex noise, put this code in another Processing tab of your sketch.

Also put this in setup() :

noise = new OpenSimplexNoise();

and define it before setup() :

OpenSimplexNoise noise;

Now we can change the code for x1() and y1() :

float motion_radius = 0.5;
float x1(float t){
  float seed = 1337;
  return 0.25*width + 150*(float)noise.eval(seed + motion_radius*cos(TWO_PI*t),motion_radius*sin(TWO_PI*t));
}
float y1(float t){
  float seed = 1515;
  return 0.5*height + 150*(float)noise.eval(seed + motion_radius*cos(TWO_PI*t),motion_radius*sin(TWO_PI*t));
}

We go through simplex noise to get horizontal and vertical values with a radius equal to motion_radius.

Result :
agif5.gif
(where the seond disk doesn’t move to show clearly the propagation)
agif5_2.gif
(where the second disk moves like before)
agif5_3.gif
(where “motion_radius” has been increased)

I also actually changed some other parameters… here is the full code.

Explanation and code for more complex gifs

I’ll give a link to the code of each gif, but I warn you : it uses the motion blur system of beesandbombs (Dave Whyte), it might be experimental code with bad variable names, unecessary stuff, details that may be hard to understand, or just stupid stuff, or there might be some mistakes.

Gif 1

manymovingonefixed.gif
This gif simply uses an array of disks that have a trajectory based on simplex noise like previously, with different seeds for each disk. A curve is drawn with the trick between each disk and a point at the bottom that doesn’t move. Each disk has a method that gives the position depending on time, like array[i].x(t) makes sense.

Code

Gif 2

10allconnected.gif
Here there is an array of 10 disks that have their simplex noise trajectory centered on points forming a circle. Like previously positions (with delay) can be obtained with array[i].x(t – f*tt).

Code
(parameters are different, I haven’t found back the right ones)

Gif 3

lotslittleconnections.gif
Here it is the same thing as Gif 2, excepted that trajectories are centered differently. Also there is higher opacity and stroke weight when the disks are closer to each other. Also now the delay factor is actually proportionnal to the distance between the disks (so an almost straight curve between them is drawn when they are close). I can’t remember well why I changed the curves like that but I remember I thought I needed it. (Update : it’s because when the two points are closer with the original curve trick, the curve will just be contracted, in order to avoid that I use a smaller “delay factor” when the points are closer).

Code for something similar

Gif 4

approxsun.gif
It is similar to the previous gif. The differences : the center of trajectories are generated randomly inside a circle, with a random angle and a random radius that has quite high probability to be close to the radius of the circle (I adjusted a probability distribution). When the center of trajectory is closer to the center of the canvas, the trajectory becomes larger (it can be so large that some disks stay far outside the circle), when it’s close to the circle it moves less.

Code
(I love the different results this one gives when you run it again)

Gif 5

propafight.gif
The only thing to talk about here might be the trajectory of the left point, and it’s in the code.

Code

Gif 6

movinginthemiddle.gif
The only thing really new here is the trajectory of the point in the middle. It is a simplex noise based trajectory, with an easing function to distort time.

Code

Gif 7

rotatedstuff.gif
Take one random moving curve generated with the tutorial trick between two disks, rotate it around the center of the canvas, add some time offset depending on the angle (and so that it loops well), and you can get this kind of gif.
I was inspired by this gif from echophon.tumblr.com.

Code

Conclusion

This was quite long for such a simple trick. I hope you understood well the trick, and that you’ll go further and come up with stuff I haven’t made yet. Thanks a lot for reading !

Things made by other people from this tutorial

Check out this codepen by Decatron where you can change the parameters like “delay factors” and see the results in real time.
Link : curve-experiment

Also check out this SketchpunkLabs video where my trick is used as a path in 3D.
Link : WebGL2 : 085 : Curved Paths

This gif by wavegrower on tumblr :

You can see he seems to have used a negative delay factor with three points 🙂

Generating animated gifs from random 2D transformations

Goal

The aim of this tutorial is to explain how to obtain with Processing the following kinds of GIF generatively from the same program, using random 2D transformations. This something I’ve been exploring since November 2017.

agif8.gif

agif19.gif

Or with another program :

agif16.gif

agif5.gif

It will first explain how to obtain those kinds of generative GIFs :

tumblr_ovwxf9p1Mo1w3y4ilo2_500.gif

tumblr_ozvnwojbQF1w3y4ilo1_500.gif

Preliminaries : static images from random 2D transformations

The algorithms presented in this tutorial are based on the work of Tomasz Sulej (http://generateme.tumblr.com, http://folds2d.tumblr.com).

Here is a tutorial that presents some 2D transformations and explains how to render them nicely with Processing :
https://generateme.wordpress.com/2016/04/11/folds/

It is important to understand the key ideas of this tutorial to understand mine, so I will sum them up. The idea is to draw the image of little dots of low opacity after applying a 2D transformation. The dots are initially in [-3.0,3.0]x[-3.0,3.0]. What is done to render the transformation can be seen as “folding a square”. The dots will concentrate at some places resulting in higher drawing opacity at those places. 2D transformation can also be called folds.

Also the code he uploaded on OpenProcessing here is fundamental. It uses the composition of several types of functions randomly (there are 21 types in this version), and each type of function can have random parameters, so the number of possibilities of drawings that can be generated explodes. You can click to skip the current drawing and draw a different one.

I advise to visit http://folds2d.tumblr.com to see what can be drawn from 2D transformations.

Now let’s see how to make gifs from there.

First approach : morphing with linear interpolation between folds

I can hardly explain it better than what’s in the title. The idea is to do linear interpolation between 3 transformation (only between 2 transformations at a time). Why 3? to get a nice cycle 1 -> 2 -> 3 -> 1 and not just 1 -> 2 -> 1 that would give a less interesting motion.

Here is what can be obtained using some transformation from the folds2d tutorial, with its rendering technique.
tumblr_ovwxf9p1Mo1w3y4ilo2_500.gif
(Lost the code for this version and too lazy to write it back, but it’s pretty straight forward)
Update : oh I actually found it back ! Here it is.

Also what can be done is to show the motion more slowly using many representations in a circle. It can look like this :

tumblr_ozvnwojbQF1w3y4ilo1_500.gif

tumblr_ozvm496uTw1vd2kqfo8_500.gif

tumblr_ozvm496uTw1vd2kqfo10_500.gif

I still have the code for that one : circularfoldsmorphing.pde
Some comments about this program :
I don’t advise you to understand all of this horrible code, but I can tell you how to use it : click to start generating again with other parameters and transformations. Most of the time it uses transformations that use randomness, and it doesn’t look nice.
It uses two random 2D transformations (obtained by random composition of functions from more than 170 types) and the identity transformation (no transformation). Also here it doesn’t fold a square but some points on 100 lines that start from the center of a circle. An easing function is used for smoother motion.

Second approach : using 2D transformations on moving lines

A second approach to make an animated gif from a 2D transformation is to apply it to moving lines.

Here is what we will draw with no transformation (still drawn with little dots of low opacity).
agif16.gif
The motivation behind such lines is that we want a perfectly looping motion.

With a random 2D transformation obtained by composing many types of functions randomly, it’s possible to obtain the following results from the same program :

agif19.gif

agif21.gif

agif20.gif

agif22.gif

Here is the code : foldingmovinglines2.pde

Comments about how to use this program :
The lines are being drawn one by one for each frame. So you can see structures being drawn one by one, and the animation is so that each structure will replace the next structure that will be drawn. That’s why you can know if the motion will be interesting or not, and if you have to click in order to generate another gif. Typically if structures are drawn on top of each other the motion will be uninteresting. Note that most often the results don’t look as nice as what I show here. The 2D transformations that are obtained by this algorithm are often not continuous and that can cause some structures to disappear brutally.

Another idea is to apply the 2D transformation to those lines (it uses a fade in opacity) :
agif4.gif

Code : foldingmovinglines.pde

Some results :
agif2.gif
agif6.gif

A more recent idea is to apply the 2D transformation to this :
agiftuto.gif
(still drawn with little dots of low opacity)

Code : foldingmovingparts.pde

Some results :
agif16.gif

agif22.gif

agif5.gif

More results here.

Third approach : using a moving 2D transformation in between other generative 2D transformations

I won’t explain this one much more than the title. Basically it uses a 2D transformation based on noise (that loops well), in between two 2D transformations obtained like previously. That technique gives a final 2D transformation that changes through time and that loops well. I obtained those results from the same program :

tumblr_p0njqbNmFm1w3y4ilo7_r1_500.gif

tumblr_p0njqbNmFm1w3y4ilo9_r1_500.gif

tumblr_p0njqbNmFm1w3y4ilo6_r1_500.gif

Conclusion

The second approach (but also the third one) to make gifs from 2D transformations shows that they can give some complex generative motion. An algorithm that obtains random 2D transformations by random composition of several types of functions can therefore lead to a great variety of shapes and motion. By increasing the resolution, reducing the opacity of the dots and using more of them, it’s probably possible to obtain some better looking results than what I present here at the price of more computation time.

Keyword of this tutorial : 2D transformation 🙂

Big thanks to Tomasz Sulej, I would have done nothing of this without him.

Drawing from noise, and then making animated loopy GIFs from there.

This tutorial will focus on explaining how to draw things from noise functions in Processing, and then will present an automatic way to produce GIFs that loop well from a noise-based drawing.

It will explain how to obtain the following GIFs with the same animation technique :

agif3opt.gif

agif4.gif

Most of my latest gifs have all used this same trick I will present here, so I thought it was worth sharing it.

About noise functions

First of all let’s explain a little bit noise functions.

Processing has a function noise() that produces values between 0 and 1, centered on 0.5 given some inputs. Those values are random, but will always be the same with the same inputs on the same noise seed (fixed when the program is launched). Another property is that noise is continuous : for close inputs you get nearly the same value. Computing noise values is fast, you don’t have to compute previous values. The noise function of Processing is sometimes called Perlin noise.

Let’s show with a quite short code what noise() gives us :

void setup(){
  size(500,500);
  background(0);
  stroke(255);
  noFill();
}

void draw(){
  float scale = 0.03;

  beginShape();
  for(int x = 0; x<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>&lt;width;x++){
    float y = height*noise(scale*x);
    vertex(x,y);
  }
  endShape();
}

Result :
tuto.jpg

The parameter scale is used because without it the values changed too fast.

Beware that the noise function is symmetrical (noise(x) = noise(-x)).

Everytime the sketch is launched, the curve looks different.
You can have the noise functions to give always the same result by using Processing’s function noiseSeed. Also check out the function noiseDetail to change the parameters of the noise function (for example its smoothness).

Here’s a trick : To draw another independant curve at the same time one can just take the noise values far away from previously (use noise(scale*x) first, then noise(scale*x + 1000) ).

That was 1-dimensional noise. 2-dimensional noise takes 2 float values and returns a value between 0 and 1. We can represent that by brightness level on a 2D picture.

void setup(){
  size(500,500);
  background(0);
  stroke(255);
  noFill();
}

void draw(){
  float scale = 0.01;

  for(int x = 0; x<width;x++){
    for(int y = 0; y<height;y++){
      stroke(255*noise(scale*x,scale*y));
      point(x,y);
    }
  }
}

Result :
tuto2.png

One way to visualize 3D noise is to use time as third dimension…

void setup(){
  size(500,500);
  background(0);
  stroke(255);
  noFill();
  noiseDetail(5);
}

void draw(){
  background(0);
  float scale = 0.01;

  loadPixels();
  for(int x = 0; x<width;x++){
    for(int y = 0; y<height;y++){
      float col = 255*noise(scale*x,scale*y,10*scale*frameCount);
      pixels[x + width*y] = color(col);
    }
  }
  updatePixels();

  if(frameCount<=50){
    saveFrame("tuto###.png");
  }
}

Result :
tuto1.gif

We’re getting closer to making animated GIFs that loop well but this doesn’t loop well so far.

To make it loop we’ll need 4-dimensional noise. Processing’s noise is limited to 3D so I’ll introduce openSimplex noise (code by Kurt Spencer). To use it paste this code in another tab of your Processing sketch.

openSimplex noise is similar to Perlin noise but returns values between -1 and 1 (centered on 0).

openSimplex noise can be used this way (back to the first example) :

OpenSimplexNoise noise;

void setup(){
  size(500,500);
  background(0);
  stroke(255);
  noFill();

  noise = new OpenSimplexNoise();
}

void draw(){
  float scale = 0.03;

  beginShape();
  for(int x = 0; x<width;x++){
    float ns = (float)noise.eval(scale*x,0);
    float y = map(ns,-1,1,0,height);
    vertex(x,y);
  }
  endShape();
}

Result :
tuto2.jpg

I actually used 2D noise here with 0 as second input because the implementation of openSimplex noise I have only has 2D,3D and 4D noise.

Before using my trick to make loopy animations, let’s draw something more interesting/different from noise…

Let’s use a threshold on the noise to draw pixels in black or white :

OpenSimplexNoise noise;

void setup(){
  size(500,500);
  background(0);
  stroke(255);
  noFill();

  noise = new OpenSimplexNoise();
}

void draw(){
  background(0);
  float scale = 0.02;

  loadPixels();
  for(int x = 0; x<width;x++){
    for(int y = 0; y0;
      float col = b?255:0;
      pixels[x + width*y] = color(col);
    }
  }
  updatePixels();

  saveFrame("tuto3.jpg");
}

Result :
tuto3.jpg

How to animate noisy stuff with a nice trick

Now here’s the trick to animate this with a perfect loop, a trick that will work with anything that uses 1D or 2D noise…
Replace noise.eval(scale*x,scale*y) by noise.eval(scale*x,scale*y,radius*cos(TWO_PI*t),radius*sin(TWO_PI*t)).

We make a circle in the two new dimensions of noise space to make the animation loop perfectly. It’s hard to have a graphical interpretation with 2D noise but from 1D noise, (noise.eval(scale*x,radius*cos(TWO_PI*t),radius*sin(TWO_PI*t))) would mean to take noise values on a line for each frame, and move this line along a cylinder as time increases…

So let’s apply this to the thresholded drawing :

OpenSimplexNoise noise;

void setup(){
  size(500,500);
  background(0);
  stroke(255);
  noFill();

  noise = new OpenSimplexNoise();
}

int numFrames = 75;

float radius = 1.0;

void draw(){
  float t = 1.0*frameCount/numFrames;

  background(0);
  float scale = 0.02;

  loadPixels();
  for(int x = 0; x<width;x++){
    for(int y = 0; y0;
      float col = b?255:0;
      pixels[x + width*y] = color(col);
    }
  }
  updatePixels();

  println(t);

  if(frameCount<=numFrames){
    saveFrame("tuto2###.jpg");
  }
  if(frameCount == numFrames){
    println("finished");
    stop();
  }
}

Result :
tuto2.gif

radius is a parameter that will control how much things will change in the animation.

We can animate the curve example with the same technique :

OpenSimplexNoise noise;

void setup(){
  size(500,500);
  background(0);
  stroke(255);
  noFill();

  noise = new OpenSimplexNoise();
}

int numFrames = 150;

float radius = 1.5;

void draw(){
  float t = 1.0*frameCount/numFrames;

  background(0);
  float scale = 0.02;

  beginShape();
  for(int x = 0; x<width;x++){
    float ns = (float)noise.eval(scale*x,radius*cos(TWO_PI*t),radius*sin(TWO_PI*t));
    float y = map(ns,-1,1,0,height);
    vertex(x,y);
  }
  endShape();

  println(t);

  if(frameCount<=numFrames){
    saveFrame("tuto2###.jpg");
  }
  if(frameCount == numFrames){
    println("finished");
    stop();
  }
}

Result :
tuto3.gif

Let’s apply the technique to the previous noisy brightness example. Here the result will look blurred because the implementation of openSimplex noise used here doesn’t have a lot of details (but that smoothness looks nice sometimes 🙂 ).

Code :

OpenSimplexNoise noise;

void setup(){
  size(500,500);
  background(0);
  stroke(255);
  noFill();

  noise = new OpenSimplexNoise();
}

int numFrames = 75;

float radius = 1.5;

void draw(){
  float t = 1.0*frameCount/numFrames;

  background(0);
  float scale = 0.02;

  loadPixels();
  for(int x = 0; x<width;x++){
    for(int y = 0; y<height;y++){
      float ns = (float)noise.eval(scale*x,scale*y,radius*cos(TWO_PI*t),radius*sin(TWO_PI*t));
      float col = map(ns,-1,1,0,255);
      pixels[x + width*y] = color(col);
    }
  }
  updatePixels();

  println(t);

  if(frameCount<=numFrames){
    saveFrame("tuto3###.jpg");
  }
  if(frameCount == numFrames){
    println("finished");
    stop();
  }
}

Result :
tuto4.gif

Now you must be convinced that every Processing drawing that uses 1D or 2D noise can be turned into a perfectly looping animation easily. I haven’t spent time explaining where the trick comes from, but I hope it will seem natural to you after thinking about it for a while.

I’m going to explain how I obtained some more complex and interesting gifs.
I’ll start with that one :
agif5.gif

White points are randomly generated inside a circle. Each one follows an horizontal and a vertical displacement based on 2D noise. Also the intensity of the displacement becomes 0 near the border (near the circle). The animation is done just by using the same 4D noise technique. The GIF also uses motion blur from @beesandbombs.

Here is the code to generate the frames of the GIF : noisetraj.pde
Note that drawCurve just draws a circle but it was coded so that it could be easily disorted too.

Let’s move to another example…
agif2.gif
Here some points start from a grid and draw their trajectory following a noise-based field. This field changes using the 4D-noise technique.
(Code)

agif.gif

Here a threshold on 2D noise determines if it will show ‘/’ or ‘\’. Animated with 4D noise once again.
(Code)

tumblr_oxqbzm52qK1w3y4ilo1_500.gif

The lines represent a 2D field based on 2D noise. Once again animated with the same trick.
(Code)

agif4.gif

That one is a little complex to do. Basically it thresholds 1D noise curves to determine if it will draw in black or white. Parameters to animate each column are different. A big disk is drawn in the center in exclusion mode.
(Code for something similar)

tumblr_oyjk1cPdef1w3y4ilo1_500.gif

Each line uses independant noise values from 1D noise, animated with 3D noise with the trick.
(Code)

That’s the end of this tutorial, thanks for having a look, I hope you liked it !

You can use noise to draw all sort of things so I hope you’ll come up with totally different animations 🙂

PS : openSimplex noise isn’t symmetrical and there isn’t any problem when using (radius*cos(TWO_PI*t),radius*sin(TWO_PI*t)) as inputs (since it uses positive and negative values, there will be an undesired effect if the noise is symmetrical – that could be avoided by shifting the circle).

Special thanks to Chris Ball for letting me know about openSimplex noise !

Selection of work from other people inspired by this tutorial

Golan Levin made a gif that explains how a value can be looped with noise :

Also I love those gifs by littlecuuub on tumblr, inspired by the tutorial :

And this one by echophon :

Animated gifs from vector fields as force fields

This tutorial is about generating this kind of gif :

agif4opt.gif

This is a variant of the previous flow field tutorial, so make sure to check it out first.

The difference with flow fields is that here the vector field will be used to accelerate the particles. It will be an acceleration field instead of speed field. Particles will keep their inertia and now trajectories can cross. To avoid particles going too fast, a coefficient is used to slow down the speed at each time step.

Here are the only changes from the flow field algorithm :

// slowdown coefficient on speed
float slowdown = 0.93;

initial speed for each path :

  // initial speed
  float vx = 0;
  float vy = 0;

new “update” method to Path :

  void update(){
    PVector res = field(x,y);
    vx += DT*res.x;
    vy += DT*res.y;
    vx *= slowdown;
    vy *= slowdown;
    x += DT*vx;
    y += DT*vy;
    positions.add(new PVector(x,y));
  }

If you decrease the “slowdown” coefficient particles will go slower and follow the field more like a speed field than acceleration field.

Here is the code to get the following example of result :
agif.gif

It uses this vector field :

float curvex(float q){
  return cos(q);
}

float curvey(float q){
  return sin(q);
}

PVector field(float x,float y){
  
  float amount = 30;
  
  float scl = 0.004;
  
  float ns = 50*noise(100+scl*x,100+scl*y);
  
  float fx = amount*curvex(ns);
  float fy = amount*curvey(ns);
  
  return new PVector(fx,fy);
}

The example in the beginning of the tutorial uses a field that uses centers, its code is here.

I hope you found this helpful and that you will come up with nice gifs. Please don’t hesitate to ask questions or suggest improvements.

Animated gifs from vector fields

This tutorial explains how to obtain this kind of gif with Processing :

agif.gif

Vector fields will give the speed of particles at any location (x,y), that’s why here they will be called “flow fields”.

THE ALGORITHM

The algorithm here computes trajectories (called paths here) and then animates particles following them.

Here is the code to generate an animation from a simple flow field, I’ll explain it later on. You can skip this part if you don’t want to know how the algorithm works and just want to change the flow field (but this might be useful to understand the parameters of the algorithm and change them).

/// This code starts with with the rendering system I took from @beesandbombs
/// (it also contains some useful functions and stuff)
/// You don't have to understand it
/// Just know that it does an average on many drawings
/// from drawings parametrized by the global variable t going from 0 to 1

int[][] result;
float t, c;

float ease(float p) {
  return 3*p*p - 2*p*p*p;
}

float ease(float p, float g) {
  if (p < 0.5) 
    return 0.5 * pow(2*p, g);
  else
    return 1 - 0.5 * pow(2*(1 - p), g);
}

float mn = .5*sqrt(3), ia = atan(sqrt(.5));

void push() {
  pushMatrix();
  pushStyle();
}

void pop() {
  popStyle();
  popMatrix();
}

void draw() {

  if (!recording) {
    t = mouseX*1.0/width;
    c = mouseY*1.0/height;
    if (mousePressed)
      println(c);
    draw_();
  } else {
    for (int i=0; i<width*height; i++)
      for (int a=0; a<3; a++)
        result[i][a] = 0;

    c = 0;
    for (int sa=0; sa<samplesPerFrame; sa++) {
      t = map(frameCount-1 + sa*shutterAngle/samplesPerFrame, 0, numFrames, 0, 1);
      draw_();
      loadPixels();
      for (int i=0; i<pixels.length; i++) {
        result[i][0] += pixels[i] >> 16 & 0xff;
        result[i][1] += pixels[i] >> 8 & 0xff;
        result[i][2] += pixels[i] & 0xff;
      }
    }

    loadPixels();
    for (int i=0; i<pixels.length; i++)
      pixels[i] = 0xff << 24 | 
        int(result[i][0]*1.0/samplesPerFrame) << 16 | 
        int(result[i][1]*1.0/samplesPerFrame) << 8 | 
        int(result[i][2]*1.0/samplesPerFrame);
    updatePixels();
    
    if(invert_colors){
      filter(INVERT);
    }

    saveFrame("frame###.png");
    println(frameCount,"/",numFrames);
    
    if (frameCount==numFrames)
      exit();
  }
}

/// END OF THE RENDERING SYSTEM
//////////////////////////////////////////////////////////////////////////////

// Number of drawings used to render each final frame with motion blur
int samplesPerFrame = 7;
// Total number of frames in the gif
int numFrames = 20;
// Kind of the time interval used for each frame in the motion blur
float shutterAngle = .8;
// If you put this to false you will control time with the mouse and no pictures will be saved
boolean recording = true;

///////////////////////////////////////////////////
/// various parameters to control the aesthetic

// This one is quite explicit
boolean use_white_rectangle = true;
// Border margin
int border = 50;
// Inverting colors or not
boolean invert_colors = false;
// Maximum point size
float maximimum_point_size = 1;

/////////////////////////////////////////////////
/// FLOW FIELD ANIMATION ALGORITHM

// Time step
float DT = 0.1;
// Number of steps
int nsteps = 500;
// Number of particles per path
int number_of_particles_per_path = 40;
// Number of paths
int NPath = 3000;
// The total number of particles will be NPath*number_of_particles_per_path

/// A class to define paths particles take
class Path{
  float x = random(width);
  float y = random(height);
  
  ArrayList<PVector> positions = new ArrayList<PVector>();
  
  // point size
  float sz = random(1,maximimum_point_size);
  
  // Nunmber of particles per path
  int npart = number_of_particles_per_path;
  
  // offset so that particles don't appear at the same time for each path
  float t_off = random(1);
  
  Path(){
    positions.add(new PVector(x,y));
  }
  
  void update(){
    PVector res = field(x,y);
    x += DT*res.x;
    y += DT*res.y;
    positions.add(new PVector(x,y));
  }
  
  void show(){
    
    strokeWeight(sz);
    
    float tt = (t+t_off)%1;
    
    int len = positions.size();
    
    for(int i=0;i<npart;i++){
      // Particle location calculated by linear interpolation from the computed positions
      float loc = constrain(map(i+tt,0,npart,0,len-1),0,len-1-0.001);;
      int i1 = floor(loc);
      int i2 = i1+1;
      float interp = loc - floor(loc);
      float xx = lerp(positions.get(i1).x,positions.get(i2).x,interp);
      float yy = lerp(positions.get(i1).y,positions.get(i2).y,interp);
      
      float fact = 1;
      if(use_white_rectangle && (xx<border||xx>width-border||yy<border||yy>height-border)) fact = 0;
      
      // This is to make the particles appear and disappear gradually
      float alpha = fact*255*pow(sin(PI*loc/(len-1)),0.25);
      
      stroke(255,alpha);
      
      point(xx,yy);
    }
  }
}

Path[] array2 = new Path[NPath];

void path_step(){
  for(int i=0;i<NPath;i++){
    array2[i].update();
  }
}

//////////////////////////////////////
/// Definition of the flow field

PVector field(float x,float y){
  return new PVector(0,15);
}

////////////////////
/// SETUP AND DRAW_


void setup(){
  /// drawing size
  size(500,500);
  
  /// Initialization of the array used to render frames
  result = new int[width*height][3];
  
  /// Initilization of Paths
  for(int i=0;i<NPath;i++){
    array2[i] = new Path();
  }
  
  /// Computation of Paths
  for(int i=0;i<nsteps;i++){
    println(i+1,"/",nsteps);
    path_step();
  }
}

void draw_(){
  background(0);

  for(int i=0;i<NPath;i++){
    array2[i].show();
  }
  
  if(use_white_rectangle){
    noFill();
    stroke(255);
    strokeWeight(1);
    rect(border,border,width-2*border,height-2*border);
  }
}

Gives :

agif2.gif

Each object of the class Path corresponds to a trajectory. Trajectories start from random locations :

  float x = random(width);
  float y = random(height);

x,y will then represent the current latest computed position later on.

An array stores the different positions of the trajectory :

  ArrayList<PVector> positions = new ArrayList<PVector>();

The paths are initilized by adding the starting position to that array :

  Path(){
    positions.add(new PVector(x,y));
  }

The update() method adds a next position to the array “positions”, using the time step “DT” and the speed field (flow field) “field”.

  void update(){
    PVector res = field(x,y);
    x += DT*res.x;
    y += DT*res.y;
    positions.add(new PVector(x,y));
  }

We will use many trajectories so an array of Paths is created :

Path[] array2 = new Path[NPath];

Here is a function to update (add a position) all the trajectories :

void path_step(){
  for(int i=0;i<NPath;i++){
    array2[i].update();
  }
}

In setup() this function is repeated “nsteps” times :

  /// Computation of Paths
  for(int i=0;i<nsteps;i++){
    println(i+1,"/",nsteps);
    path_step();
  }

By the way the Paths are initiliazed in setup() :

  /// Initilization of Paths
  for(int i=0;i<NPath;i++){
    array2[i] = new Path();
  }

Now the draw_() function needs to draw things parametrized by the global variable “t” going from 0 to 1.

Each Path has a draw() method to draw it depending on t.
The animation algorithm used here uses many particles on a same trajectory. Linear interpolation is used to use positions between computed positions of the array “positions”. For example a particle at location 2.2 in the array will be between the position 2 and the position 3 from “positions”, and closer to the position 2 (2.5 would be in the middle). This gives us an infinite number of positions from the array “positions”. We’ll make a loop to go through each particle i of the trajectory to display and show it at location map(i+t,0,number_of_particles_of_the _path,0,lengh_of_positions_array-1) in the array “position”. That way as t goes from 0 to 1 each particle will take the place of the next particle and it will loop nicely.

To avoid having particles appearing at the same time for each path, an offset is used :

  float t_off = random(1);

And gives a new time variable “tt” :

    float tt = (t+t_off)%1;

The sine curve between 0 and PI, put to the power 0.25 gives a nice curve to define the alpha channel of the particle so that particles appear and disappear gradually.

Here is the method show() to implement all of that :

  void show(){
    
    strokeWeight(sz);
    
    float tt = (t+t_off)%1;
    
    int len = positions.size();
    
    for(int i=0;i<npart;i++){
      // Particle location calculated by linear interpolation from the computed positions
      float loc = constrain(map(i+tt,0,npart,0,len-1),0,len-1-0.001);;
      int i1 = floor(loc);
      int i2 = i1+1;
      float interp = loc - floor(loc);
      float xx = lerp(positions.get(i1).x,positions.get(i2).x,interp);
      float yy = lerp(positions.get(i1).y,positions.get(i2).y,interp);
      
      float fact = 1;
      if(use_white_rectangle && (xx<border||xx>width-border||yy<border||yy>height-border)) fact = 0;
      
      // This is to make the particles appear and disappear gradually
      float alpha = fact*255*pow(sin(PI*loc/(len-1)),0.25);
      
      stroke(255,alpha);
      
      point(xx,yy);
    }
  }

show() is called in draw_() :

  for(int i=0;i<NPath;i++){
    array2[i].show();
  }

Here are the main parameters of the algorithm (it can also be interesting to change the start positions). They are defined before the class Path in the full code.

// Time step
float DT = 0.1;
// Number of steps
int nsteps = 500;
// Number of particles per path
int number_of_particles_per_path = 40;
// Number of paths
int NPath = 3000;
// The total number of particles will be NPath*number_of_particles_per_path

MOTION BLUR RENDERING

I took the motion blur rendering system from @beesandbombs. It is the first part of the code. The function draw() will use many drawings depending on t from draw_() (“samplesPerFrame” exactly), to compute a final frame averaging the colors on those drawings. This produces a nice motion blur effect on things that change fast.

FLOW FIELD DESIGN

Now it’s time to experiment with different flow fields.

The gif above uses a field that goes down vertically :

PVector field(float x,float y){
  return new PVector(0,15);
}

With Perlin noise and an horizontal bias (+20) :

PVector field(float x,float y){
  float amount = 50;
  float scale = 0.03;
  return new PVector(amount*(noise(scale*x,scale*y)-0.5)+20,amount*(noise(100+scale*x,scale*y)-0.5));
}

Result :
agif3.gif

Because particles go mostly out of view, let’s change some parameters for faster rendering :

// Number of steps
int nsteps = 100;
// Number of particles per path
int number_of_particles_per_path = 10;

let’s change the field too :

PVector field(float x,float y){
  float amount = 50;
  float scale = 0.03;
  return new PVector(amount*(noise(scale*x,scale*y)-0.5)+10,amount*(noise(100+scale*x,scale*y)-0.5)+10);
}

Result :
agif4.gif

Let’s use “Perlin noise on a circle” as field :

PVector field(float x,float y){
  float amount = 50;
  float scale = 0.01;
  float parameter = 25*noise(scale*x,scale*y);
  return new PVector(amount*cos(parameter),amount*sin(parameter));
}

Result :
agif5.gif

You can combine (add, substract, multiply…) fields to obtain new ones.

Here is a great tutorial to obtain interesting fields : drawing vector field.

Here is some code to use fields based on the effect of some centers : link.
Result :
agif.gif

I hope this was helpful and that you will come up with nice stuff. Let me know if you have questions or ideas of improvements. In case you missed anything here’s the complete code to make a flow field gif :

int[][] result;
float t, c;

float ease(float p) {
  return 3*p*p - 2*p*p*p;
}

float ease(float p, float g) {
  if (p < 0.5) 
    return 0.5 * pow(2*p, g);
  else
    return 1 - 0.5 * pow(2*(1 - p), g);
}

float mn = .5*sqrt(3), ia = atan(sqrt(.5));

void push() {
  pushMatrix();
  pushStyle();
}

void pop() {
  popStyle();
  popMatrix();
}

void draw() {

  if (!recording) {
    t = mouseX*1.0/width;
    c = mouseY*1.0/height;
    if (mousePressed)
      println(c);
    draw_();
  } else {
    for (int i=0; i<width*height; i++)
      for (int a=0; a<3; a++)
        result[i][a] = 0;

    c = 0;
    for (int sa=0; sa<samplesPerFrame; sa++) {
      t = map(frameCount-1 + sa*shutterAngle/samplesPerFrame, 0, numFrames, 0, 1);
      draw_();
      loadPixels();
      for (int i=0; i<pixels.length; i++) {
        result[i][0] += pixels[i] >> 16 & 0xff;
        result[i][1] += pixels[i] >> 8 & 0xff;
        result[i][2] += pixels[i] & 0xff;
      }
    }

    loadPixels();
    for (int i=0; i<pixels.length; i++)
      pixels[i] = 0xff << 24 | 
        int(result[i][0]*1.0/samplesPerFrame) << 16 | 
        int(result[i][1]*1.0/samplesPerFrame) << 8 | 
        int(result[i][2]*1.0/samplesPerFrame);
    updatePixels();
    
    if(invert_colors){
      filter(INVERT);
    }

    saveFrame("frame###.png");
    println(frameCount,"/",numFrames);
    
    if (frameCount==numFrames)
      exit();
  }
}

/// END OF THE RENDERING SYSTEM
//////////////////////////////////////////////////////////////////////////////

// Number of drawings used to render each final frame with motion blur
int samplesPerFrame = 7;
// Total number of frames in the gif
int numFrames = 20;
// Kind of the time interval used for each frame in the motion blur
float shutterAngle = .8;
// If you put this to false you will control time with the mouse and no pictures will be saved
boolean recording = true;

///////////////////////////////////////////////////
/// various parameters to control the aesthetic

// This one is quite explicit
boolean use_white_rectangle = true;
// Border margin
int border = 50;
// Inverting colors or not
boolean invert_colors = false;
// Maximum point size
float maximimum_point_size = 1;

/////////////////////////////////////////////////
/// FLOW FIELD ANIMATION ALGORITHM

// Time step
float DT = 0.1;
// Number of steps
int nsteps = 100;
// Number of particles per path
int number_of_particles_per_path = 10;
// Number of paths
int NPath = 3000;
// The total number of particles will be NPath*number_of_particles_per_path

/// A class to define paths particles take
class Path{
  float x = random(width);
  float y = random(height);
  
  ArrayList<PVector> positions = new ArrayList<PVector>();
  
  // point size
  float sz = random(1,maximimum_point_size);
  
  // Nunmber of particles per path
  int npart = number_of_particles_per_path;
  
  // offset so that particles don't appear at the same time for each path
  float t_off = random(1);
  
  Path(){
    positions.add(new PVector(x,y));
  }
  
  void update(){
    PVector res = field(x,y);
    x += DT*res.x;
    y += DT*res.y;
    positions.add(new PVector(x,y));
  }
  
  void show(){
    
    strokeWeight(sz);
    
    float tt = (t+t_off)%1;
    
    int len = positions.size();
    
    for(int i=0;i<npart;i++){
      // Particle location calculated by linear interpolation from the computed positions
      float loc = constrain(map(i+tt,0,npart,0,len-1),0,len-1-0.001);;
      int i1 = floor(loc);
      int i2 = i1+1;
      float interp = loc - floor(loc);
      float xx = lerp(positions.get(i1).x,positions.get(i2).x,interp);
      float yy = lerp(positions.get(i1).y,positions.get(i2).y,interp);
      
      float fact = 1;
      if(use_white_rectangle && (xx<border||xx>width-border||yy<border||yy>height-border)) fact = 0;
      
      // This is to make the particles appear and disappear gradually
      float alpha = fact*255*pow(sin(PI*loc/(len-1)),0.25);
      
      stroke(255,alpha);
      
      point(xx,yy);
    }
  }
}

Path[] array2 = new Path[NPath];

void path_step(){
  for(int i=0;i<NPath;i++){
    array2[i].update();
  }
}

//////////////////////////////////////
/// Definition of the flow field


PVector field(float x,float y){
  float amount = 50;
  float scale = 0.005;
  float parameter = 25*noise(scale*x,scale*y);
  return new PVector(amount*cos(parameter),amount*sin(parameter));
}


////////////////////
/// SETUP AND DRAW_


void setup(){
  /// drawing size
  size(500,500);
  
  /// Initialization of the array used to render frames
  result = new int[width*height][3];
  
  /// Initilization of Paths
  for(int i=0;i<NPath;i++){
    array2[i] = new Path();
  }
  
  /// Computation of Paths
  for(int i=0;i<nsteps;i++){
    println(i+1,"/",nsteps);
    path_step();
  }
}

void draw_(){
  background(0);

  for(int i=0;i<NPath;i++){
    array2[i].show();
  }
  
  if(use_white_rectangle){
    noFill();
    stroke(255);
    strokeWeight(1);
    rect(border,border,width-2*border,height-2*border);
  }
}

Animated distortion gifs from a vector field and a scalar field

This short tutorial explains how to obtain this kind of effect on images with Processing :

agif.gif

I advice to read the stripes tutorial first because this is kind of an extension to it.

This is the photograph we’ll work with :

water.jpg

First of all let’s distort an image without animation. To do that we’ll use a vector field that indicates for each pixel of the resulting image which pixel from the original image to take. We’ll use a vector field based on Perlin noise :

PVector vector_field(float x,float y){
  float amount = 100;
  float scale = 0.01;
  return new PVector(amount*(noise(scale*x,scale*y)-0.5),4*amount*(noise(100+scale*x,scale*y)-0.5));
}

The “+100” is there to have independant noise values from the first calculated ones. The “-0.5” are there because noise values are centered on 0.5.

Here is the complete code achieve the static distortion :

int numFrames = 25;

color[] result;
PImage img;

PVector vector_field(float x,float y){
  float amount = 100;
  float scale = 0.01;
  return new PVector(amount*(noise(scale*x,scale*y)-0.5),4*amount*(noise(100+scale*x,scale*y)-0.5));
}

PVector distortion(float x,float y,float time){
  PVector res = vector_field(x,y);
  return res;
}

void setup() {
  img = loadImage("water.jpg");
  size(370,254);
  
  result = new color[width*height];
}

void draw(){
  // Time variable
  float t = 1.0*(frameCount-1)%numFrames/numFrames;
  
  image(img,0,0);
  
  // Renders the pixels using the distortion field
  loadPixels();
  for (int i=0; i<width; i++) {
    for(int j=0;j<height;j++){
      PVector res = distortion(i,j,t);
      
      int ii = constrain(floor(i + res.x),0,width-1);
      int jj = constrain(floor(j + res.y),0,height-1);
      
      result[i + width*j] = pixels[ii + width*jj];
    }
  }
  for (int i=0; i<width; i++) {
    for(int j=0;j<height;j++){
      pixels[i + width*j] = result[i + width*j];
    }
  }
  updatePixels();
  
  // Saves the frame
  println(frameCount,"/",numFrames);
  saveFrame("frame###.png");
  
  // Stops when all the frames are rendered
  if(frameCount==numFrames){
    println("finished");
    stop();
  }
}

Result :

frame025.png

The position of the pixel from which we take the color is constrained to avoid having an out of bounds error :

      int ii = constrain(floor(i + res.x),0,width-1);
      int jj = constrain(floor(j + res.y),0,height-1);

An array “result” is used to avoid ovewriting on original pixels that must be used to compute other resulting pixels.

Now to animate it, we’ll have moving stripes over the image activating and desactivating the distortion, that’s why I advice to understand the stripes tutorial first.

Here is the added code :

float scalar_field(float x,float y){
  return 0.03*y;
}

PVector distortion(float x,float y,float time){
  PVector res = vector_field(x,y);
  
  float intensity = map(sin(TWO_PI*(time+scalar_field(x,y))),-1,1,0,1);
  
  res.mult(intensity);
  
  return res;
}

It should be easy to understand if you understood the stripes tutorial, because here the white stripes correspond to the use of the distortion and black stripes to no distortion.

Here is the result :

agif2.gif

Now we can try other vector and scalar fields :

PVector vector_field(float x,float y){
  float amount = 50;
  float scale = 0.01;
  return new PVector(amount*(noise(scale*x,scale*y)-0.5),amount*(noise(100+scale*x,scale*y)-0.5));
}

float scalar_field(float x,float y){
  return 0.05*dist(x,y,width/2,height/2);
}

Result :

agif3.gif

By reducing the intensity of the distortion away from the center :

PVector vector_field(float x,float y){
  float amount = 150;
  float scale = 0.01;
  float intensity = constrain(map(dist(x,y,width/2,height/2),0,0.75*height,1,0),0,1);
  return new PVector(amount*(noise(scale*x,scale*y)-0.5)*intensity,amount*(noise(100+scale*x,scale*y)-0.5)*intensity);
}

float scalar_field(float x,float y){
  return -0.05*dist(x,y,width/2,height/2);
}

Result :

agif4.gif

That is all, I hope things were quite clear and that this tutorial will inspire you to make something (maybe much better and interesting) yourself.

Here is the complete code to generate the last GIF in case you missed anything…

int numFrames = 25;

color[] result;
PImage img;

PVector vector_field(float x,float y){
  float amount = 150;
  float scale = 0.01;
  float intensity = constrain(map(dist(x,y,width/2,height/2),0,0.75*height,1,0),0,1);
  return new PVector(amount*(noise(scale*x,scale*y)-0.5)*intensity,amount*(noise(100+scale*x,scale*y)-0.5)*intensity);
}

float scalar_field(float x,float y){
  return -0.05*dist(x,y,width/2,height/2);
}

PVector distortion(float x,float y,float time){
  PVector res = vector_field(x,y);
  
  float intensity = map(sin(TWO_PI*(time+scalar_field(x,y))),-1,1,0,1);
  
  res.mult(intensity);
  
  return res;
}

void setup() {
  img = loadImage("water.jpg");
  size(370,254);
  
  result = new color[width*height];
}

void draw(){
  // Time variable
  float t = 1.0*(frameCount-1)%numFrames/numFrames;
  
  image(img,0,0);
  
  // Renders the pixels using the distortion field
  loadPixels();
  for (int i=0; i<width; i++) {
    for(int j=0;j<height;j++){
      PVector res = distortion(i,j,t);
      
      int ii = constrain(floor(i + res.x),0,width-1);
      int jj = constrain(floor(j + res.y),0,height-1);
      
      result[i + width*j] = pixels[ii + width*jj];
    }
  }
  for (int i=0; i<width; i++) {
    for(int j=0;j<height;j++){
      pixels[i + width*j] = result[i + width*j];
    }
  }
  updatePixels();
  
  // Saves the frame
  println(frameCount,"/",numFrames);
  saveFrame("frame###.png");
  
  // Stops when all the frames are rendered
  if(frameCount==numFrames){
    println("finished");
    stop();
  }
}