For our last project, Kate and I both wanted to work with projections. We chose to augment a common, often overlooked but frequently used object, the water cooler. We first discussed how we wanted to create a “flooded” water effect, as if the digital water was flowing from the tap. We attempted to work with the physics engine, Box 2D, but we ended up creating our own particle system in Processing to generate the waterfall. We added some floating kids into the waterfall to create an unexpected sense of playfulness.

Here’s our video:

To create the projection mapping, we used the Keystone library for Processing to correct the perspective from the projector throw. In the final documentation, we used Millumin to add further control over the warping of the projections, fitting the waterfall precisely to the water cooler tap and floor level. This allowed us to use bezier curves and segmenting to enhance our projection mapping accuracy.

Here’s some code:

```Water[] drops = new Water[500];
Mist[] bubbles = new Mist[500];
Ball[] balls = new Ball[200];

int numBalls = 200;
float spring = 0.05;
float gravity = 0.2;
float friction = -.1;

int numFrames = 81;  // The number of frames in the animation
int currentFrame = 0;
PImage[] images = new PImage[numFrames];
//ArrayList mistClouds;

float[] p1 = {237, 0};
float[] p2 = {320, 0};
float[] p3 = {320, 0};
float[] p4 = {320, 0};
float[] p5 = {320, 0};
float[] p6 = {320, 0};
float[] p7 = {320, 0};
float[] p8 = {320, 0};
float[] p9 = {337, 0};

int mouseR = 25;

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

//frameRate(30);
//animation1 = new Animation("Witch Flying_2_", 81);
//animation2 = new Animation("PT_Teddy_", 60);

//for (int j = 0; j < numFrames; j++) {
//  String imageName = "Witch Flying_2_" + nf(j, 5) + ".png";
//}

for (int i = 0; i();
}

void draw() {
background(0, 0, 0);
//frameRate(30);

//currentFrame = (currentFrame+1) % numFrames;  // Use % to cycle through frames
/*int offset = 0;
for (int x = -100; x < width; x += images[0].width) {
image(images[(currentFrame+offset) % numFrames], x, -20);
offset+=2;
image(images[(currentFrame+offset) % numFrames], x, height/2);
offset+=2;
}*/

//------------------------------------------------------------//
//                    draw pool
//------------------------------------------------------------//

//fill(150, 180, 255);

//pushMatrix();

//beginShape();

//translate(0, height/2);

//curveVertex(p1[0], p1[1]);
//curveVertex(p1[0], p1[1]);
//curveVertex(p2[0], p2[1]);
//curveVertex(p3[0], p3[1]);
//curveVertex(p4[0], p4[1]);
//curveVertex(p5[0], p5[1]);
//curveVertex(p6[0], p6[1]);
//curveVertex(p7[0], p7[1]);
//curveVertex(p8[0], p8[1]);
//curveVertex(p9[0], p9[1]);
//curveVertex(p9[0], p9[1]);

////ellipse(p1[0], p1[1], 10, 10);
////ellipse(p2[0], p2[1], 10, 10);
////ellipse(p3[0], p3[1], 10, 10);
////ellipse(p4[0], p4[1], 10, 10);
////ellipse(p5[0], p5[1], 10, 10);
////ellipse(p6[0], p6[1], 10, 10);
////ellipse(p7[0], p7[1], 10, 10);
////ellipse(p8[0], p8[1], 10, 10);
////ellipse(p9[0], p9[1], 10, 10);

//endShape(CLOSE);

//popMatrix();

//for (int dot= 0; dot p1[1]) { //shrink up
//      p2[1] -= .2;
//    }
//  }
//}

//for (int dot= 0; dot p1[1]) { //shrink up
//      p3[1] -= .5;
//    }
//  }
//}

//for (int dot= 0; dot p1[1]) { //shrink up
//      p4[1] -= .5;
//    }
//  }
//}

//for (int dot= 0; dot p1[1]) { //shrink up
//      p5[1] -= .5;
//    }
//  }
//}

//for (int dot= 0; dot width/2) { //shrink left
//      p6[0] -= .5;
//    }
//    if (p6[1]> p1[1]) { //shrink up
//      p6[1] -= .5;
//    }
//  }
//}

//for (int dot= 0; dot width/2) { //shrink left
//      p7[0] -= .5;
//    }
//    if (p7[1]> p1[1]) { //shrink up
//      p7[1] -= .5;
//    }
//  }
//}

//for (int dot= 0; dot width/2) { //shrink left
//      p8[0] -= .5;
//    }
//    if (p8[1]> p1[1]) { //shrink up
//      p8[1] -= .5;
//    }
//  }
//}

//for (int dot= 0; dot width/2+25) { //shrink left
//      p9[0] -= .5;
//    }
//  }
//}

for (int drop = 0; drop
```