// Ammar Hassonjee | lee chu
// section c
// ahassonj@andrew.cmu.edu | lrchu@andrew.cmu.edu
// final project
var mode = 0; // variable for cycling through frames
// variables for the beachBall ball scene
// choosing an initial point to launch the ball from
var ballx = 240;
var bally = 100;
var dir1 = 1; // direction of ball
var dir2 = 1; // direction of ball
var speedx = 1; // speed of the ball
var speedy = 0.5;
var size = 100; // size of the ball
// variables for rain scene
var raindrops = []; // array to store rain objects
var rainSpeed = 5; // tracking the speed of the rain
var increment = 0.01; //incrementing the rain values
var tone = 255; // the color of the lightning
var strweight = 30; // stroke weight of the lightning bolt
var cloudNumber; //
var cloudwidth;
var cloudheight;
var frame = 8; // keeping track of the frame count
var myCaptureDevice;
var px = [];
var py = [];
var theColorAtPxPy;
var theBrightnessOfTheColorAtPxPy;
var brightnessThreshold;
var darknessThreshold = 10;
// variables for determining silhouette
var borderX = [];
var borderY = [];
// variables for particles
var parties = [];
var ex = 20;
var why = 20;
function setup() {
createCanvas(480, 360);
myCaptureDevice = createCapture(VIDEO);
myCaptureDevice.size(480, 360); // attempt to size the camera.
myCaptureDevice.hide(); // this hides an unnecessary extra view.
// three variables declared to initialize the lighting function
var xi = random(40, 440);
var xii = random(30, 450);
var xiii = random(20, 460);
//bImage.resize(480, 360);
}
function isColor(c) {
return (c instanceof Array);
}
//--------------------------------------------------------------------
function draw() {
background(220);
myCaptureDevice.loadPixels();
image(myCaptureDevice, 0, -0, width, height); // draw the camera at 1:1 resolution
// ammar's code
if (mode === 0) {
initialScene();
}
if (mode === 1) {
// varying the clouds for each time the rain scene is called
// done before rainFall is called to ensure this code doesn't loop again
cloudNumber = random(3, 8);
cloudwidth = random(105, 140);
cloudheight = random(60, 95);
//calling the beach ball scene
ballSlap();
}
if (mode === 2) {
rainFall();
}
// lee's code
findBorder(); // finding silhouette of person
if (mode === 3) {
// particle effects
addParti();
updateAndDisplayParti();
removeParti();
}
if (mode === 4) {
// censoring effect
censor();
}
}
function mousePressed() {
mode = (mode + 1) % 5; // code to swtich between modes
}
//-------AMMAR'S CODE----------------------------------------------------------------------
// NOTE: the code works best if users are wearing dark clothing against a light background
function initialScene() { // initial scene for webcam text
textAlign(CENTER, CENTER);
textSize(15);
fill(100);
text("WELCOME TO AMMAR AND LEE'S WEBCAM MACHINE", width / 2, height / 2);
text("Press the mouse to cycle between scenes", width / 2, height * .8);
text("Press 'O' to increase darkness threshold", width / 2, height * .85);
text("Press 'P' to decrease darkness threshold", width / 2, height * .9);
text("Press 'R' to detect threshold automatically", width / 2, height * .95);
}
function ballSlap() {
// Creating the ball and setting its movement by changing
// ballx and bally variables
beachBall(ballx, bally, size);
ballx += dir1 * speedx;
bally += dir2 * speedy;
// Making ball bounce off the right
if (ballx >= width - size / 2 || ballx <= size / 2) {
dir1 = -dir1;
}
// Making the ball bounce off the top and bottom
if (bally < size / 2 || bally > height - size / 2) {
dir2 = -dir2;
}
var colorAtBall = myCaptureDevice.get(ballx, (bally + size / 2));
if (isColor(colorAtBall)) {
// setting the brightness to a variable and testing if it meets the
// the threshold values
brightnessAtBall = brightness(colorAtBall);
// if the ball hits the user's head (darker pixel) the ball will bounce up
if (brightnessAtBall < darknessThreshold & dir2 > 0) {
dir2 = -dir2;
speedy = random(0.5, 5);
speedyx = random(-4, 2);
}
}
}
function beachBall(x, y, diameter) { // function for creating rendering the beach ballx
var ballcolors = ["RED", "BLUE", "YELLOW"];
noStroke();
fill(255);
ellipse(x, y, diameter, diameter);
fill(ballcolors[0]);
arc(x, y, diameter, diameter, radians(30), radians(90));
fill(ballcolors[1]);
arc(x, y, diameter, diameter, radians(150), radians(210));
fill(ballcolors[2]);
arc(x, y, diameter, diameter, radians(270), radians(330));
fill(255);
ellipse(x, y, diameter / 5, diameter / 5);
}
function keyPressed() { // allowing the user to alter the beachball size with keys
// increasing the ball size if the a key is pressed
if (key === 'a' || key === 'A') {
if (size < 200) {
size += 10;
}
}
// decreasing the ball size if the d key is pressed
if (key === 'd' || key === 'D') {
if (size > 50) {
size -= 5;
}
}
// this code allows the user to edit the darkness threshold in order to fix bugs in the webcam
if (key === 'o' || key === 'O') {
if (darknessThreshold < 100) {
darknessThreshold += 2;
}
}
// decreasing the darkness threshold
if (key === 'p' || key === 'P') {
if (darknessThreshold > 10) {
darknessThreshold -= 2;
}
}
// finding threshold automatically
if (key ==='r' || key === 'R') {
findThreshold();
}
}
function rainFall() { // function for creating the rain scene
clouds(cloudNumber, cloudwidth, cloudheight); // calling the cloud function
// these lines of called call the function for the rain objects to make ensure
// the rain continuously falls
updateRain();
removeRain();
addRain();
// these if statements remap the speed of the rain to change as time passes
rainSpeed += increment;
if (rainSpeed >= 15) {
increment = -0.01;
}
if (rainSpeed <= 5) {
increment = 0.01;
}
// code to implement the lighting function at a random timeout
// this first if statement sets the lighting variables to an initial value
// and increments the frame variable
if (frameCount % 200 === 0) {
frame++;
tone = 255;
xi = random(40, 440);
xii = random(30, 450);
xiii = random(20, 460);
strweight = 30;
}
// a separate frame variable is used to make sure the lighning stays for multiple frames
if (frame % 3 === 0) {
// a random variable is used to make sure the lightning is called unpredictably
var chance = random(0, 2);
lightning(strweight, tone, xi, xii, xiii);
tone -= 4; // altering the lighting render values for aesthetic effect
strweight -= 1;
}
}
function makeRain(xpos, ypos) { // function for making the rain objects
var rain = {x: xpos, y: ypos, move: rainMove, display: rainDraw};
return rain;
}
function rainDraw() { // function for drawing the rain particles
noStroke();
fill(0, 0, 240);
push();
translate(this.x, this.y);
rotate(radians(15));
ellipse(0, 0, 2, 10);
pop();
}
function rainMove() { // moving the rain by the rain speed global variable
this.y += rainSpeed;
}
function updateRain() {
// for loop that calls train track objects and moves/displays them
for (var i = 0; i < raindrops.length; i++) {
var colorAtRain = myCaptureDevice.get(raindrops[i].x, raindrops[i].y);
raindrops[i].move();
if (isColor(colorAtRain)) {
// setting the brightness to a variable and testing if it meets the
// the threshold values
brightnessAtRain = brightness(colorAtRain);
// this ensures the rain stops falls on top of the figure in the WEBCAM
// by comparing to the pixel brightness
if (brightnessAtRain >= darknessThreshold) {
raindrops[i].display();
}
}
}
}
function addRain() { // function for adding rain objects to raindrops array
for (var i = 0; i < 6; i++) {
raindrops.push(makeRain(random(0, width), 0));
}
}
function removeRain() { // function for removing objects from the array
var rainKeep = [];
for (var i = 0; i < raindrops.length; i++) {
if (raindrops[i].y < 380) {
rainKeep.push(raindrops[i]);
}
}
raindrops = rainKeep; // reassigning the raindrops array to the duplicate array
}
// this function calls the lighting from a list of values that create polylines
function lightning(weight, brightness, xpos, xpos2, xpos3) {
noFill();
strokeWeight(weight);
stroke(brightness);
var x2 = xpos + random(-10, 10);
var x3 = xpos2 + random(-20, 25);
var x4 = xpos3 + random (-30, 40);
var y1 = random(height / 4, height / 3);
var y2 = random(height / 8 * 5, height * 2 / 3);
line(xpos, 0, x2, y1);
line(x2, y1, x3, y2);
line(x3, y2, x4, height);
}
// function that calls the clouds from a list of variables
function clouds(number, length, cheight) {
noStroke();
for (var i = 0; i < number + 1; i++) {
fill( map(i, 0, number, 100, 150));
var cloudx = map(i, 0, number, 0, width);
ellipse(cloudx, 0, length, cheight + 15 * (i % 3));
}
}
//-------LEE'S CODE----------------------------------------------------------------------
function findBorder() {
borderX = [];
borderY = [];
// finds the silhouette of person based on darkness threshold in intervals of 5
for (var i = 0; i < width; i += 5) {
for (var j = 0; j < height; j += 5) {
if (brightness(myCaptureDevice.get(i, j)) < darknessThreshold &
borderX.length < i / 5) {
borderX.push(i);
borderY.push(j);
}
}
}
}
function findThreshold() {
var brightest = 50;
var darkest = 50;
var xColors = [];
var xx = [];
// stores brightnesses of all colors along a line in the image displayed
for (var j = 0; j < width; j += 5) {
xColors.push(brightness(myCaptureDevice.get(j, height / 2)));
xx.push(j);
}
// finds brightest and darkest pixels
for (var k = 0; k < xColors.length; k ++) {
if (xColors[k] > xColors[brightest]) {
brightest = k;
}
if (xColors[k] < xColors[darkest]) {
darkest = k;
}
}
// elementary ratio to determine thresholds
brightnessThreshold = (xColors[brightest] - xColors[darkest]) / 3 + xColors[darkest];
darknessThreshold = (xColors[brightest] - xColors[darkest]) / 5 + xColors[darkest] - 10;
}
function addParti() {
// creating particles along top of person
var likelihood = 0.45;
for (var i = 0; i < borderX.length; i ++) {
if (random(1) < likelihood) {
parties.push(createParticle(borderX[i], borderY[i]));
}
}
}
function removeParti() {
// removes particle when size decreases below 1
var partiesToKeep = [];
for (var i = 0; i < parties.length; i ++) {
if (parties[i].size > 1) {
partiesToKeep.push(parties[i]);
}
}
parties = partiesToKeep;
}
function updateAndDisplayParti() {
for (var i = 0; i < parties.length; i ++) {
parties[i].move();
parties[i].display();
}
}
function createParticle(birthX, birthY) {
var parti = {x: birthX, y: birthY, size: round(random(5, 15)),
speed: -5, move: partiMove, display: partiDisplay,
color: myCaptureDevice.get(birthX, birthY)}
return parti;
}
function partiMove() {
// move particle and decrease size
this.y += this.speed;
this.size -= 1;
}
function partiDisplay() {
strokeWeight(0);
fill(this.color);
push();
translate(this.x, this.y);
rotate(millis() / 1000 * 2 * PI);
rectMode(CENTER);
rect(0, 0, this.size, this.size);
pop();
}
function censor() {
// divides area below silhouette into 25 by 25 squares and extracts colors
for (var i = 0; i < borderX.length; i += 5) {
for (var j = borderY[i]; j < height; j += 25) {
push();
translate(borderX[i], j);
rectMode(CENTER)
fill(myCaptureDevice.get(borderX[i], j));
rect(0, 0, 25, 25);
pop();
}
}
}
Ammar Hassonjee and Lee Chu collaborated on this webcam machine. It consists of four scenes: a beachball headbutting game, a rainy day, snapped by Thanos, and an unnecessary censorship. Click the mouse to cycle through the scenes. Press the keys O and P to increase or decrease the darkness threshold depending on the lighting of your setting. Press the key R to automatically detect the darkness threshold, although this process may not be perfect.