Animated stripes gifs from scalar fields

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

agif3.gif

We’ll start from this code that generates the following simple animation ;

int margin = 50;
int numFrames = 20;

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

}

float pixel_color(float x,float y,float t){
  float result = map(sin(TWO_PI*t),-1,1,0,1);
  return 255*result;
}

void draw(){
  background(0);

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

  // Draws every pixel
  for(int i=margin;i<width-margin;i++){
    for(int j=margin;j<height-margin;j++){
      stroke(pixel_color(i,j,t));
      point(i,j);
    }
  }

  // Draws a white rectangle
  stroke(255);
  noFill();
  rect(margin,margin,width-2*margin,height-2*margin);

  // Saves the frame
  println(frameCount,"/",numFrames);
  saveFrame("frame###.png");

  // Stops when all the frames are rendered
  if(frameCount == numFrames){
    println("finished");
    stop();
  }
}

agif.gif

That code makes each pixel in a white rectangle oscillate between black and white over time. The rest will be only little variations from there so make sure you understand the basis.

Now let’s have moving stripes !
So let’s change the pixel_color function so that the color of pixels doesn’t oscillate at the same time vertically.

float pixel_color(float x,float y,float t){
  float result = map(sin(TWO_PI*(t+0.05*y)),-1,1,0,1);
  return 255*result;
}

agif2.gif

Now let’s make those stripes more contrasted. To achieve that we’ll use an easing function that makes values between 0 and 0.5 closer to 0 and values between 0.5 and 1 closer to 1, with a parameter g to increase the effect.

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);
}

Let’s use it on our oscillating color :

float pixel_color(float x,float y,float t){
  float result = ease(map(sin(TWO_PI*(t+0.05*y)),-1,1,0,1),3.0);
  return 255*result;
}

This is the result :

agif3.gif

Now let’s make a general function that controls the offset in the oscilation :
(a scalar field is just a function that gives a real value for each position of the plane in our case)

float scalar_field_offset(float x,float y){
  return 0.05*x+0.05*y;
}

float pixel_color(float x,float y,float t){
  float result = ease(map(sin(TWO_PI*(t+scalar_field_offset(x,y))),-1,1,0,1),3.0);
  return 255*result;
}

Result (there is now horzontal change due to 0.05*x) :

agif4.gif

Now to obtain different results we just have to change the scalar field. Let’s use the distance to the center.

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

agif5.gif

Perlin noise (a very nice function given by Processing, I won’t make a tutorial about it here) :

float scalar_field_offset(float x,float y){
  float scale = 0.003;
  float result = 40*noise(scale*x,scale*y);
  return result;
}

agif6.gif

Stripes and perlin noise gradually vertically :

float scalar_field_offset(float x,float y){ 
  float perlin_noise_intensity = pow(constrain(map(y,0.1*height,height,0,1),0,1),2);
  
  float scale = 0.006;
  float result = -0.05*y + 20*(noise(scale*x,scale*y)-0.5)*perlin_noise_intensity;
  return result;
}

agif7.gif :

Perlin noise in the center :

float scalar_field_offset(float x,float y){
  float distance = dist(x,y,width/2,height/2);
  float perlin_noise_intensity = ease(constrain(map(distance,0,0.3*height,1,0),0,1),2);
  
  float scale = 0.002;
  float result = -0.05*y -0.05*x + 60*(noise(scale*x,scale*y)-0.5)*perlin_noise_intensity;
  return result;
}

agif8.gif

Using distance differently :

float scalar_field_offset(float x,float y){
  float distance = dist(x,y,0.5*width,0.5*height);
  
  float result = 300/(25+distance);
  return result;
}

agif9.gif

By adding the effect of many centers you can get something like this :

tumblr_ouscz6fS6X1w3y4ilo1_500.gif

And with many centers and using an effect only near them :

agif.gif

Here is a complete code to generate one in case you missed anything :

int margin = 50;
int numFrames = 20;

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

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 scalar_field_offset(float x,float y){
  float distance = dist(x,y,0.5*width,0.5*height);
  
  float result = 300/(25+distance);
  return result;
}

float pixel_color(float x,float y,float t){
  float result = ease(map(sin(TWO_PI*(t+scalar_field_offset(x,y))),-1,1,0,1),3.0);
  return 255*result;
}

void draw(){
  background(0);
  
  float t = 1.0*(frameCount-1)%numFrames/numFrames;
  
  // Draws every pixel
  for(int i=margin;i<width-margin;i++){
    for(int j=margin;j<height-margin;j++){
      stroke(pixel_color(i,j,t));
      point(i,j);
    }
  }
  
  // Draws a white rectangle
  stroke(255);
  noFill();
  rect(margin,margin,width-2*margin,height-2*margin);
  
  // Saves the frame
  println(frameCount,"/",numFrames);
  saveFrame("frame###.png");
  
  // Stops when all the frames are rendered
  if(frameCount == numFrames){
    println("finished");
    stop();
  }
}

I hope this was helpful and that you will be creative and come up with better stuff than me !

Bonus : if you know glsl, it would be a better idea to code this in glsl to have much faster rendering.

Advertisements

Making GIFs with Processing and GIMP

Use the saveFrame function in Processing to save frames, for example like this :

saveFrame("frame###.png"); 

Then import the frames as layers in GIMP, and export as .gif and as animation. You can choose the delay between frames there. There must be a more automated way to make gifs but this works and I’m fine with it.