# 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 : 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 : 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 : 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 : 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 : 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 : 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();
}
}
```
Advertisements