Alec Albright – Final Project

sketch

// Alec Albright
// aalbrigh
// Section B
// Project 12 - Final Project

var video;
var nPoints = 100; // number of points used to draw curves

var previous; // previous image from camera
var threshold = 150; // used to determine if a pixel is different enough to consider it "moving"
var motionX = 0; // main coordinate of X motion
var motionY = 0; // main coordinate of Y motion
var lerpX = 0; // X coordinate for smoothing of motion
var lerpY = 0; // Y coordinate for smoothing of motion

var higherLove; // Whitney Houston's "Higher Love"
var low; // Flo Rida's "Low"
var irreplaceable; // Beyonce's "Irreplaceable"
var newBooty; // Bubba Sparxxx's "Ms. New Booty"
var higherVol; // Higher love volume
var lowVol; // low volume
var irrepVol; // irreplaceable volume
var bootyVol; // new booty volume

function preload(){
    higherLove = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/12/Higher-Love.wav");
    low = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/12/Low.wav");
    irreplaceable = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/12/Irreplaceable.wav");
    newBooty = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/12/New-Booty.wav");
}

function soundSetup() { // setup for audio generation
    // making sine
    sine = new p5.Oscillator();
    sine.setType("sine");
    //sine.start();

    // making sawtooth
    sawtooth = new p5.Oscillator();
    sawtooth.setType("sawtooth");
    //sawtooth.start();

    // making square wave
    square = new p5.Oscillator();
    square.setType("square");
    square.freq(440);
    //square.start();
}

function setup(){
    createCanvas(480, 480);
    angleMode(RADIANS);

    video = createCapture(VIDEO);
    video.size(480, 480); // attempt to size the camera. 
    video.hide(); // this hides an unnecessary extra view.
    // prepping to copy previous frame for difference in motion
    previous = createImage(480, 480, RGB);

    useSound();

    higherLove.play();
    higherLove.setVolume(0);
    higherLove.loop();

    low.play();
    low.setVolume(0);
    low.loop();

    irreplaceable.play();
    irreplaceable.setVolume(0);
    irreplaceable.loop();

    newBooty.play();
    newBooty.setVolume(0);
    newBooty.loop();
}

function draw(){
    var count = 0; // number of pixel instances we've looped through
    var sumX = 0; // sum of motion X coordinates
    var sumY = 0; // sum of motion X coordinates

    // making camera actually mirror user
    push();
    translate(width, 0);
    scale(-1, 1);
    image(previous, 0, 0);
    pop();

    loadPixels();
    video.loadPixels(); // this must be done on each frame.
    previous.loadPixels();

    // comparing all pixels to previous image
    for (var x = 0; x < video.width; x ++) {
        for (var y = 0; y < video.height; y ++) {
            var location = (x + y * video.width) * 4;
            // finding previous and current colors
            // previous
            var red1 = previous.pixels[location]; 
            var green1 = previous.pixels[location + 1];
            var blue1 = previous.pixels[location + 2];
            // current
            var red2 = video.pixels[location];
            var green2 = video.pixels[location + 1];
            var blue2 = video.pixels[location + 2];

            var diff = distSquared(red1, green1, blue1, red2, green2, blue2);

            // checking whether they are different enough to call motion
            if (diff > threshold * threshold) {
                sumX += x;
                sumY += y;
                count ++;
            }
        }
    }
    updatePixels();

    // only count it as a different frame if more than 30 pixels have changed
    // find main X and Y coordinate of motion, this will be our control for everything
    if (count > 100) { 
        motionX = sumX / count;
        motionY = sumY / count;
    }
      
    // maintaining mirrored scale for user interface
    push();
    translate(width, 0);
    scale(-1, 1);
    // smoothing out how the point of focus is travelling
    lerpX = lerp(lerpX, motionX, 0.1); 
    lerpY = lerp(lerpY, motionY, 0.1); 

    // drawing point so user knows where the main motion point is
    stroke("black");
    fill("white");
    ellipse(lerpX, lerpY, 20, 20);
    pop();

    push();
    translate(width / 2, height / 2);
    
    // draw all the shapes
    drawHippopede();
    drawEpicycloid();
    drawHypotrochoid();

    pop();

    previous.copy(video, 0, 0, video.width, video.height, 0, 0, video.width, video.height);
    
    // creating slight boundaries for better sound isolation
    if(lerpY < 200 || lerpY > 320) {
        // letting contrast between top and bottom come through
        bootyVol = 0;
        irrepVol = 0;
        newBooty.setVolume(bootyVol);
        irreplaceable.setVolume(irrepVol);

        // as we move up, more higher love/less low
        // volume 0 to 1
        higherVol = map(lerpY, 0, width, 0, 1);
        higherLove.setVolume(1 - higherVol);
        lowVol = map(lerpY, 0, width, 0, 1);
        low.setVolume(lowVol);
    } else {
        // letting contrast between right and left come through
        higherVol = 0;
        lowVol = 0;
        higherLove.setVolume(higherVol);
        low.setVolume(lowVol);

        // as we move right, more new booty/less irreplaceable
        // volume 0 to 1
        bootyVol = map(lerpX, 0, width, 0, 1);
        newBooty.setVolume(1 - bootyVol);
        irrepVol = map(lerpX, 0, width, 0, 1);
        irreplaceable.setVolume(irrepVol);
    }
}

// draws Hippopede
function drawHippopede() {
    var x; // x coordinate of vertex
    var y; // y coordinate of vertex
    var r; // polar coordinate
    var a = lerpX / 3 // main parameter of the curve
    var b = map(a, 0, 480, 100, 200); // circle radius
    var rotation = map(lerpY, 0, 480, 0, TWO_PI); // amount of rotation

    // thickness of line proportional to the circle radius
    strokeWeight(b / 6);
    stroke(255, 255, 255, 150);
    noFill();

    // rotate shape
    push();
    rotate(rotation);

    // start drawing the shape, one point at a time
    beginShape();
    for(var i = 0; i < nPoints; i++){
        var t = map(i, 0, nPoints, 0, TWO_PI);
        
        // find r (polar equation)
        r = sqrt(4 * b * (a - b * sinSq(t)));

        // convert to x and y coordinates
        x = r * cos(t);
        y = r * sin(t);

        // draw a point at x, y
        vertex(x, y);
    }
    endShape();
    pop();
}

// draws hypotrochoid
function drawHypotrochoid() {
    var x; // x coordinate of vertex
    var y; // y coordinate of vertex
    var a = map(lerpX, 0, 480, 20, 100); // radius of the interior circle
    var b = 3; // radius of the petals
    var h = lerpX / 10; // distance from center of interior circle
    var red = map((lerpX + lerpY) / 2, 0, 480, 0, 255); // how much red
    var blue = map(lerpY, 0, 480, 0, 255); // how much blue
    var alpha = map(lerpX, 0, 480, 50, 150); // how opaque
    var rotation = map(lerpY, 100, 300, 0, TWO_PI); // amount of rotation

    strokeWeight(2)
    stroke(255, 255, 255, 150);

    // control color and opacity with mouse location
    fill(red, 0, blue, alpha);

    // control rotation with lerpY
    push();
    rotate(rotation);

    // create the shape itself
    beginShape();
    for(var i = 0; i < nPoints; i++) {
        var t = map(i, 0, nPoints, 0, TWO_PI);

        // use parametric euqations for hypotrochoid to find x and y
        x = (a - b) * cos(t) + h * cos((a - b) / b * t);
        y = (a - b) * sin(t) - h * sin((a - b) / b * t);

        // draw a point at x, y
        vertex(x, y)
    }
    endShape(CLOSE);
    pop();
}

// draws an epicycloid
function drawEpicycloid() {
    var x; // x coordinate of vertex
    var y; // y coordinate of vertex
    var a = map(lerpX, 0, 480, 20, 100); // radius of interior circle
    var b = map(lerpY, 0, 480, 5, 30); // radius of petals
    var blue = map((lerpX + lerpY) / 2, 0, 480, 0, 255); // how much blue
    var red = map(lerpY, 0, 480, 0, 255); // how much red
    var rotation = map(lerpY, 100, 300, 0, TWO_PI); // how muhc rotation

    // control color with mouse location
    strokeWeight(10)
    stroke(red, 0, blue, 150);

    // control rotation with mouse location
    push();
    rotate(rotation);

    // start drawing shape
    beginShape();
    for(var i = 0; i < nPoints; i++) {
        var t = map(i, 0, nPoints, 0, TWO_PI);

        // find coordinates using epicycloid parametric equations
        x = (a + b) * cos(t) - b * cos((a + b) / b * t);
        y = (a + b) * sin(t) - b * sin((a + b) / b * t);

        // draw a point at x, y
        vertex(x, y);
    }
    endShape();
    pop();
}

// defines sin^2 using trigonometric identities
function sinSq(x) {
    return((1 - cos(2 * x)) / 2);
}

function distSquared(x1, y1, x2, y2) {
    let dx = x2 - x1;
    let dy = y2 - y1;
    return (dx * dx) + (dy * dy);
}

For the Final Project, I wanted to do something that had to deal with music, as it is my primary focus at CMU, but I also wanted to try doing something computationally complex that I would certainly be challenged by. Thus, the idea to create a motion detection music player was born! I also wanted to keep it fun, so I decided to play music related to each quadrant: “Irreplaceable” – Beyonce for the left (to the left, to the left…), “Higher Love” – Whitney Houston for the top, “Low” – Flo Rida for the bottom, and “Ms. New Booty” – Bubba Sparxxx for the right (get it right, get it right get it tight). For better user interfacing, I also included a floating point that denotes where the program has tracked the average motion.

The process was most difficult in rendering and comparing one frame to its previous frame in order to create a successful motion detection program. It was interesting mapping the visualization to the motion detection as well, giving each song its own visual identity.

In order to run the program, you must allow the website to access your video camera. However, I have included a demo video below for those who would rather see that.

Minjae Jeong- Final Project

sketch

//Minjae Jeong
//Section B
//minjaej@andrew.cmu.edu
//Final Project

var human = []; //array to save x y position
var count = 0;
var transp = 255; //transparency

function preload() {
    //simplified world map
    worldMap = loadImage("https://i.imgur.com/Kviuun0.png");
}

function setup() {
    createCanvas(600, 348);
    textAlign(CENTER);
}

function draw() {
    background(132, 181, 229); //rgb of image's body of water
    tint(255, transp); //this makes the water to fill up as you click
    image(worldMap, 0, 30);

    //title
    textSize(24);
    fill('red');
    text("UNTIL WHEN CAN THE EARTH SUPPORT US?", width / 2, 27);//Have you thought about it

    //Draw human + CO2
    strokeWeight(1);
    for (var i = 0; i < human.length; i++) {
        xx = human[i][0];
        yy = human[i][1];
        fill('black');
        drawHuman(xx, yy);
        drawco2(xx, yy - 5);
    }

    //display global warming text
    if (count > 20 & count < 40) {
        textSize(20);
        fill("red");
        text("GLOBAL WARMING", 350, 330);
        // count = count + 1;
    }

    //display "No more penguins and polar bears." Could be very soon
    if (count > 40 & count < 60) {
        textSize(20);
        fill("red");
        text("NO MORE PENGUINS & POLAR BEARS", 320, 50);
    }

    //display "No more place to live"
    //(Elon Musk please)
    if (count > 70 & count < 100) {
        textSize(27);
        fill("red");
        text("NO MORE PLACE TO LIVE", width / 2, height / 2);
    }

    //changes background color to black
    if (count > 90 & count < 100) {
        fill("black");
        rect(0, 0, width, height);
        fill("red");
        text("NO MORE PLACE TO LIVE", width / 2, height / 2);
    }

    //"What will you do?"
    if (count >= 100){
        fill('black');
        rect(0, 0, width, height);
        fill('red');
        textSize(30);
        text("WHAT WILL YOU DO?", width / 2, height /2);
    }
}

//draw human
function drawHuman(x, y) {
    ellipse(x, y, 5);
    rect(x - 2, y + 5, 4, 11);
    line(x - 2, y + 7, x - 8, y + 2);
    line(x + 2, y + 7, x + 8 , y + 2);
}

//CO2
function drawco2(x, y) {
    textSize(9);
    text("+ CO2", x, y);
}

function mousePressed() {
    human.push([mouseX, mouseY]); //Save mouseX, mouseY
    transp = transp - 3.3; //Water fills up the land
    count += 1;
}

For the final project, I made an interactive map that I wish to deliver some message. Click to add people on the map, keep on adding as it represents human population on the Earth.

My final project is little different from the proposal, because the original idea, which was to indicate temperature rise and deforestation, did not have clear message compared to what it actually shows. So I rather made a simple interactive map that shows what is happening as we grow on the Earth.

Julia Nishizaki – Final Project

sketch

//Julia Nishizaki
//Section B
//jnishiza@andrew.cmu.edu
//Final Project - ABCs of Climate Change


//variables for climate change effects, uses true/false statements to make effects appear/disappear
var cE = {
    lakeColor: false, //algae blooms
    flowers: false, //biodiversity
    clouds: false, //carbon footprint
    trees: true, //deforestation
    gas: false, //greenhouse gases
    mtn: false, //hazardous waste
    hill: false, //invasive species
    jeopardy: false, //jeopardy
    house: false, //population growth
    sky: false, //smog
    field: false, //toxic pollutants
    building: false, //urban sprawl
    lake: false, //water levels
    deadz: false, //zones that are dead
}

//variables to help with effects of climate change
var lakeWidth = 200; //initial width of lake
var lakeHeight = 35; //height of lake
var skyHeight = 0; //height of sky for smog
var cloudX = 475; //starting x position for cloud
var gasX = 475; //starting x position for gas cloud
var jtranslu = 0; //starting opacity for "smoke screen", jeopardy

//variables for the window and its dimensions
var win = {
    ws: 125, //distance from window to right and left sides of canvas
    wt: 40, //distance from window to top of canvas
    wb: 160, //distance from window to bottom of canvas
    frm: 5, //width of window frame
    dis: 5, //displacement outside of canvas
}

//variables for colors used throughout, especially for objects that change color
var colors = {
    hill: 80, //light green hill (HSB)
    mtnb: 70, //purple mountains (HSB)
    sky: '#8ED9EF', //light blue of sky
    smogop: 80, //opacity of smog in background
    field: 'green', //green
    lakeh: 55, //hue value of lake (HSB)
    lakes: 50, //saturation value of lake (HSB)
    lakeb: 100, //brightness value of lake (HSB)
    fieldb: 60, //brightness value of field (HSB)
    wall: 230, //light gray of walls
    table: 150, //dark gray of table 
    flower: 'purple', //purple flowers
    plnflw: 'green', //green flowers
    type: '#8BC63F', //light green of type
}

//arrays that store letters and each letter's climate change info
var alphabetLetters = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"];
var letterInfo = [ //stores copy on climate change ABCs
    "Algae Blooms: toxic algae can poison water and create dead zones", 
    "Biodiversity: the variety of life on the planet", 
    "Carbon Footprint: the amount of carbon dioxide each of us produces", 
    "Deforestation: the reduction of trees due to natural forces or human activity", 
    "Extinctions: the death of all individuals of a species", 
    "Fossil Fuels: nonrenewable fuels that are burned for energy like coal and gas", 
    "Greenhouse Gases: gases like methane, trap heat and warm the atmosphere", 
    "Hazardous Waste: waste that pose a risk to human health and the environment", 
    "Invasive Species: any organism not native to an ecosystem that causes harm", 
    "Jeopardy: danger of loss, harm, or failure", 
    "Keystone Species: a species that is critical to the survival of other species", 
    "Landfill: a site where wastes are dumped for permanent disposal", 
    "Microplastics: very small pieces of plastic that pollute the environment", 
    "Nonrenewable: resources that can't be replenished onced used", 
    "Ozone Layer: a thin protective layer of gas above Earth that filters UV radiation", 
    "Population Growth: an increase in the total human population", 
    "Quotas Insufficient: limits and restrictions in environmental policies not met", 
    "Runoff: stormwater from cities or farms that carry pollutants into water systems", 
    "Smog: dust, smoke, or chemical fumes that pollute the air", 
    "Toxic Pollutants: contaminate areas and cause death, disease, or birth defects", 
    "Urban Sprawl: expansion of auto-dependent developments over large areas of land", 
    "Vulnerability: susceptibility to harm from exposure to stresses", 
    "Water Level Rise: rising sea levels due to global warming and melting glaciers", 
    "Xenobiotic: non-natural or man-made substances found in the environment", 
    "Yield: amount of crops produced per unit area", 
    "Zones that are Dead: dead zones are caused by hypoxia, or lack of oxygen in water",
    ];

function setup() {
    createCanvas(600, 480);
    frameRate(30);   
}

function draw() {
    background(colors.sky); //sets the background as a light blue
    
    prepEnvironEffects(); //connects each key to a variable

    drawLandscape(); //creates all elements of the landscape 
    drawRoom(); //creates all elements of the room
    
    typedLetterAlphabet(); //displays the alphabet
    typedLetterInfo(); //displays the words and definitions
}

function prepEnvironEffects() {//when a particular key is pressed, the variable associated with that key switches from false to true
    if (key == 'a') { //algae blooms
            cE.lakeColor = true;
        } 
    if (key == 'b') { //biodiversity
            cE.flowers = true;
        }
    if (key == 'c') { //carbon footprint
            cE.clouds = true;
        } 
    if (key == 'd') { //deforestation
            cE.trees = false;
        } 
    if (key == 'g') { //greenhouse gases
            cE.gas = true;
        } 
    if (key == 'h') { //hazardous waste
            cE.mtn = true;
        } 
    if (key == 'i') { //invasive species
            cE.hill = true;
        } 
    if (key == 'j') { //jeopardy
            cE.jeopardy = true;
        } 
    if (key == 'p') { //population growth
            cE.house = true;
        } 
    if (key == 's') { //smog
            cE.sky = true;
        } 
    if (key == 't') { //toxic pollutants
            cE.field = true;
        } 
    if (key == 'u') { //urban sprawl
            cE.building = true;
        } 
    if (key == 'w') { //water level rise
            cE.lake = true;
        } 
    if (key == 'z') { //zones that are dead
            cE.deadz = true;
    }
}

//Creates elements in the landscape, and calls for changes to those elements
function drawLandscape() {
    makeClouds(); //creates clouds for carbon footprint and greenhouse gas, letters c and g
    changeSky(); //creates smog screen in background, letter s
    changeMtnHill(); //changes color of mountains and hills, letters h and i

    var landscapeScale1 = 0.004; //detail in hills
    var landscapeScale2 = 0.018; //detail in mountains

    //creates mountains in the background
    colorMode(HSB, 100);
    stroke(70, 30, colors.mtnb);
    strokeWeight(1);
    for (var x = 0; x < width; x ++) {
        var l = (x * landscapeScale2);
        var y = map(noise(l), 0, 1, height * 0.2, height * 0.5); //constrains hills
        line(x, y, x, height); //creates vertical lines, forming a solid shape
    }

    colorMode(RGB);
    makeBuilding(); //creates buildings when letter u is pressed

    //creates hills in the middleground
    colorMode(HSB, 100); //switches color mode to HSB to help with color changes
    stroke(25, 50, colors.hill);
    for (var x = 0; x < width; x ++) {
        var l = (x * landscapeScale1);
        var y = map(noise(l), 0, 1, height * 0.4, height * 0.6); //constrains hills
        line(x, y, x, height); //creates vertical lines, forming a solid shape
    }

    //creates field in foreground
    noStroke();
    fill(28, 100, colors.fieldb); //field color
    rect(0, height * 0.55, width, height * 0.35);
    changeField(); //changes color of field, letter t
    
    //creates lake
    fill(colors.lakeh, colors.lakes, colors.lakeb);
    ellipse(175, 286, lakeWidth, lakeHeight);
    changeLake(); //changes color and size of lake, letters a, w, and z

    colorMode(RGB); //switches color mode back to RGB
    makeFlowers(); //makes flowers, alternating colors, becomes one color with letter b
    makeHouse(); //makes houses, letter p
    makeTrees(); //makes single yellow tree in foreground, tree disappears with letter d 
    smokeScreen(); //creates a semi-transparent grey layer with letter j
}

//makes and moves the clouds
function makeClouds() {
    if (cE.clouds == true) { //makes a cloud for carbon footprint, letter c
        drawClouds(cloudX, 120, 150, 50, 150);
        cloudX = cloudX - 2;
        if (cloudX < win.ws - 150) {
            cloudX = 475;
        }
    }
    if (cE.gas == true) { //makes a cloud for greenhouse gases, letter g
        drawClouds(gasX, 180, 220, 75, 100);
        gasX = gasX - 1;
        if (gasX < win.ws - 220) {
            gasX = 475;
        }
    }
}

//draws the clouds
function drawClouds(locationX, locationY, width, height, color) {
    push();
    fill(color);
    translate(locationX, locationY);
    rect(0, - height, width, height, 50, 50, 50, 50);
    pop();
}

//adds "smog" in background
function changeSky() {
    if (cE.sky == true) {
        fill(80, colors.smogop);
        noStroke();
        rect(0, height * 0.55 - skyHeight, width, skyHeight);
        skyHeight = constrain(skyHeight, 0, 300) + 3;
    }
}

//changes colors of mountains and hills
function changeMtnHill() {
    if (cE.mtn == true) { //changes mountain colors
        colors.mtnb = constrain(colors.mtnb, 20, 70) - 5;
    }
    if (cE.hill == true) { //changes hill colors
        colors.hill = constrain(colors.hill, 30, 80) - 5;
    }
}

//makes the buildings for urban sprawl
function makeBuilding() {
    if (cE.building == true) { //from left to right
        drawBuildings(125, 37, 130, 180);
        drawBuildings(162, 62, 110, 150);
        drawBuildings(224, 50, 120, 200);
        drawBuildings(274, 37, 100, 130);
    }
}

//draws buildings
function drawBuildings(locationX, buildW, buildH, color) {
    push();
    noStroke();
    fill(color);
    translate(locationX, height * 0.58);
    rect(0, - buildH, buildW, buildH);
    //windows in buildings
    fill(255);
    for (var x = 0; x < (buildW / 6) - 1; x ++) {
        for (var y = 0; y < 200; y ++) {
            rect(2 + x * 6, - buildH + 4 + y * 8, 3, 4);
        }
    }
    pop();
}

//changes lake size and color
function changeLake() {
    if (cE.lake == true) { //makes larger for water level
        lakeWidth = constrain(lakeWidth, 200, 350) + 5;
    }
    if (cE.lakeColor == true) { // becomes green for algae
        colors.lakeh = constrain(colors.lakeh, 30, 55) - 0.75;
    }
    if (cE.deadz == true) { //becomes dark for dead zones
        colors.lakes = constrain(colors.lakes, 50, 100) + 5;
        colors.lakeb = constrain(colors.lakeb, 35, 100) - 5;
    }
}

//changes color of field
function changeField() { 
    if (cE.field == true) {
        colors.fieldb = constrain(colors.fieldb, 20, 60) - 2;
    }
}

//makes row of flowers at base of hills
function makeFlowers() {
    for (var i = 0; i < 20; i ++) {
        strokeWeight(2);
        for (var g = 0; g < 6; g ++) {
            if (i % 2) { //alternates colors of flowers to represent biodiversity
                stroke(colors.plnflw);
                push();
                translate(win.ws + i * 25, height * 0.55);
                rotate(g * 60);
                line(0, 0, 0, 3);
                pop();   
            } else {
                stroke(colors.flower);
                push();
                translate(win.ws + i * 25, height * 0.55);
                rotate(g * 60);
                line(0, 0, 0, 3);
                pop();   
            }
        }
    }
    if (cE.flowers == true) { //changes color of flowers to single color, removal of biodiversity
        colors.flower = colors.plnflw; 
    }
}

//makes houses for population growth
function makeHouse() {
    if (cE.house == true) {
        drawHouses(320, height * 0.58, '#FCAD77', '#E8762A', 0.9); //left
        drawHouses(375, height * 0.57,'#7BE0FF', '#1CB8E8', 0.8); //right
        drawHouses(350, height * 0.59,'#B09DCC', '#9274C1', 1.0); //middle
    }
}

//draws houses
function drawHouses(locationX, locationY, colorHouse, colorDoor, houseScale) {
    push();
    noStroke();
    fill(colorHouse);
    translate(locationX, locationY);
    scale(houseScale);
    rect(0, -20, 40, 20);
    fill('white');
    triangle(-5, -20, 45, -20, 20, -30);
    rect(24, -15, 10, 10);
    fill(colorDoor);
    rect(8, -15, 8, 15);
    pop();   
}

//makes tree in foreground of landscape
function makeTrees() {
    if (cE.trees == true) { //the tree disapears when key is pressed, as key switches variable to false
        push();
        rectMode(CENTER);
        translate(440, 110);
        noStroke();
        fill(249, 176, 30); //yellow of tree
        rect(0, 0, 130, 200, 40, 40, 40, 40);
        //tree branches
        strokeWeight(15);
        stroke(124, 20, 22);
        line(0, -50, 0, 225);
        strokeWeight(10); //thinner branches
        line(0, 80, 40, 40);
        line(0, 20, -40, -20);
        pop();
    }  
}

function smokeScreen() { //creates grey screen
    if (cE.jeopardy == true) {
        noStroke();
        jtranslu = constrain(jtranslu, 0, 80) + 15;
        fill(150, jtranslu);
        rect(win.ws, win.wt, 350, 300)
    }
}

//creates all aspects of the room - window, window frame, walls, table, poster, instructions
function drawRoom() { 
    rectMode(CORNER);
    noStroke();
    
    //walls of the room
    fill(colors.wall);
    rect(-win.dis, -win.dis, win.ws + win.dis, height + win.dis * 2);
    rect(width - win.ws, -win.dis, win.ws + win.dis, height + win.dis * 2);
    rect(0, height - win.wb, width, win.wb);
    rect(0, 0, width, win.wt);
    
    //gray "table" below window where alphabet and definitions appear
    fill(colors.table); //medium gray
    rect(0, height - 130, width, 130);

    //white lines that divide window
    stroke(255);
    strokeWeight(4);
    line(width / 2, win.wt - win.frm, width / 2, height - win.wb + win.frm);
    line(win.ws - win.frm, (height - win.wb) / 2 + win.wt / 2, width - win.ws + win.frm, (height - win.wb) / 2 + win.wt / 2);
    //white border around the window
    strokeWeight(10);
    line(win.ws - win.frm, win.wt - win.frm, win.ws - win.frm, height - win.wb + win.frm);
    line(width - win.ws + win.frm, win.wt - win.frm, width - win.ws + win.frm, height - win.wb + win.frm);
    line(win.ws - win.frm, win.wt - win.frm, width - win.ws + win.frm, win.wt - win.frm);
    line(win.ws - win.frm, height - win.wb + win.frm, width - win.ws + win.frm, height - win.wb + win.frm);

    drawPoster(); //draws the poster to the left of window
    drawInstructions(); //draws instructions on the gray "table"
}

function drawPoster() { //creates poster telling you to refresh
    push();
    noStroke();
    rectMode(CENTER);
    angleMode(DEGREES);
    textAlign(CENTER);
    fill(255);
    translate(60, 165);
    rotate(-3); //rotates poster slightly
    rect(0, 0, 75, 100);
    //pin at the top of the poster
    fill(colors.table); 
    ellipse(0, -40, 5, 5);
    //writing in gray telling you to refresh
    textSize(12);
    textLeading(15); //sets leading
    var poster = "Refresh\npage\nto\nrestart"; //puts each word on a new line
    text(poster, 0, -17);
    pop();
}

function drawInstructions() { //creates instructions
    textAlign(CENTER);
    noStroke();
    fill(255);
    var textLocation = 375;
    textSize(12);
    text("Press and hold down keys to see some ABCs of climate change", width / 2, textLocation);
}

//creates alphabet
function typedLetterAlphabet() {
    for (var i = 0; i < 26; i ++) {
        textAlign(CENTER);
        textSize(20);
        noStroke();
        var alphabetLocation = 400;
        var x = map(i, 0, alphabetLetters.length, 50, width - 30);
        if (keyCode === 65 + i) { //when key is down, letter turns grey
            fill(100);
            text(alphabetLetters[i], x, alphabetLocation);
        } else { //all letters green when not pressed
            fill(colors.type);
            text(alphabetLetters[i], x, alphabetLocation);
        } 
    }
}

//creates climate change info
function typedLetterInfo() {
    textAlign(CENTER);
    textSize(15);
    fill(255);
    for (var i = 0; i < 26; i ++) {
        if (keyCode === 65 + i) {
            text(letterInfo[i], width / 2, height - 45);  
        } 
    }   
}

//when key released, already pressed letters disapear
function keyReleased() { //replaces already clicked letters and their info with spaces in the respective arrays
    for (var i = 0; i < 26; i ++) {
        if (keyCode == 65 + i) {
            alphabetLetters.splice(i, 1, " ");
            letterInfo.splice(i, 1, " ");
        }       
    }
}

For this project, I initially wanted to use sound and the typing keyboard in order to create an instrument out of sounds related to the environmental crisis. However, I decided to instead pivot towards creating an interactive and visual display of some “ABCs” of climate change. When you type and hold down a key, a word that starts with that letter will appear, along with that word’s definition. For a little less than half of the letters in the alphabet, something related to the word will happen on the landscape that is visible outside of the window. I wanted to keep the interactions fairly simple, while still conveying some of the effects of climate change, global warming, and our actions as a society.

Crystal-Xue-Final-Project

sketch-134js


var imgPhone;
var imgLogo;
var imgAlb;
var imgBg;
var imgLike;
var song;
var amp;
var fft;
var volhistory = [];
var scrWid = 270; // width of the iphone screen

function preload(){
    //preloading images and sound source
    imgPhone = loadImage("https://i.imgur.com/Mb4yoMB.jpg?2");
    imgLogo = loadImage("https://i.imgur.com/sFAzrlV.jpg?3");
    imgAlb = loadImage("https://i.imgur.com/EAsdXzG.png?1");
    imgBg = loadImage("https://i.imgur.com/Ztd0x2o.jpg")
    imgLike = loadImage("https://i.imgur.com/vX1cA55.jpg?1");
    song = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/12/instagram.wav");
    song.setVolume(0.5);
}

function setup() {
    createCanvas(323, 635);
    song.play();
    amp = new p5.Amplitude();
    fft = new p5.FFT(0.9, 256);
    angleMode(DEGREES);

}

function draw() {
    background(255);

    var vol = amp.getLevel();
    volhistory.push(vol);

    //setup constrains for bountries
    var topWall = scrWid / 2 + 130;
    var botWall = height - scrWid / 2 - 100;
    var yc = constrain(mouseY, topWall, botWall);

    //draw iphne and instagram logo
    image(imgPhone, 0, 0);
    image(imgLogo, width / 2 - 50, 47);

    drawPost();

    push();
    translate((width - scrWid) / 2, yc-height + scrWid - 40);
    drawfft();
    pop();

    push();
    translate((width - scrWid) / 2, -height + scrWid / 2 + yc);
    drawAmp();
    pop();

    push();
    translate(width / 2, yc);
    drawCir();
    pop();


}

function drawfft(){
    //setup colors
    var colorvar = frameCount;
    if (colorvar >= 255) {
        colorvar = 0;
    } else {colorvar = frameCount;}
    var rC = map(colorvar, 0, width, random(255), 255);
    var g = map(colorvar, 0, width, random(255), 100);
    var b = map(colorvar, 0, height, random(255), 255);
    strokeWeight(2);
    stroke(rC, g, b, 90);

    var spectrum = fft.analyze();
    for (var i = 0; i < spectrum.length; i++) {
        var y = map(spectrum[i], 0, 250, 0, scrWid);
        //spacings
        var w = scrWid/64;
        line((i)*w, scrWid, (i)*w , y+scrWid);
    }
}

function drawAmp(){
    //setup colors
    var colorvar = frameCount;
    if (colorvar >= 255) {
        colorvar = 0;
    } else {colorvar = frameCount;}
    var rC = map(colorvar, 0, width, random(255), 255);
    var g = map(colorvar, 0, width, random(255), 100);
    var b = map(colorvar, 0, height, random(255), 255);

    noStroke();
    fill(rC, g, b, 90);
    //draw amplitutes
    beginShape();
    vertex(0, height);
    for (var i = 0; i < volhistory.length; i++) {
        var y = map(volhistory[i], 0, 1, height, 0);
        vertex(i, y);
    }
    vertex(scrWid, height);
    endShape();

    //make sure the graphs stays inside the width of the screen
    if (volhistory.length > scrWid) {
        volhistory.splice(0, 1);
    }
}

function drawPost(){
    rectMode(CENTER);
    stroke(255);
    //setup constrains
    var topWall = scrWid / 2 + 130;
    var botWall = height - scrWid / 2 - 100;
    var yc = constrain(mouseY, topWall, botWall);

    //draw post images
    fill(100);
    rect(width/2, yc, scrWid, scrWid);
    //draw album profile image
    image(imgAlb, 35, yc - scrWid / 2 - 60, 45, 45);
    //draw background image
    image(imgBg, width / 2 - scrWid / 2, yc - scrWid / 2, scrWid, scrWid);
    //draw like image
    image(imgLike, 250, yc, 40 + random(0, 5) ,32 + random(0, 5));

    //draw post texts
    textSize(14);
    text("DEAN", 100,  yc-scrWid / 2 - 30);
    textSize(12);
    text(frameCount, 30, yc + scrWid / 2 + 30);
    text("likes", 70, yc + scrWid / 2 + 30);
    text("#dean #instagram #soundvisualization", 30, yc + scrWid / 2 + 45);
    text("All night just wasting time like this", 30, yc + scrWid / 2 + 60);
    text("Inside your Instagram", 30, yc + scrWid / 2 + 75);
}

function drawCir(){
    noFill();
    stroke(255);
    strokeWeight(1.5);
    beginShape();
    for (var i = 0; i < 360; i++) {
        var r = map(volhistory[i], 0, 1, 10, 300);
        var x = r*cos(i);
        var y = r*sin(i);
        vertex(x,y);
    }
    endShape();
    //make sure that the the lines don't overlap
    if (volhistory.length > 360) {
        volhistory.splice(0, 1);
    }
}

This Project is about visualization of the song “Instagram”. The main theme is to deliver the message of social media anxiety among teenagers that everything is about the obsession of “likes” and how to gain popularity through posts. I mimicked the Instagram app page to set the base of this project. And as the progression of the song is represented by the intensity of the graphics. The anxiety also levels up when the “like” number goes up over time.

STAGE 1
STAGE 2
STAGE 3

Claire Lee – Final Project

Instructions: move the net with the mouse and click on the litter to remove it from the ocean! Make sure not to harm any of the sea creatures in the process. Collect as many points as possible in one minute.

finalproject

/* Claire Lee
15-104 Section B
seoyounl@andrew.cmu.edu
Final Project */

var bubbles = [];
var fish = [];
var bottle = [];
var seaTurtle = [];
var candyWrapper = [];
var candyColors = ["pink", "red", "gold", "plum", "dodgerblue"];
var score = 0;
var time = 60;
// arrays to store objects

function setup() {
    createCanvas(600, 240);
    frameRate(10);

    for (i = 0; i < 50; i++) {
        bubbleX = random(width);
        bubbleY = random(height);
        bubbles[i] = blowBubbles(bubbleX, bubbleY);
    }

    for (k = 0; k < 10; k++) {
        fishX = random(width);
        fishY = random(0, 220);
        fish[k] = makeFish(fishX, fishY);
    }

    for (j = 0; j < 6; j++) {
        bottleX = random(width);
        bottleY = random(20, 140);
        bottle[j] = makeBottle(bottleX, bottleY);
    }

    for (l = 0; l < 4; l++) {
        seaTurtleX = random(width);
        seaTurtleY = random(60, 140);
        seaTurtle[l] = makeSeaTurtle(seaTurtleX, seaTurtleY);
    }

    for (m = 0; m < 4; m++) {
        candyWrapperX = random(width); 
        candyWrapperY = random(20, 140);
        candyWrapper[m] = makeCandyWrapper(candyWrapperX, candyWrapperY);
    }
}

function keyPressed() {
    if (time == 0 & keyCode === 32) {
        bubbles = [];
        fish = [];
        bottle = [];
        seaTurtle = [];
        candyWrapper = [];
        score = 0;
        time = 60;
        setup();
        loop();
    }
    // pressing space bar will restart the game
}

function draw() {
    background(140, 225, 255);
    showBubbles();
    showFish();
    showBottle();
    showSeaTurtle(); 
    showCandyWrapper();
    showSand();

    netW = 40;
    netH = 20;
    netX = mouseX;
    netY = mouseY;
    if (netX > width / 2) {
        netX = width / 2;
    }

    if (netY > height - 20) {
        netY = height - 20;
    }

    if (frameCount % 10 == 0 & time > 0) {
        time--;
    } // using frameCount to create a 60s timer

    if (time == 0) {
        noStroke();
        fill(120, 205, 235, 120);
        rect(0, 0, width, height);
        noStroke();
        fill(0);
        textSize(15);
        text('The End!', (width / 2) - 50, height / 2 - 30);
        text('Final Score: ' + score, width / 2 - 70, height / 2 - 10);
        text('Press SPACE to restart', width / 2 - 90, 3 * height / 4);
    } // end of game

    if(time != 0) {
        stroke(0);
        strokeWeight(2);
        line(netX, 0, netX, netY - (netH / 2));
        stroke(255);
        strokeWeight(1);
        fill(255, 255, 255, 100);
        arc(netX, netY, netW, netH, HALF_PI, -HALF_PI);
        ellipse(netX, netY, netW / 4, netH);

        noStroke();
        fill(0);
        textSize(15);
        text('Score: ' + score, 500, 230);
        text('Time Remaining: ' + time + ' s', 325, 230);
    } // during game: code for user-controlled net and bottom text
}

var sandSpeed = 0.0004;

function showSand() {
    var sand = 0.01;
    stroke(235, 220, 180);
    strokeWeight(2);
    beginShape();
    for (var i = 0; i < width; i++) {
        var x = (i * sand) + (millis() * sandSpeed);
        var s = map(noise(x), 0, 1, 190, 210);
        line(i, s, i, height);
        // using map() and noise() function to generate randomized sand terrain
    }
    endShape();
}

function blowBubbles (bubbleX, bubbleY) {
    // making bubble object
    var bubble = {
        x: bubbleX,
        y: bubbleY,
        radius: random(8, 12),
        speed: -0.5,
        float: floatBubbles, 
        draw: drawBubbles 
    }
    return bubble;
}

function floatBubbles() {
    // making the bubbles move horizontally and float
    this.x += (this.speed * 2);
    this.y += this.speed;
    if (this.x <= 0) {
        this.x = width;
    }
    if (this.y <= 0) {
        this.y = height;
    } // bubbles "pop" when they reach top of screen
}

function drawBubbles() {
    push();
    translate(this.x, this.y);
    strokeWeight(1);
    stroke(255, 255, 255, 90);
    fill(160, 245, 255, 90);
    ellipse(1, 10, this.radius, this.radius);
    noStroke();
    fill(255, 255, 255, 90);
    ellipse(-1, 8, 2, 2);
    pop();
}

function showBubbles() {
    for (i = 0; i < bubbles.length; i++) {
        bubbles[i].float();
        bubbles[i].draw();
    }
}

function makeFish(fishX, fishY) {
    // making the fish object
    var fish = {
        fx: fishX,
        fy: fishY,
        fishspeed: random(-3, -8),
        fishmove: moveFish,
        fishcolor: random(100, 150),
        fishdraw: drawFish
    }
    return fish;
}

function moveFish() {
    this.fx += this.fishspeed;
    if (this.fx <= -10) {
        this.fx += width;
        this.fy = random(height);
        this.fspeed = random(-3, -8);
    }
}

function drawFish() {
    var tailLength = 8;
    noStroke();
    fill(this.fishcolor);
    ellipse(this.fx, this.fy, 12, 6);
    triangle(this.fx + (tailLength / 2), this.fy, this.fx + tailLength, this.fy - 3, this.fx + tailLength, this.fy + 3);
}

function showFish() {
    for (i = 0; i < fish.length; i++) {
        fish[i].fishmove();
        fish[i].fishdraw();
    }
}

function makeBottle(bottleX, bottleY) {
    //making the bottle object
    var bottle = {
        bx: bottleX,
        by: bottleY,
        bspeed: random(-3, -8), 
        bmove: moveBottle,
        bdraw: drawBottle,
    }
    return bottle;
}

function moveBottle() {
    this.bx += this.bspeed;
    if (this.bx <= -15) {
        this.bx += width;
        this.by = random(20, 160);
        this.bspeed = random(-3, -8);
        if (time != 0) score -= 20;
        // if bottle isn't clicked, points are deducted
    }
}

function drawBottle() {
    bottleW = 7;
    bottleH = 14;
    bottleCapW = 3;
    bottleCapH = 2;
    stroke(255);
    strokeWeight(0.5);
    fill(255, 255, 255, 50);
    rect(this.bx, this.by, bottleW, bottleH);
    triangle(this.bx, this.by, this.bx + (bottleW / 2), this.by - 4, this.bx + bottleW, this.by);
    noStroke();
    fill(0, 160, 0);
    rect(this.bx + (bottleW / 2) - (bottleCapW / 2), this.by - 5, bottleCapW, bottleCapH);
    noStroke();
    fill("turquoise");
    rect(this.bx, this.by, bottleW, bottleH / 3);
}

function showBottle() {
    for (i = 0; i < bottle.length; i++) {
        bottle[i].bmove();
        bottle[i].bdraw();
    }
}

function makeSeaTurtle (seaTurtleX, seaTurtleY) {
    //making the sea turtle object
    var seaTurtle = {
        stx: seaTurtleX,
        sty: seaTurtleY,
        stspeed: random(-3, -6),
        stmove: swimSeaTurtle,
        stdraw: hatchSeaTurtle
    }
    return seaTurtle;
}

function swimSeaTurtle() {
    this.stx += this.stspeed;
    if (this.stx <= -15) {
        this.stx += width;
        this.sty = random(60, 140);
        this.stspeed = random(-3, -6);
    }
}

function hatchSeaTurtle() {
    shellW = 30;
    shellH = 20;
    stroke(110, 150, 75);
    strokeWeight(0.5);
    fill(175, 200, 90);
    ellipse(this.stx - 17, this.sty - 3, 12, 5);
    arc(this.stx - 5, this.sty, 7, 15, HALF_PI - QUARTER_PI, PI + HALF_PI - QUARTER_PI, CHORD);
    arc(this.stx + 10, this.sty, 5, 15, HALF_PI - QUARTER_PI, PI + HALF_PI - QUARTER_PI, CHORD);
    triangle(this.stx + (shellW / 4) + 3, this.sty - 4, this.stx + (shellW / 2) + 3, this.sty - 5, this.stx + (shellW / 4) + 3, this.sty);
    stroke(70, 125, 30);
    strokeWeight(0.5);
    fill(110, 150, 75);
    arc(this.stx, this.sty, shellW, shellH, -PI, 0, CHORD);
    noStroke();
    fill(50, 105, 10);
    ellipse(this.stx - 18, this.sty - 2, 1, 1);
    stroke(175, 200, 90);
    strokeWeight(0.5);
    noFill();
    arc(this.stx, this.sty - 8, 15, 5, 0, PI, OPEN);
    line(this.stx - 4, this.sty - 5.5, this.stx - 6, this.sty - 0.5);
    line(this.stx + 4, this.sty - 5.5, this.stx + 6, this.sty - 0.5);
}

function showSeaTurtle() {
    for (i = 0; i < seaTurtle.length; i++) {
        seaTurtle[i].stmove();
        seaTurtle[i].stdraw();
    }
}

function makeCandyWrapper() {
    // making the caondy wrapper object
    var colorRand = Math.floor(random(0,5));
    var candyWrapper = {
        cwx: candyWrapperX,
        cwy: candyWrapperY,
        cwspeed: random(-3, -6),
        cwmove: floatCandyWrapper,
        cwdraw: tossCandyWrapper,
        cwcolor: candyColors[colorRand]
    }
    return candyWrapper;  
}

function floatCandyWrapper() {
    this.cwx += this.cwspeed;
    if (this.cwx <= -15) {
        this.cwx += width;
        this.cwy = random(60, 140);
        this.cwspeed = random(-3, -6);
        if (time != 0) score -= 5;
        // if candy wrapper isn't clicked, points are deducted
    }
}

function tossCandyWrapper() {
    candyW = 7;
    candyH = 5;
    noStroke();
    fill(this.cwcolor);
    ellipse(this.cwx, this.cwy, candyW, candyH);
    triangle(this.cwx - (candyW / 2), this.cwy, this.cwx - (candyW / 2) - 3, this.cwy - 2, this.cwx - (candyW / 2) - 3, this.cwy + 2);
    triangle(this.cwx + (candyW / 2), this.cwy, this.cwx + (candyW / 2) + 3, this.cwy - 2, this.cwx + (candyW / 2) + 3, this.cwy + 2);
}

function showCandyWrapper() {
    for (i = 0; i < candyWrapper.length; i++) {
        candyWrapper[i].cwmove();
        candyWrapper[i].cwdraw();
    }
}

function mousePressed() {
    //making the objects "reset" when pressed on by mouse
    for(var i = 0; i < fish.length; i++) {
        if (dist(netX - 7, netY, fish[i].fx, fish[i].fy) < 10 & mouseIsPressed && time != 0) {
            fish[i].fx = width;
            fish[i].fy = random(height);
            fish[i].fspeed = random(-3, -8);
            score -= 5;
            // if fish are clicked, points are deducted
        }
    }

    for(var i = 0; i < seaTurtle.length; i++) {
        if (dist(netX - 7, netY, seaTurtle[i].stx, seaTurtle[i].sty) < 15 & mouseIsPressed && time != 0) {
            seaTurtle[i].stx = width;
            seaTurtle[i].sty = random(height);
            seaTurtle[i].stspeed = random(-3, -6);
            score -= 25;
            // if turtle is clicked, points are deducted
        }
    }

    for(var i = 0; i < bottle.length; i++) {
        if (dist(netX - 7, netY, bottle[i].bx, bottle[i].by) < 10 & mouseIsPressed && time != 0) {
            bottle[i].bx = width;
            bottle[i].by = random(height);
            bottle[i].bspeed = random(-3, -6);
            score += 20;
        }
    }

    for(var i = 0; i < candyWrapper.length; i++) {
        if (dist(netX - 7, netY, candyWrapper[i].cwx, candyWrapper[i].cwy) < 10 & mouseIsPressed && time != 0) {
            candyWrapper[i].cwx = width;
            candyWrapper[i].cwy = random(height);
            candyWrapper[i].cwspeed = random(-3, -8);
            score += 5;
        }
    }
}

For my final project, I made an interactive game that’s supposed to serve as a commentary on the ocean pollution crisis. The game itself is a metaphor: we have a limited amount of time to solve the problem of ocean pollution, and finding a solution requires awareness and active work.

Working more with using objects in code and figuring out how to make objects interactive was a really fun process. I also liked testing out my game and thinking about the ways to improve the user-oriented aspects of my game design.

Ghalya Alsanea – Final Project

Project Interface

I created a smart watch interface for someone with anxiety and panic disorder. It reads someone’s pulse and breath rate and can detect when someone is having a panic attack. I used real-time biometric data from a pulse sensor and a wind sensor and hooked it up to an ardiuno, then I used serial communication to send the data and trigger & display different calming graphics using p5.js. I will go through the project interface and design first, then I will show you how I went about doing it.

Regular Interface

The regular display when someone is not having a panic attack consists of a clock, the date, and a BPM graphic that reacts to your pulse.

Interface during a panic attack

Depending on the kind of panic attack you are having, there are 3 modes/techniques. The good thing is that any of these techniques would work during a panic attack. I weighted certain techniques to be triggered based on someone’s sensor data. But for the purpose of the scope of this project, and because this requires more knowledge, time and research, I used keyboard keys to to demonstrate the different interfaces.

  1. Deep Breathing Technique (for when you’re hyperventilating)
  2. Counting Technique (for when your heart rate cannot be controlled)
  3. Distraction Technique (for when you need to be distracted from your overwhelming thoughts)

These techniques and methods were based on my research on medical articles here, here, and here.

Deep Breathing
The blobs are made to soothe you and help you concentrate on your breathing. As you breath in, the blobs expand, and as you exhale the deflate.
Counting

“Recently I found that trying to focus on something else such as counting numbers… works, because it requires concentration.”

Melanie Luxenberg
The idea here is you count to 5 over and over again, until you naturally realize that your slowly calming down. Before you know it, as you’re trying to fill the canvas with tick marks, you’ll start to feel much better!
Distraction
This is a game interface to act as a distraction technique. Note the large “IT’S OK!” text as an emphasis on how it’s okay that you lost, and you can try again, since it’s not about winning. This way, you can play for as long as you want and/or until you feel calmer.

Proof of Concept

Testing the pulse sensor and how it communicates using the p5.js serial port.
Testing the wind sensor and how it communicates with the p5.js graphics
Going through all the different types of interfaces and how they react to the user’s biometric sensor data and interactions.

How to Implement it

I used the step by step guide provided by NYU’s physical computing department here to learn how to use ardiuno and p5.js together. If you follow that guide, you can know how to download and run the project. You will need to download the P5.js complete library, the P5.serialserver, and the ardiuno software.

Circuit Sketch of how to hook up the sensors to an ardiuno

You can download all the files you need to run the project here:

galsanea_FinalProject_allFiles

WordPress Version

If you just want to see the p5.js interaction, I modified the project to run on WordPress using mouse X location to simulate breathing pattern, and mouse Y location to simulate pulse patterns.

Use the following to replace the sensor data interaction:

keys:
b:
breathing blobs interface
c: counting interface
g: game interface
any other key: regular clock interface

mouse:
x:
move mouse in x directions to simulate breathing
y: move mouse in y directions to simulate heart beat

sketch

/* 
Ghalya Alsanea
Section B
galsanea@andrew.cmu.edu
Final Project

normal mode:clock
            date
            sensor data: heart rate with beating heart

panic mode: deep breathing --- moving blob objects reacting to your breathing
            distraction technique --- use pingpong-like game to distract you
            counting technique --- count till a number to calm down
*/
// ----------------------------------------------------------------------------
// overall global variables
var proportion = 500;   //canvas proportion
var MX, MY;             //mouse x and y locations
// ----------------------------------------------------------------------------
//SENSOR global variables
var pulseSensor;        //global variable for pulse sensor data
var breathSensor;       //global variable for breath sensor data
var toggle = false;     //toggle to help sort the incoming sensor data from the serial
var pulseData = [];     //array container for incoming pulse data
// ----------------------------------------------------------------------------
// SERIAL global variables
var serial;                     // variable to hold an instance of the serialport library
var portName = 'COM11';         // fill in your serial port name here
var inData;                     // for incoming serial data. NOTE: it comes a byte at a time
var options = {baudrate: 9600}; // change the data rate to whatever you wish
// ----------------------------------------------------------------------------
// CLOCK global variables
var diameter = proportion * 0.75;   //clock diameter
//configure clock hands based on clock diameter
var secHand = diameter * 0.45;
var minHand = diameter * 0.35;
var hourHand = diameter * 0.2;
// ----------------------------------------------------------------------------
// BLOBS global variables
var blobs = [];     //used to store instances of blob objects
var change = 0;     //stores the rate of rotation and the y coordinate for noise later
var colorsPalette;  //color palette for blob objects
var numBlobs = 120; //number of blobs to make
// ----------------------------------------------------------------------------
// GAME global variables
var ballX;          //ball x position
var ballY;          //ball y position
var ballSize = 40;  //diameter of ball
var xvel;           //horizontal velocity
var yvel;           //vertical velocity
var d;              //distance between mouse and circle center
var dangerX;        //danger zone
var ballColor;      //what to color the ball
var score;          //keep score of hits
var scores = [];    //keep a list of all final scores to get the highest score
// ----------------------------------------------------------------------------
// CLICKER global variables
var gClickCount = 0;    //mouse click counter
var inc = 20;           //x increments
var yInc = 100;         //y increments         
var yPos1 = 25          //top y position
var yPos2 = yPos1 + 50; //bottom y position
// ----------------------------------------------------------------------------

function setup() {
    createCanvas(proportion, proportion);
    // setupSerial();   ..comment out to disable serial
    setupBlobs();
    setupGame();
}

// ----------------------------------------------------------------------------

function draw() {
    //constrain mouse x and y to within the canvas
    MX = constrain(mouseX, 0, width);
    MY = constrain(mouseY, 0, height);

    if (key === "b") {
        //draw the blobs
        displayBlobs();
    } else if (key === "g") {
        //draw the game
        drawGame();
    } else if (key === "c") {
        //draw clicker
        displayClicker();
    } else {
        clockDisplay();
    }
}

////////////////////////////////////////////////////////////////////////
//**************************** SERIAL ********************************//
////////////////////////////////////////////////////////////////////////

function setupSerial() {
    serial = new p5.SerialPort();       // make a new instance of the serialport library
    serial.on('data', serialEvent);     // callback for when new data arrives
    serial.open(portName);              // open the serial port
}

function serialEvent() {
    // read a byte from the serial port, convert it to a number
    // NOTE: the inData comes in a byte at a time (so range is 0-255)
    inData = serial.read();
    // sort the data incoming by distributing every other byte to their respective variable
    // serial data comes in 2 at a time, so using a toggle you can store the data correctly
    if (toggle) {
        pulseSensor = inData;
    } else {
        breathSensor = inData;
    }
    toggle = !toggle;
}


////////////////////////////////////////////////////////////////////////
//*********************** base/CLOCK Display *************************//
////////////////////////////////////////////////////////////////////////

function clockDisplay() {
    background(255, 228, 225);    //mistyRose
    noStroke();

    //show the date
    dateDisplay();

    //for testing: using mouse data instead of sensor data
    var size = map(MY, 0, height, 20, 40);
    var BPM = 88;

    // // for serial: map heart size to pulse sensor data
    // var size = map(pulseSensor, 0, 255, 20, 40);
    // 
    // //calculate beats per min (BPM) based on average pulse data
    // pulseData.push(pulseSensor);
    // //total pulse values read
    // var total = 0;
    // //find the total of all the readings
    // for(var i = 0; i < pulseData.length; i++) {
    //     total += pulseData[i];
    // }
    // //divide by the length of the data to get the average
    // var avg = total / pulseData.length;
    // //map the average number onto a range of BPM hear rates
    // var BPM = map(avg, 0, 255, 70, 130);
    //
    // //only read the last 100 readings
    // if (pulseData.length > 100) {
    //     pulseData.shift();
    // }

    //show the heart rate data
    heartDisplay(size, BPM);

    //draw from the center of the canvas
    push();
    translate(width / 2, height / 2);

    //draw clock base
    fill(255);
    circle(0, 0, diameter);

    //convert time to angles
    //subtract half a circle so the clock starts at the top, rather than 6 o'clock 
    var s = map(second(), 0, 60, 0, TWO_PI) - PI; 
    var m = map(minute(), 0, 60, 0, TWO_PI) - PI;
    //the % takes the remainder of the hours/12, which makes the dif between AM/PM 
    var h = map((hour() % 12), 0, 12, 0, TWO_PI) - PI;
 
    //draw the hands
    drawClockHands(s, secHand, 1);  //seconds hand
    drawClockHands(m, minHand, 3);  //minutes hand
    drawClockHands(h, hourHand, 5); //hour hand

    //draw the ticks every 6 degrees (6 * 60 = 360)
    for (i = 0; i < 360; i += 6) {
        //make every 5th tick thicker
        if(i % 30 == 0) {
            strokeWeight(2);
        } else {
            strokeWeight(1);
        }
        push();
        rotate(radians(i));
        line(0, diameter / 2, 0, secHand + 5);
        pop();
    }
    pop()
}

//draw the clock hands 
function drawClockHands(angle, hand, weight) {
    stroke("PaleVioletRed");
    push();
    rotate(angle);
    strokeWeight(weight);
    line(0, 0, 0, hand);
    pop();
}

//display today's date
function dateDisplay() {
    var d = day();
    var m = month();
    var y = year();
    fill("PaleVioletRed");
    textAlign(LEFT);
    textSize(18);
    text(m + "." + d + "." + y, 10, height - 15);
}

//display heart rate with heart graphic
function heartDisplay(size, BPM) {
    textSize(12);
    textAlign(CENTER);
    text(floor(BPM), width - 50, height - 60);
    drawHeart(width - 50, height - 50, size);
}

function drawHeart(x, y, size) {
    beginShape();
    vertex(x, y);
    bezierVertex(x - size / 2, y - size / 2, x - size, y + size / 3, x, y + size);
    bezierVertex(x + size, y + size / 3, x + size / 2, y - size / 2, x, y);
    endShape(CLOSE);
}

////////////////////////////////////////////////////////////////////////
//******************** breathing BLOB Display ************************//
////////////////////////////////////////////////////////////////////////

function setupBlobs() {
    //create color paletter for the blob objects
    colorsPalette = [color(146, 167, 202,30),
                    color(186, 196, 219,30),
                    color(118, 135, 172,30),
                    color(76, 41, 81,30),
                    color(144, 62, 92,30),
                    color(178, 93, 119,30),
                    color(215, 118, 136,30),
                    color(246, 156, 164,30)];

    // create new blob object for every num of blobs
    for (var i = 0; i < numBlobs; i++){
        var temp = makeBlob(i + 0.1, i, i * random(90),
            colorsPalette[floor(random(colorsPalette.length))]);
        blobs.push(temp);
    }
}

function displayBlobs() {
    background(240, 200, 240, 30);
    //for testing: using mouse data instead of sensor data
    var addToRadius = map(MX, 0, width, 0, 100);

    // //for serial: map the breathing sensor data to blob radius
    // //it's inversely mapped because when you breath out it decreases and vice versa
    // var addToRadius = map(breathSensor, 255, 0, 0, 100);

    for(var i = 0; i < blobs.length; i++){
        blobs[i].r = i + addToRadius;
        blobs[i].draw(change);
    }
    //create rotation change and noise change
    change+=0.01;
}

function makeBlob(radius, roughness, angle, color){
    var blob = {r: radius,      // radius of blob
                x: width / 2,   // x position of blob
                y: height / 2,  // y position of blob
                rough: roughness, // magnitude of how much the circle is distorted
                ang: angle,     // how much to rotate the circle by
                c: color,       // color of the blob
                draw: drawBlob, //draw the blobs
    }
    return blob;
}

function drawBlob(change) {
    noStroke(); 
    fill(this.c);   //color to fill the blob

    push();     
    translate(this.x, this.y);  //move to xpos, ypos
    rotate(this.ang + change);  //rotate by this.angle+change
    
    beginShape();   //begin a shape based on the vertex points below
    
    //create vertex points
    var off = 0;
    for (var i = 0; i < TWO_PI; i += 0.1) {
        var offset = map(noise(off, change), 0, 1, -this.rough, this.rough);
        var R = this.r + offset;
        var x = R * cos(i);
        var y = R * sin(i);
        vertex(x, y);
        off += 0.1;
    }
    endShape(); 
    pop();
}

////////////////////////////////////////////////////////////////////////
//************************ GAME display ******************************//
////////////////////////////////////////////////////////////////////////

function setupGame() {
    noStroke();
    //ball starts at the center of the canvas
    ballX = width / 2;
    ballY = height / 2;
    //define danger zone
    dangerX = width / 4;
    //set x and y velocity
    xvel = 5;
    yvel = 0;
    //set score
    score = 0;
}

function drawGame() {
    background (100);

    //draw the hitting paddle
    strokeWeight(5);
    stroke(255);
    line(mouseX, mouseY + 10, mouseX, mouseY - 10);

    //instructions
    noStroke();
    fill(255);
    textAlign(LEFT);
    writeText(20, "Let's play a game!", 20, 25);
    writeText(12, "Don't let the ball go left of the screen", 20, 50);

    //define the playing area
    fill(0);
    rect(width / 2, 0, width / 2, height);

    //warn if ball is too close to edge
    if(ballX - ballSize / 2 <= dangerX & xvel < 0){
        ballColor = "yellow"; 
    } else {
        ballColor = "white";
    }

    //draw the circle
    fill (ballColor);
    circle(ballX, ballY, ballSize);
    
    //makes the ball move in x and y directions
    ballX = ballX + xvel;
    ballY = ballY + yvel;

    //When it hits the right edge of the canvas,
    //it reverses horizontal velocity
    if (ballX >= width - ballSize / 2) {
        xvel = -1 * xvel;
    }

    //when it hits the bottom or top edge of the canvas,
    //it reverses vertical velocity
    if (ballY <= ballSize / 2) {
        yvel = -1 * yvel;
    } else if (ballY >= height - ballSize / 2) {
        yvel = -1 * yvel;
    }

    //find the distance between ball center and mouse
    d = dist(mouseX, mouseY, ballX, ballY);

    //if  (1) the mouse hits the edge of the ball 
    //and (2) the mouse is on the left half of the canvas
    //and (3) the x velocity is negative
    //then switch directions and get a random yvel. 
    if (d <= ballSize / 2 & mouseX <= width / 2 && xvel < 0){
        yvel = random(-3, 3);
        xvel = -1 * xvel;
        yvel = -1 * yvel;
        score = score + 1;            
    }

    //keep current score
    fill("white");
    writeText(30, score, width - 50, height - 50);

    //reset game if th eball went to far out the canvas
    if (ballX < -150) {
        resetGame();
    }
}

//reset game
function resetGame() {
    scores.push(score)          //push final score to scores
    background (0, 51, 102);
    fill(255);
    textAlign(LEFT);
    writeText(80, "IT'S OK!", 10, height / 4);
    writeText(30, "click to try again", 20, height / 2);
    //cureent score
    writeText(20, "score: " + score, 20, height / 2 + 50);
    //highest score
    writeText(20, "best score: " + max(scores), 20, height / 2 + 100);
    
    if (mouseIsPressed){
        setupGame();
    }
}

function writeText(size, words, x, y) {
    textSize(size);
    text(words, x, y);
}

////////////////////////////////////////////////////////////////////////
//*********************** COUNT display ******************************//
////////////////////////////////////////////////////////////////////////

function displayClicker() {
    background("pink");

    if (gClickCount === 0) {
        noStroke();
        fill(0);
        textAlign(LEFT);
        textSize(20);
        text("tap me and count to fives...", 20, 50);
    } 

    stroke (0);
    strokeWeight(3);

    //everytime you click, draw a line
    for(var i = 0; i < gClickCount; i++){
        //variable to show which line the tickmarks are on
        var a = floor((i * inc + inc - 1) / width);

        //x and y locations depending on which line
        var x = i * inc + inc - width * a;
        var y1 = yPos1 + a * yInc;
        var y2 = yPos2 + a * yInc;

        if ( (i + 1) % 5 === 0) {
            //draw diagonal line every fifth tick
            line(x, y1, x - 5 * inc, y2);
        } else {
            //otherwise draw a straight line
            line(x, y1, x, y2);
        }    
    }
}


function mousePressed() {
    //everytime you click, you increase the amount of times the loop runs
    gClickCount++;
}

function keyPressed() {
    //reset gClickCount to zero whenever a keyboard key is pressed
    gClickCount = 0;
}

Nadia Susanto – Final Project

sketch

// Nadia Susanto
// nsusanto@andrew.cmu.edu
// Section B
// Final Project

var terrainSpeed = 0.0005;
var terrainDetail = 0.003;
var clouds = [];
var pipes = [];
var birdY = 300;
var birdX = 65;
var birdGravity = 0.75;
var birdVelocity = 0;
var birdUpForce = -18;
var secs = 0;


function setup() {
    createCanvas(600, 600);
    background(135,235,250);
    pipes.push(makePipe());
    //initalizing clouds
    for (var x = 0; x < 5; x++) {
        var cx = random(width);
        clouds[x] = makeClouds(cx);
    }

    frameRate(60);

}

function draw() {
    //blue sky
    background(135,235,250);
    //show mountains and waves
    makeMountains();
    makeWaves();
    //show clouds
    addNewClouds();
    updateAndDisplayClouds();
    removeCloudsOutofView();
    //pipes
    addNewPipes();
    updateAndDisplayPipes();
    //bird
    drawBird();
    updateBird();
    //collision detection for bird with pipe
    topCollision();
    bottomCollision();
    if (frameCount % 60 == 0) {
        secs += 1;
    }
    //score
    fill("black");
    textSize(30);
    textAlign(LEFT);
    text("TIME = " + secs, 5, 50);

}

//collision for bird with top pipe
function topCollision() {
    for (var i = 0; i < pipes.length; i++) {
        if ((birdY < pipes[i].top + 30) & (birdX > pipes[i].x && (birdX < pipes[i].x + pipes[i].w))) {
            gameOver();
          }
      }
}

//collision for bird with bottom pipe
function bottomCollision() {
    for (var i = 0; i < pipes.length; i++) {
        if ((birdY > pipes[i].top + 150) & (birdX > pipes[i].x && (birdX < pipes[i].x + pipes[i].w))) {
            gameOver();
          }
      }
}

//game over function that stops the game when triggered
function gameOver() {
    fill("red");
    textAlign(CENTER);
    textSize(60);
    text("GAME OVER", width/2, height/2);
    noLoop();
}

//constantly calling the pipes
function updateAndDisplayPipes() {
  for (var i = 0; i < pipes.length; i++) {
      pipes[i].move();
      pipes[i].draw();
    }
  }

function addNewPipes() {
    if (frameCount % 150 == 0) {
        pipes.push(makePipe());
    }
}

//moving the pipes right to left
function movePipe() {
    this.x -= this.speed;
}

function drawPipe() {
  noStroke();
  //top pipe
  fill(112,186,45);
  rect(this.x, 0, this.w, this.top);
  rect(this.x - 5, this.top - 2, this.w + 10, 20, 10);
  //bottom pipe
  rect(this.x, height, this.w, this.top - height + 150);
  rect(this.x - 5, this.top + 135, this.w + 10, 20, 10);
}

//object for pipe
function makePipe() {
    var pipe = {x: width,
                w: 50,
                top: random(50, height/2),
                speed: 1.5,
                move: movePipe,
                draw: drawPipe
                }
    return pipe;
}

//drawing the bird
function drawBird() {
    //legs
    fill(0);
    ellipse(birdX - 10, birdY + 15, 3, 10);
    ellipse(birdX + 10, birdY + 15, 3, 10);
    //halo
    noFill();
    stroke("gold");
    ellipse(birdX, birdY - 20, 40, 5);
    //body
    fill("yellow");
    ellipse(birdX, birdY, 35, 35);
    //eye
    fill(0);
    ellipse(birdX + 5, birdY - 5, 5);
    //beak
    fill("orange");
    triangle(birdX + 18, birdY - 4, birdX + 23, birdY, birdX + 18, birdY + 4);

}

//implementing velocity and gravity
function updateBird() {
  birdVelocity += birdGravity;
  //air resistance
  birdVelocity *= 0.9;
  birdY += birdVelocity;
  //making sure it doesnt go beyond top and bottom of canvas
  if (birdY > height) {
      birdY = height;
      birdVelocity = 0;
    }
  if (birdY < 0) {
      birdY = 0;
      birdVelocity = 0;
  }
}

//adding up force to bird velocity
function birdUp() {
    birdVelocity += birdUpForce;
}

//if space bar pressed, trigger the bird to go up
function keyPressed() {
    if (key == ' ') {
        birdUp();
    }
}

//constantly calling the clouds
function updateAndDisplayClouds() {
    for (var x = 0; x < clouds.length; x++){
        clouds[x].move();
        clouds[x].draw();
    }
}

function removeCloudsOutofView() {
    var cloudsKeep = [];
    for (var x = 0; x < clouds.length; x++) {
        if (clouds[x].x > 0) {
            cloudsKeep.push(clouds[x]);
        }
    }
    clouds = cloudsKeep; //remember the clouds
}

function addNewClouds() {
    var cloudProb = 0.01;
    //if random number less than probability then a new cloud is shown
    if (random(0, 1) < cloudProb) {
        clouds.push(makeClouds(width));
    }
}

function cloudsMove() {
    //move the clouds from right to left
    this.x -= this.speed;
}

function drawClouds() {
    //draw the white clouds
    fill("white");
    ellipse(this.x, this.y, this.width, 10);
    ellipse(this.x - 25, this.y - 10, 35, 30);
    ellipse(this.x + 25, this.y - 10, 30, 30);
    ellipse(this.x + 5, this.y - 15, 40, 25);
}

function makeClouds(cloudX) {
  //creating object for cloud
  var cloud = {x: cloudX,
              y: 50,
              width: random(50, 100),
              speed: 0.50,
              move: cloudsMove,
              draw: drawClouds}
  return cloud;
}

//adopted the terrain starter code to make mountains in background
function makeMountains() {
    noStroke();
    fill(127,221,136);
    beginShape();
    for (var x = 0; x < width; x++) {
        var t = (x * terrainDetail*3) + (millis() * terrainSpeed);
        var y = map(noise(t), 0, 1, height/4, height/3);
        vertex(x, y);
      }
    vertex(width, height);
    vertex(0, height);
    endShape(CLOSE);
}

//adopted the terrain starter code to make ocean waves
function makeWaves() {
    noStroke();
    fill(1, 108, 194);
    beginShape();
    for (var x = 0; x < width; x++) {
        var t = (x * terrainDetail/3) + (millis() * terrainSpeed/2);
        var y = map(noise(t), 0, 1, height/2, height);
        vertex(x, y);
      }
    vertex(width, height);
    vertex(0, height);
    endShape(CLOSE);
}

For my final project I made angel bird! I have always been a gamer at heart and love competition whether it be beating my peers or my personal bests. Originally for my proposal I wanted to do scream go hero but had difficulties with voice recognition. Flappy bird was my backup and I am really happy with the way it turned out. I used my knowledge of objects to create the generative backgrounds and the pipes, and I believe my angel bird with a halo is pretty cute.

How the game works:

The game automatically starts and all you have to do is press the space bar to make the bird go up. Instead of scoring, you want to beat your personal best times as you want to see how long you can go without the dreaded “GAME OVER.” Enjoy!

Sewon Park – PO – Final

sketch

//Sewon Park
//sewonp@andrew.cmu.edu
//Final Project
//Section B

var counter = 0;
var oceanamp = 100;
var oceanspeed = 0.001;
var x = 150;
var polarbearURL = "https://i.imgur.com/dQOjRSb.png"


function preload() {
    polarbear = loadImage(polarbearURL);
}

function setup() {
    createCanvas(600, 400);
    background(180, 240, 250);
  
    fill(150, 75, 0);
    rect(380, 365, 10, 30);
    rect(440, 365, 10, 30);
    rect(500, 365, 10, 30); //Treetrunks

    fill(0, 255, 0)
    triangle(370, 365, 385, 325, 400, 365);
    triangle(430, 365, 445, 325, 460, 365);
    triangle(490, 365, 505, 325, 520, 365); //Tree Leaves
}

function draw() {
    fill(255, 100, 0);
    rect(400, 30, 100, 50); //button
    
    fill(255);
    textSize(13);
    text("Click", 435, 50); //click text on button

    strokeWeight(2);
	stroke(255);
	line(300, 0, 300, 400); //Divider down the middle

    noStroke();
    fill(255, 204, 51)
    ellipse(150, 100, 30, 30) //sun

    fill(255);
    rect(50, 250, 200, 200); //Iceberg
  
    noFill();
    rect(360, 330, 40, 20);
    rect(420, 330, 40, 20);
    rect(480, 330, 40, 20); //Windows

    ocean();

    imageMode(CENTER);
    image(polarbear, 150, 240, 50, 50);

    if (counter == 1) {
        fire1();
    } 
    if (counter == 2) {
        fire2();
    }
    if (counter == 3) {
        fire3();
    }
    if (counter == 4) {
        factory();
        ocean2(); //Increase water levels afer iceberg sinks
        newsun(); //New sun after factory is built
        textSize(30)
        text("GAME OVER", 210, 200)
    }
}

function ocean() {
    fill(0, 0, 250); 
    noStroke();
    beginShape(); 
    for (var x = 0; x < width/2; x++) {
        var position2 = x * oceanamp + (millis() * oceanspeed);
        var y = map(noise(position2), 0, 10, height/1.4, height); 
        vertex(x, y); 
    }
    vertex(width/2, height);
    vertex(0, height);
    endShape(); //The Ocean
}

function ocean2() {
    fill(0, 0, 250); 
    noStroke();
    beginShape(); 
    for (var x = 0; x < width/2; x++) {
        var position2 = x * oceanamp + (millis() * oceanspeed);
        var y = map(noise(position2), 0, 10, height/2, height); 
        vertex(x, y); 
    }
    
    vertex(width/2, height);
    vertex(0, height);
    endShape(); //The new ocean after the iceberg sinks
}

function newsun() {
    fill(255, 51, 51);
    ellipse(150, 100, 60, 60); //bigger and stronger sun to appear 
}

function factory() {
    fill(100);  
    rect(350, 300, 180, 200); //factory body

    triangle(350, 300, 410, 250, 410, 300); 
    triangle(410, 300, 470, 250, 470, 300);
    triangle(470, 300, 530, 250, 530, 300); //Ceiling
  
    fill(250);
    rect(360, 330, 40, 20);
    rect(420, 330, 40, 20);
    rect(480, 330, 40, 20); //Windows
}

function fire1() {
    fill(255,0,0);
    ellipse(385, 375, 46, 46);
    triangle(361, 375, 385, 310, 409, 375)
    fill(255, 255, 0);
    ellipse(385, 375 ,30 ,30); //fire 1
}

function fire2() {

    fill(255,0,0);
    ellipse(445, 375, 46, 46);
    triangle(421, 375, 445, 310, 469, 375)
    fill(255, 255, 0);
    ellipse(445, 375, 30, 30); //fire 2
}

function fire3() {
    fill(255,0,0);
    ellipse(505,375,46,46);
    triangle(481,375,505,310,529,375);
    fill(255,255,0);
    ellipse(505,375,30,30); //fire 3
}

function mousePressed() {
    if (mouseX < 500 & mouseX > 400 && mouseY < 80 && mouseY > 30 ){
        counter = counter + 1; 
    } //Counter that keeps track of elemets to be appeared when button is clicked
}


  

 







As I initially had trouble selecting a theme for my final project, I decided to follow Professor Dannenburg’s suggestion to make a climate change themed work. As I appreciated art not only for its aesthetics but also for the message it sends to the public, I thought creating a project that sends a message about global warming could be rewarding.

I wanted to show how the actions of humans can have a detrimental impact on the livelihood of polar bears. Although it was already a common topic, I wanted to express that human beings ultimately have the power to preserve or destroy the environment that these animals were dependent on.

As such, I created a “button” that emulated real life actions that caused global warming and the destruction of the ice caps. Each click creates a fire that would burn the trees and finally create a factory. After the action sequence is completed, the player will have destroyed the ozone layer, causing the ice cap to melt and kill the polar bear.

The first stage of the game

The final sequence of the game

Xu Xu & Xiaoyu Kang – Final Project

sketch

// Xiaoyu Kang & Xu (Claire) Xu
// Section B
// xkang@andrew.cmu.edu & xux1@andrew.cmu.edu
// Final Project
var hatShapeX = 130; //BY BOTH XIAOYU AND XU
var hatShapeY = 128;
var heartShapeX = 205;
var heartShapeY = 128;
var moneyShapeX = 270;
var moneyShapeY = 140;
var textboxShapeX = 330;
var textboxShapeY = 128;
var backgroundColor = 0;
var gameStart = false;
var badEnd = false;
var goodEnd = false;
var img1 = [];
var imageCount = 0;
var testLinks = ["https://i.imgur.com/VWoGtbO.jpg", 
             "https://i.imgur.com/OpLGpha.jpg",
             "https://i.imgur.com/1Ik4bPx.jpg",
             "https://i.imgur.com/vM6ZK9c.jpg",
             "https://i.imgur.com/8J6Gxdo.jpg",
             "https://i.imgur.com/yk3tV27.jpg",
             "https://i.imgur.com/FH9qhm4.jpg",
             "https://i.imgur.com/E7a5TDu.jpg",
             "https://i.imgur.com/9vRGrKE.jpg",
             "https://i.imgur.com/C4XhQaI.jpg",
             "https://i.imgur.com/NgIAvpS.jpg",
             "https://i.imgur.com/QRXsh9S.jpg",
             "https://i.imgur.com/04EkVKf.jpg",
             "https://i.imgur.com/0dOYwZv.jpg",
             "https://i.imgur.com/xc56D6n.jpg",
             "https://i.imgur.com/0sk94O3.jpg"];
var questionCount = 0;
let testQuestions = ["I can't code for life,", " but do you want to collaborate on this 15104 project?",
                  "Did you hear about the history exam tomorrow?", "I can’t believe it’s on EVERYTHING!",
                  "I heard Vivian got a 104/100", " on the recent physics test!",
                  "I really need to get some sleep,", "will you sign me in for lecture today?",
                  "There’s a frat party tonight,", "do you want to go and hang out?",
                  "OMG I heard there’s a new pho place downtown,", "are you interested??",
                  "There’s a library part time job on Handshake", "let’s apply together!",
                  "Let’s play","League of Legends tonight!",
                  "<Introduction to Javascript> is a required textbook,", "please get it by next class.",
                  "Re: Please see the HW attached,", "submit all to autolab by tonight at midnight.",
                  "I noticed that you have not paid attention in class.", "Is there something wrong?",
                  "Class, this project", "will be due in a week’s time.",
                  "Re: Would you like to be a generous supporter", "of the CMU community by becoming a donor?",
                  "The total amount of your food will be $15.76.", "Paid in card or cash?",
                  "New: Pittsburgh popcorn & smokey barbeque", "flavored mac and cheese",
                  "It’s 2am in the mornin’,","why are y’all still workin’?"]

var imageposX = 115;
var imageposY = 80;
var questionposY = 380;
var answerCount = 0;
let answerX = ["Um… Sure?",
               "What exam?",
               "Ha, I can do better",
               "Sure, go sleep",
               "Sure, I’ll go!",
               "YES LET’S GO",
               "Sure I’ll join",
               "I'll grab my laptop",
               "I’ll buy it",
               "Grinds through HW",
               "It won’t happen again",
               "Complete the task",
               "Fine I’ll donate",
               "Takes out debit card",
               "Gives it a try",
               "We have a deadline"];
let answerY = ["I prefer someone better",
               "Ya I studied all night",
               "She must’ve worked hard",
               "I’m also not going",
               "Nah, I’ll pass",
               "No I’m broke",
               "I’m too lazy",
               "My heart is in the work",
               "I’ll find a fake one",
               "Ignores this email",
               "Your class is boring",
               "Request for extension",
               "I’m not generous",
               "Gets something cheaper",
               "Gets something normal",
               "Cries hysterically"];
var answerposX = 90;
var answerposY = 430;
var timeTracker = 0;
var choices = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15];
var choices_A = [];
var academicsHP = 50;
var sanityHP = 50;
var financeHP = 50;
var socialHP = 50;
var HPposX = 35;
var HPposY = 40;
var logoPosX = 15;
var logoPosY = 30;
var subtract = true;
var monthCount = 0;
var star = [];
var cloud = [];
var crow = [];

function preload(){ //BY XU
    for (var i = 0; i < testLinks.length; i++){
        img1[i] = loadImage(testLinks[i]);
    }
}
function setup(){ //BY XIAOYU
    createCanvas(480,480);
    frameRate(10);
    //setup stars that will display at night
    for (var i = 0; i < 50; i ++) {
        var starX = random(width);
        var starY = random (0, height/2);
        star[i] = makeStar(starX, starY);
    }
    //setup clouds that will display at morning
    for (var i = 0; i < 10; i ++) {
        var cloudX = random(width);
        var cloudY = random (0, height/2);
        cloud[i] = makeCloud(cloudX, cloudY);
    }
    //setup crows that will display at afternoon
    for (var i = 0; i < 20; i ++) {
        var crowX = random(width);
        var crowY = random (0, height/2);
        crow[i] = makeCrow(crowX, crowY);
    }
}
function draw(){ //BY XU
    //starting page text
    background(0);
    fill("yellow");
    textSize(70);
    textFont('Courier');
    textAlign(CENTER);
    text('REIGNS', width/2, height/2);
    fill("white");
    textSize(20);
    text('- CMU EDITION -', width/2, height/2 + 50);
    textSize(15);
    text('Press Mouse To Start', width/2, height - 70); 
    //starting page logo
    fill("yellow");
    rectMode(CENTER);
    rect(width/2, 130, 250, 60);
    //academics logo
    noStroke();
    fill(0);
    beginShape();
    vertex(hatShapeX, hatShapeY);
    vertex(hatShapeX + 20, hatShapeY - 5);
    vertex(hatShapeX + 40, hatShapeY);
    vertex(hatShapeX + 20, hatShapeY + 5);
    vertex(hatShapeX, hatShapeY);
    endShape();
    rectMode(CORNER);
    rect(hatShapeX, hatShapeY, 1, 10);
    rect(hatShapeX + 10, hatShapeY, 20, 10);
    //sanity logo
    circle(heartShapeX, heartShapeY, 15);
    circle(heartShapeX + 10, heartShapeY, 15);
    triangle(heartShapeX - 8, heartShapeY, heartShapeX + 18, heartShapeY, heartShapeX + 6, heartShapeY + 15);
    //finance logo
    textSize(33);
    text('S', moneyShapeX, moneyShapeY);
    rect(moneyShapeX - 1.5, moneyShapeY - 22, 3, 25);
    //social life logo
    ellipse(textboxShapeX, textboxShapeY, 30, 20);
    triangle(textboxShapeX, textboxShapeY, textboxShapeX + 10, textboxShapeY, textboxShapeX + 10, textboxShapeY + 15);
    if(gameStart){ 
        backgroundChange();
        fill(backgroundColor);
        rectMode(CENTER);
        rect(width/2, height/2, 480, 480); 
        //display clouds when background is light gray
        if (timeTracker == 0) {
            displayCloud();
        }
        //display crows when background is dark gray
        if (timeTracker == 1) {
            displayCrow();
            addCrow();
            removeCrow();
        }
        //display stars when background is black
        if (timeTracker == 2) {
            displayStars();
        }        
        gameInterface();
    }
    //check if the game has ended
    checkStatus();
    //bad end
    if (badEnd){
        gameStart = false;
        background(0);
        fill("yellow");
        textSize(70);
        textFont('Courier');
        textAlign(CENTER);
        text('GAME OVER', width/2, height/2);
        fill("white");
        textSize(20);
        text('- SADLY YOU DIDNT GRADUATE -', width/2, height/2 + 50);
        textSize(15);
        text('Refresh Page to Start Over', width/2, height - 70); 
    }
    //goodEnd
    if (goodEnd){
        gameStart = false;
        background(0);
        fill("yellow");
        textSize(50);
        textFont('Courier');
        textAlign(CENTER);
        text('CONGRATULATIONS', width/2, height/2);
        fill("white");
        textSize(20);
        text('- YOU SUCCESFULLY GRADUATED CMU -', width/2, height/2 + 50);
        textSize(15);
        text('Refresh Page to Play Again', width/2, height - 70); 
    }
}
function mouseClicked(){ //BY XU
    gameStart = true;
}
function gameInterface(){ //BY XU
    //frame
    fill("yellow");
    rectMode(CENTER);
    rect(width/2, height/2 - 35, 280, 280);
    //implement images
    putImage();
    //implement question
    putQuestion();
    //implement answers
    putAnswers();
    putDate();
    hpTracker();

}
function putImage(){ //BY XIAOYU
    image(img1[imageCount], imageposX, imageposY);
}
function putQuestion(){ //BY XIAOYU
    fill("white");
    textFont('Courier');
    textAlign(CENTER);
    textSize(12.5);
    text(testQuestions[questionCount], width/2, questionposY);
    text(testQuestions[questionCount + 1], width/2, questionposY + 15);
}
function putAnswers(){ //BY XIAOYU
    //left side
    fill("yellow");
    textFont('Courier');
    textSize(12.5);
    text("A. " + answerX[answerCount], answerposX, answerposY);
    text("B. " + answerY[answerCount], answerposX + 280, answerposY);
}
function putDate(){ //BY XIAOYU
    fill("yellow");
    textFont('Courier');
    textAlign(CENTER);
    textSize(12.5);
    text(monthCount*2 + " months survived", width/2, 460);
}
function keyTyped(){ //BY XU
    if (key === 'a'){
        //time passes
        timeTracker ++;
        imageCount = floor(random(choices));
        questionCount = imageCount *2;
        answerCount = imageCount;
        subtractHP();
        monthCount ++;
    } else if (key === 'b'){
        //time passes
        timeTracker ++;
        imageCount = floor(random(choices));
        questionCount = imageCount *2;
        answerCount = imageCount;
        subtractHP();
        monthCount ++;
    }
    if (timeTracker >= 3){
        timeTracker = 0;
    }
}
function notRepeat(){ //BY XU
    while (choices_A.includes(imageCount)){
        imageCount = floor(random(choices));
    }
    if(choices_A.length < 6){
        choices_A.push(imageCount);
    }else {
        choices_A.shift();
        choices_A.push(imageCount);
    }
}
function hpTracker(){ //BY XIAOYU
    //academics logo
    noStroke();
    fill("yellow");
    beginShape();
    vertex(logoPosX, logoPosY);
    vertex(logoPosX + 20, logoPosY - 5);
    vertex(logoPosX + 40, logoPosY);
    vertex(logoPosX + 20, logoPosY + 5);
    vertex(logoPosX, logoPosY);
    endShape();
    rectMode(CORNER);
    rect(logoPosX, logoPosY, 1, 10);
    rect(logoPosX + 10, logoPosY, 20, 10);
    //academics hp
    fill("white");
    textFont('Courier');
    textAlign(CENTER);
    textSize(12.5);
    text(academicsHP + "/100", HPposX + 50, HPposY);
    //sanity logo
    fill("yellow");
    circle(logoPosX + 130, logoPosY, 15);
    circle(logoPosX + 130+ 10, logoPosY, 15);
    triangle(logoPosX + 130 - 8, logoPosY, logoPosX + 130 + 18, logoPosY, logoPosX + 130 + 6, logoPosY + 15);
    //sanity hp
    fill("white");
    text(sanityHP + "/100", HPposX + 130 + 30, HPposY);
    //finance logo
    fill("yellow");
    textSize(33);
    text('S', logoPosX + 250, logoPosY + 10);
    rect(logoPosX - 1.5 + 250, logoPosY + 10 - 22, 3, 25);
    //finance hp
    fill("white");
    textSize(12.5);
    text(financeHP + "/100", HPposX + 250 + 25, HPposY);
    //social life logo
    fill("yellow");
    ellipse(logoPosX + 370, logoPosY, 30, 20);
    triangle(logoPosX + 370, logoPosY, logoPosX + 370 + 10, logoPosY, logoPosX + 370 + 10, logoPosY + 15);
    //social hp
    fill("white");
    text(socialHP + "/100", HPposX + 400, HPposY);
}
function subtractHP(){ //BY XU
    //subtract hp
    if (imageCount <= 3){
        var randomNum = random(0, 1);
        if (randomNum < 0.5){
            academicsHP -= 10;
            sanityHP += 10;
        }else{
            academicsHP += 10;
            sanityHP -= 10;     
        }
    }
    if (imageCount >= 4 || imageCount <= 7){
        var randomNum = random(0, 1);
        if (randomNum < 0.5){
            financeHP -= 10;
            socialHP += 10;
        }else{
            financeHP += 10;
            socialHP -= 10;     
        }
    }
    if (imageCount >= 8 || imageCount <= 11){
        var randomNum = random(0, 1);
        if (randomNum < 0.5){
                academicsHP += 10;
            sanityHP -= 10;
        }else{
            academicsHP -= 10;
            sanityHP += 10;     
            }
    }
    if (imageCount >= 12 || imageCount <= 15){
        var randomNum = random(0, 1);
            if (randomNum < 0.5){
            financeHP += 10;
            socialHP -= 10;
        }else{
            financeHP -= 10;
            socialHP += 10;     
        }
    }
}
function checkStatus(){ //BY XU
    if (academicsHP == 100 || sanityHP == 100 || financeHP == 100 || socialHP == 100){
        badEnd = true;
    }
    if (academicsHP == 0 || sanityHP == 0 || financeHP == 0 || socialHP == 0){
        badEnd = true;
    }
    if (monthCount >= 24){
        goodEnd = true;
    }
}
function backgroundChange(){ //BY XIAOYU
    if (timeTracker == 0) {
        backgroundColor = 130;
    }
    if (timeTracker == 1) {
        backgroundColor = 95;
    }
    if (timeTracker == 2) {
        backgroundColor = 0;
    }
}
function drawStar(){ //BY XIAOYU
    noStroke();
    fill("yellow");
    push();
    translate(this.x, this.y);
    ellipse(20, 20, random(1,5), random(1,5));
    pop();
}
function makeStar(starX, starY){ //BY XIAOYU
    var makeStar = {x: starX,
                y: starY,
                draw: drawStar}
    return makeStar;
}
function displayStars(){ //BY XIAOYU
    for (i = 0; i < star.length; i++){
        star[i].draw();
    }
}
function drawCloud(){ //BY XU
    noStroke();
    fill(185);
    push();
    translate(this.x2, this.y2);
    ellipse(15, 26, 34, 20);
    ellipse(45, 30, 22, 25);
    ellipse(15, 35, 15, 16);
    ellipse(30, 28, 28, 26);
    pop();
}
function makeCloud(cloudX, cloudY){ //BY XU
    var makeCloud = {x2: cloudX,
                y2: cloudY,
                speed: -6,
                move: moveCloud,
                draw: drawCloud}
    return makeCloud;
}
function moveCloud(){ //BY XU
    this.x2 += this.speed;
    if(this.x2 <= -10){
        this.x2 += width;
    }
}
function displayCloud(){ //BY XU
    for (i = 0; i < cloud.length; i++){
        cloud[i].move();
        cloud[i].draw();
    }
}
function drawCrow(){ //BY XIAOYU
    strokeWeight(1);
    stroke(0);
    noFill();
    arc(this.x3, this.y3, this.size, this.size/2, PI, TWO_PI);
    arc(this.x3 + this.size, this.y3, this.size, this.size/2, PI, TWO_PI);
}
function makeCrow(crowX, crowY){ //BY XIAOYU
    var makeCrow = {x3: crowX,
                y3: crowY,
                speed2: random(3, 10),
                size: random(5, 10),
                move2: moveCrow,
                draw2: drawCrow}
    return makeCrow;
}
function moveCrow(){ //BY XIAOYU
    this.x3 -= this.speed2;
    this.y3 -= this.speed2 / random(5, 10);
}
function displayCrow(){ //BY XIAOYU
    for (i = 0; i < crow.length; i++){
        crow[i].move2();
        crow[i].draw2();
    }
}
function addCrow(){ //BY XU
    if (random(0,1) < 0.1) {
        var crowX = width;
        var crowY = random(0, height/2);
        crow.push(makeCrow(crowX, crowY));
    }
}
function removeCrow() { //BY XU
    var keep = [];
    for (i = 0; i < crow.length; i++) {
        if (crow[i].x3 + crow[i].size > 0) {
            keep.push(crow[i]);
        }
    }
    crow = keep;
}

For this final project, Xiaoyu and I collaborated to use p5js to create a CMU version of Reigns (a phone game). The game starts by pressing the mouse, then a series of questions will be presented, and the player uses ‘a’ key and ‘b’ key to make choices. There are four HP counters at the top of the screen, and they will fluctuate based on the choices the player makes. Once any of the four HPs reach 0/100 or 100/100, the game will present the bad end. If the player survives until 48 months (4 years), the game presents the good end.

We used things we learned in the past semester to give the game some particular features, such as the background changes, making sure the characters don’t repeat themselves, etc. We drew the characters ourselves in illustrator, and created the gamestart/end pages with p5js.

Ankitha Vasudev & Mari Kubota – Final Project


Instructions:

Up- w key

Down- s key

Left- a key

Right- d key

WARNING: DO NOT TOUCH THE FIRE

The objective of the game is to avoid the wild fires and reach the plant at the lower right hand corner of the screen. If you reach the plant, the words “You Win” appear along with the button that leads you to the next level. If you touch the fire, the words “You Lose” appear along with a restart button. The game has multiple levels which get harder as you go due to the increase in the number of fires and the heightened speed of the character. The level resets without an increase in difficulty if you touch the fire. The game is meant to bring awareness to forest fires caused by climate change.

Note: click on the screen to start the game 

sketch

// Ankitha Vasudev and Mari Kubota
// 15-104 Final Project

// ghost position
var dx = 0; 
var dy = 0;
var posX;
var posY;

//ghost speed
var speed = 1;

//sapling postion
var xsap = 375;
var ysap = 370;

//fire 
var shapes = [];

//level number
var lvl = 1;

var gameOn = true;
var lose = false;

function setup() {
	createCanvas(400, 400);
    //Makes fires and puts into array
	for (var i = 0; i < 15; i++) { 
        shapes.push(makeShapes());
    } 
}

function draw() { //written together 
	background(178, 204, 163);

	//calls functions
	sapling();
	ghost();
	pressingKey();
	levelNumber();
	
	for (i = 0; i < shapes.length; i++) { 
        shapes[i].display();

        //Game Over route
		if (posX >= shapes[i].x & posX <= shapes[i].x+30 
		    && posY >= shapes[i].y-25 && posY <= shapes[i].y+20) {
		 	lose = true;
		    speed = 0;
		    resetButton();
		    gameOn = false;
	    }
	}
 		
        //You Win route
    if (posX >= xsap-10 & posY >= ysap) { 
    	noStroke();
    	fill(0);
		textSize(50);
		// winsound.play();
		text('You Win!', 100, 200);
		speed = 0;
		nextLvl();
		gameOn = false;
    }

    if (lose==true){
    	noStroke();
		fill(0);
		textSize(50);
    	text('You Lose', 100, 200);
    }
}

function pressingKey() { //by Ankitha
    //Ghost movements controlled by keys

	if(keyIsDown(87)) { //W key
		dy -= speed;
	}	
    
	if(keyIsDown(83)) { //S key
		dy += speed;
	}	

	if(keyIsDown(65)){ //A key
		dx -= speed;
	}	

	if(keyIsDown(68)){ //D key
		dx+=speed;
	}
}

function ghost() { //by Mari
    //Ghost character shape and constraints
	noStroke();
	fill(255);
	posX = 25+dx;
	posY = 17+dy;

	if (posX <= 7){
		dx += speed;
	}

	if (posX >= width - 7){

		dx -= speed;
	}

	if (posY <= 9){
		dy += speed;
	}

	if (posY >= height-14){
		dy -= speed;
	}

	ellipse(25+dx,17+dy,17,20);//head
	ellipse(25+dx,27+dy,5,10);//feet
	ellipse(20+dx,27+dy,5,10);//feet
	ellipse(30+dx,27+dy,5,10);//feet
	fill(0);
	ellipse(20+dx,19+dy,3,3);//left eye
	ellipse(30+dx,19+dy,3,3);//right eye
	strokeWeight(1);
	stroke(0);
	line(22+dx,23+dy,28+dx,23+dy);
}

function shapesDisplay() { //by Ankitha
    //Make fire shape using vertex
    strokeWeight(3);
	stroke("red");
    fill("orange");
    beginShape();
    vertex(this.x-5, this.y);
    vertex(this.x, this.y+20);
    vertex(this.x+20, this.y+20);
    vertex(this.x+30, this.y+20);
    vertex(this.x+40, this.y+10);
    vertex(this.x+30, this.y-20);
    vertex(this.x+25, this.y+5);
    vertex(this.x+22, this.y-20);
    vertex(this.x+13, this.y);
    vertex(this.x+12, this.y-20);
    vertex(this.x-5, this.y);
    endShape();
}

function makeShapes() { //by Ankitha
    //randomizes fire position 
    var sh = {x: random(-30,430), 
    	      y: random(50,340),
              display: shapesDisplay}
    return sh;
}

function resetButton() { //by Mari
    //Reset button when game over appears
	stroke(0);
	strokeWeight(2);
	fill("red");
	rectMode(CORNER);
	rect(115,360,75,30);
	fill(0);
	textSize(12);
	noStroke();
	text("RESTART",128,379);
}

function nextLvl() { //by Mari
    //next level button when you win
	rectMode(CORNER);
	strokeWeight(2);
	stroke(0);
	fill("green");
	rect(30,360,75,30);
	fill(0);
	textSize(10);
	noStroke();
	text("Next Level",45,378);
}

function sapling() { //by Mari
    //sapling (the goal)
	fill("green");
	strokeWeight(5);
	stroke("green")
	line(xsap,ysap,xsap,ysap+20); //stem
	noStroke();
	ellipse(xsap-10,ysap,20,10); //left leaf
	ellipse(xsap+10,ysap,20,10); //right leaf
	fill("yellow");
	ellipse(xsap,ysap+20,10,10);
}

function levelNumber() { //by Mari
	noStroke();
	fill(0);
    textSize(15);
    text("Level: " + lvl, 340, 20);
}

function mouseClicked() { //by Ankitha
    //mouse clicking on button resets the games
    //game over button LOSE
	if (mouseX<245 & mouseX>130 && mouseY>360 && mouseY<400 && gameOn==false) { 
		shapes=[];
		gameOn=true;
		lvl = 1;
		speed=lvl;
		makeShapes();
		for (var i = 0; i < 15; i++) {
        	shapes.push(makeShapes());
    	} 
    	dx=0;
    	dy=0;
    	lose = false;
	}
    //next level button WIN
	if (mouseX<80 & mouseX>30 && mouseY>360 && mouseY<400 && gameOn==false) { 
		gameOn=true;
		lvl++;
		speed=lvl;
		makeShapes();
		for (var i = 0; i < 15; i++) {
        	shapes.push(makeShapes());
    	} 
    	dx=0;
    	dy=0;
	}
}