Final Project – Survive 2020 Game

Our project is inspired by the game Flappy Bird. In our rendition, 2020 events are the obstacles on the pipes and the goal is to “survive” 2020. The character moves up when you hit the letter ‘m’, the game ends if you hit one of the pipes, hit the ground or if you win by passing 12 pairs of pipes. If you lose by hitting a pipe or win you can play the game again, but if you hit the ground you lose and cannot restart the game. The two sounds you hear are the bounce when you move the character and the clash when the character hits one of the pipes. Based on which pipe you hit, you see a different “game over” message. We chose a darker color scheme and used a fire in the background to match the mood of 2020. If we had more time with this project, we would add a webcam function so that the character would be the user’s face instead of just a smiley face. Overall, this project was definitely challenging but also really fun to create.

final project
/* 
 * Final Project 
 * Rishi Karthikeyan and Amy Lee 
 * rkarthik and amyl2
 * Section B 
 */ 

// Run a local server to hear sounds used 
// Press the letter m to control character
// The game ends if the character hits the ground or the pipes 
// If you lose by hitting a pipe or win you can play the game again 
// but if you hit the ground you lose and cannot restart the game

var score = 0;
var count = 0;
var faceX = 50; 
var faceY = 200; 
var hitPipe = false; 
var endGame = false; 
var gameOn = true;
var gameStart = true;
var characterOn = true;
var pipes = []; 
var fire = [];
var pipeImages = []; // an array to store the images 
var factImages = []; // an array to store the dates between the pipes 
var gameOverImages = []; // an array to store the customized game over images 

var timeline1 = ["1/1/20", "1/16/20", "1/19/20", "2/2/20", "3/11/20", 
    "3/19/20", "3/20/20", "4/1/20", "5/1/20", "5/26/20", "11/6/20", "11/7/20"]

var timeline2=["Australian Bush Fire","Trump Impeached","COVID-19","Superbowl",
    "Parasite Wins Oscars","Zoom University","Tiger King","Royal Family Split",
    "Murder Hornets", "BLM Movement", "Election", "First Female VP Elect"]

function preload() {
    // Circular images that go in the pipes 
    var filenames = []; 
    filenames[0] = "https://i.imgur.com/tP1n00I.png"; // Fire 
    filenames[1] = "https://i.imgur.com/sY9uWvm.png"; // Trump 
    filenames[2] = "https://i.imgur.com/lCBeEEt.png"; // Covid 
    filenames[3] = "https://i.imgur.com/3Rckn2m.png"; // Superbowl 
    filenames[4] = "https://i.imgur.com/ul9jOdN.png"; // Oscar 
    filenames[5] = "https://i.imgur.com/iWRAmux.png"; // Zoom 
    filenames[6] = "https://i.imgur.com/dbYhauj.png"; // Tiger King
    filenames[7] = "https://i.imgur.com/lVbGhGa.png"; // Meg and Harry 
    filenames[8] = "https://i.imgur.com/U1PoDE5.png"; // Hornets 
    filenames[9] = "https://i.imgur.com/nqwizdc.png"; // BLM
    filenames[10] = "https://i.imgur.com/xtEZvMR.png"; // Election 
    filenames[11] = "https://i.imgur.com/1lZjhEQ.png"; // Kamala

    for (var i = 0; i < filenames.length; i ++) {
        pipeImages.push(loadImage(filenames[i])); 
    }

    // Displays game over message based on which pipe the character dies on
    var filenames2 = []; 
    filenames2[0] = "https://i.imgur.com/ibfDcOO.png"; // Fire  
    filenames2[1] = "https://i.imgur.com/vFpcofS.png"; // Trump 
    filenames2[2] = "https://i.imgur.com/NMyD9W2.png"; // Covid 
    filenames2[3] = "https://i.imgur.com/aU3iUST.png"; // Superbowl 
    filenames2[4] = "https://i.imgur.com/hqLtxr4.png"; // Oscar 
    filenames2[5] = "https://i.imgur.com/59MSAMG.png"; // Zoom 
    filenames2[6] = "https://i.imgur.com/ArzVHYU.png"; // Tiger King
    filenames2[7] = "https://i.imgur.com/up0miQL.png"; // Meg and Harry 
    filenames2[8] = "https://i.imgur.com/pSRx5bC.png"; // Hornets 
    filenames2[9] = "https://i.imgur.com/EzfHYCr.png"; // BLM
    filenames2[10] = "https://i.imgur.com/wJvHY17.png"; // Election 
    filenames2[11] = "https://i.imgur.com/929AM73.png"; // Kamala

    for (var i = 0; i < filenames2.length; i ++) {
        gameOverImages.push(loadImage(filenames2[i])); 
    }

    // Load sounds for when the character moves or hits the pipes 
    characterSound = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/12/bounce.wav");
    clashSound = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/12/clash.wav"); 
}

function setup() {
    createCanvas(450, 400);
    background(12, 22, 24);
    imageMode(CENTER); 
    useSound();

    if (gameOn == true) {
        fire.push(makeFire());
        pipes.push(makePipes());
    } 
}

function soundSetup() { 
    // Set the volume for the sound effects 
    characterSound.setVolume(0.01);
    clashSound.setVolume(0.05); 
}

function draw() {
    background(12, 22, 24);
    gradientSky();

    if (gameOn == true) {
        // Start the count once the game is on 
        count++
        // Display & add fire
        updateAndDisplayFire(); 
        if (count % 50 == 10){
            fire.push(makeFire()); 
        }
        updateAndDisplayPipes();
        // Add new pipes 
        if (count % 120 == 0){
            if (gameOn == true) {
            }

            if (pipes.length < 12) {
                pipes.push(makePipes()); 
            }

            if (count > 300) {
                score += 150;
            }
        }
    }

    // Score tracker  
    textSize(15);
    fill(72,160,141);
    noStroke();
    text('SCORE ' + score, 50, 23);

    drawFace(); 

    if (count >= 1690){ 
        gameOn = false; 
    } 

    if (gameOn == false) {
        gameWon();
    }

    if (hitPipe == true){
        gameOver(round((count/120) - ((120*3)/120))); 
    }
    
}

function gradientSky() {
    // Background color gradient 
    var darkCol = color(0,12,15); 
    var lightCol = color(14,38,39); 
    for (var y = 0; y < height; y ++){
        var backG = map(y, 0, height, 0, 1); 
        var backGColor = lerpColor(darkCol, lightCol, backG); 
        stroke(backGColor); 
        line(0, y, width, y); 
    }

    // Fire Embers 
    for (var i = 0; i < 35; i++) {
        strokeWeight(4);
        if (round(random(1)) == 0 ) {
            stroke(148, 67, 43);
        } else {
            stroke(81, 33, 22);
        }
        point(random(0,width), random(-0, height));
    }
}

// *************** FIRES *************** //
function updateAndDisplayFire(){
    // Update the fire's positions, and display them.
    for (var i = 0; i < fire.length; i++){
        fire[i].move();
        fire[i].draw();
    }
}

// Method to update position of fire every frame
function fireMove() {
    this.x += this.speed;
}

// Draw the fire 
function fireDraw() {
    push();
    translate(0, height - 220);
    scale(1.2, 1.75);
    noStroke();
    fill(81, 33, 22);   //red fire
        beginShape();
        vertex(this.x, this.y + 125);
        vertex(this.x, this.y  + 115);
        vertex(this.x + 5, this.y  + 115);
        curveVertex(this.x + 4, this.y  + 113);
        curveVertex(this.x + 3, this.y  + 112);
        curveVertex(this.x + 3, this.y  + 108);
        curveVertex(this.x + 5, this.y  + 102);
        curveVertex(this.x + 9, this.y  + 98);
        vertex(this.x + 18 + random(-1, 1), this.y + 93+ random(-1, 1));

        curveVertex(this.x + 17, this.y  + 110);
        curveVertex(this.x + 21, this.y  + 112);
        curveVertex(this.x + 31, this.y  + 109);
        curveVertex(this.x + 31, this.y  + 102);
        curveVertex(this.x + 25, this.y  + 95);
        curveVertex(this.x + 24, this.y  + 89);
        curveVertex(this.x + 27, this.y  + 83);
        curveVertex(this.x + 34, this.y  + 77);
        curveVertex(this.x + 40, this.y  + 70);
        curveVertex(this.x + 34, this.y  + 56);
        vertex(this.x + 30 + random(-1, 1), this.y  + 53 + random(-1, 1));

        curveVertex(this.x + 40, this.y  + 56);
        curveVertex(this.x + 50, this.y  + 64);
        curveVertex(this.x + 48, this.y  + 75);
        curveVertex(this.x + 41, this.y  + 83);
        curveVertex(this.x + 38, this.y  + 90);
        curveVertex(this.x + 40, this.y  + 98);
        curveVertex(this.x + 49, this.y  + 99);
        curveVertex(this.x + 52, this.y  + 95);
        curveVertex(this.x + 48, this.y  + 90);
        curveVertex(this.x + 48, this.y  + 84);
        vertex(this.x + 55 + random(-1, 1), this.y  + 75 + random(-1, 1));

        curveVertex(this.x + 54, this.y  + 81);
        curveVertex(this.x + 56, this.y  + 89);
        curveVertex(this.x + 60, this.y  + 97);
        curveVertex(this.x + 56, this.y  + 104);
        curveVertex(this.x + 62, this.y  + 117);
        curveVertex(this.x + 72, this.y  + 118);
        curveVertex(this.x + 80, this.y  + 112);
        curveVertex(this.x + 75, this.y  + 99);
        curveVertex(this.x + 68, this.y  + 92);
        curveVertex(this.x + 63, this.y  + 83);
        curveVertex(this.x + 70, this.y  + 63);
        curveVertex(this.x + 84, this.y  + 54);
        vertex(this.x + 94 + random(-1, 1), this.y  + 50 + random(-1, 1));

        curveVertex(this.x + 85, this.y  + 56);
        curveVertex(this.x + 77, this.y  + 65);
        curveVertex(this.x + 73, this.y  + 73);
        curveVertex(this.x + 77, this.y  + 77);
        curveVertex(this.x + 87, this.y  + 75);
        vertex(this.x + 89 + random(-1, 1), this.y  + 63 + random(-1, 1));

        curveVertex(this.x + 93, this.y  + 68);
        curveVertex(this.x + 93, this.y  + 78);
        curveVertex(this.x + 87, this.y  + 86);
        curveVertex(this.x + 81, this.y  + 93);
        curveVertex(this.x + 87, this.y  + 99);
        curveVertex(this.x + 92, this.y  + 99);
        curveVertex(this.x + 97, this.y  + 92);
        curveVertex(this.x + 94, this.y  + 87);
        curveVertex(this.x + 94, this.y  + 84);
        vertex(this.x + 98 + random(-1, 1), this.y  + 77 + random(-1, 1));

        curveVertex(this.x + 101, this.y  + 85);
        curveVertex(this.x + 104, this.y  + 89);
        curveVertex(this.x + 107, this.y  + 97);
        curveVertex(this.x + 104, this.y  + 104);
        curveVertex(this.x + 101, this.y  + 110);
        curveVertex(this.x + 109, this.y  + 115);

        vertex(this.x + 113, this.y  + 115);
        vertex(this.x + 113, this.y  + 128);
        endShape(CLOSE);
    pop();

    push();
    translate(0, height - 125);
    scale(1, 1);
    noStroke();
    fill(148, 67, 43);  //orange fire   
        beginShape();
        vertex(this.x, this.y + 125);
        vertex(this.x, this.y  + 115);
        vertex(this.x + 5, this.y  + 115);
        curveVertex(this.x + 4, this.y  + 113);
        curveVertex(this.x + 3, this.y  + 112);
        curveVertex(this.x + 3, this.y  + 108);
        curveVertex(this.x + 5, this.y  + 102);
        curveVertex(this.x + 9, this.y  + 98);
        vertex(this.x + 18 + random(-1, 1), this.y + 93+ random(-1, 1));

        curveVertex(this.x + 17, this.y  + 110);
        curveVertex(this.x + 21, this.y  + 112);
        curveVertex(this.x + 31, this.y  + 109);
        curveVertex(this.x + 31, this.y  + 102);
        curveVertex(this.x + 25, this.y  + 95);
        curveVertex(this.x + 24, this.y  + 89);
        curveVertex(this.x + 27, this.y  + 83);
        curveVertex(this.x + 34, this.y  + 77);
        curveVertex(this.x + 40, this.y  + 70);
        curveVertex(this.x + 34, this.y  + 56);
        vertex(this.x + 30 + random(-1, 1), this.y  + 53 + random(-1, 1));

        curveVertex(this.x + 40, this.y  + 56);
        curveVertex(this.x + 50, this.y  + 64);
        curveVertex(this.x + 48, this.y  + 75);
        curveVertex(this.x + 41, this.y  + 83);
        curveVertex(this.x + 38, this.y  + 90);
        curveVertex(this.x + 40, this.y  + 98);
        curveVertex(this.x + 49, this.y  + 99);
        curveVertex(this.x + 52, this.y  + 95);
        curveVertex(this.x + 48, this.y  + 90);
        curveVertex(this.x + 48, this.y  + 84);
        vertex(this.x + 55 + random(-1, 1), this.y  + 75 + random(-1, 1));

        curveVertex(this.x + 54, this.y  + 81);
        curveVertex(this.x + 56, this.y  + 89);
        curveVertex(this.x + 60, this.y  + 97);
        curveVertex(this.x + 56, this.y  + 104);
        curveVertex(this.x + 62, this.y  + 117);
        curveVertex(this.x + 72, this.y  + 118);
        curveVertex(this.x + 80, this.y  + 112);
        curveVertex(this.x + 75, this.y  + 99);
        curveVertex(this.x + 68, this.y  + 92);
        curveVertex(this.x + 63, this.y  + 83);
        curveVertex(this.x + 70, this.y  + 63);
        curveVertex(this.x + 84, this.y  + 54);
        vertex(this.x + 94 + random(-1, 1), this.y  + 50 + random(-1, 1));

        curveVertex(this.x + 85, this.y  + 56);
        curveVertex(this.x + 77, this.y  + 65);
        curveVertex(this.x + 73, this.y  + 73);
        curveVertex(this.x + 77, this.y  + 77);
        curveVertex(this.x + 87, this.y  + 75);
        vertex(this.x + 89 + random(-1, 1), this.y  + 63 + random(-1, 1));

        curveVertex(this.x + 93, this.y  + 68);
        curveVertex(this.x + 93, this.y  + 78);
        curveVertex(this.x + 87, this.y  + 86);
        curveVertex(this.x + 81, this.y  + 93);
        curveVertex(this.x + 87, this.y  + 99);
        curveVertex(this.x + 92, this.y  + 99);
        curveVertex(this.x + 97, this.y  + 92);
        curveVertex(this.x + 94, this.y  + 87);
        curveVertex(this.x + 94, this.y  + 84);
        vertex(this.x + 98 + random(-1, 1), this.y  + 77 + random(-1, 1));

        curveVertex(this.x + 101, this.y  + 85);
        curveVertex(this.x + 104, this.y  + 89);
        curveVertex(this.x + 107, this.y  + 97);
        curveVertex(this.x + 104, this.y  + 104);
        curveVertex(this.x + 101, this.y  + 110);
        curveVertex(this.x + 109, this.y  + 115);

        vertex(this.x + 113, this.y  + 115);
        vertex(this.x + 113, this.y  + 128);
        endShape(CLOSE);
    pop();
}

function makeFire() {
    var fire = {x: width,
                y: 0,
                speed: -2,
                move: fireMove,
                draw: fireDraw}
    return fire;
}

// *************** CHARACTER *************** //
function drawFace() {
    if (characterOn == true) { 
        push(); 
        // Head 
        noStroke();
        fill(72,160,141); 
        ellipse(faceX, faceY, 30, 30); 
        // Eyes 
        fill(12, 22, 24); 
        ellipse(faceX - 9, faceY - 1, 3.5, 3.5); 
        ellipse(faceX + 9, faceY - 1, 3.5, 3.5); 
        // Mouth 
        noFill(); 
        stroke(12, 22, 24); 
        strokeWeight(1); 
        arc(faceX, faceY, 5, 5, 0, PI, OPEN);
        pop(); 
        faceY += 1; 

        // Keep face from going off of the canvas
        if (faceY < 15){
            faceY += 5;
        }
        // Game over if you hit the bottom of the canvas
        if (faceY > height - 15){
           hitBottomGameOver();
        }
    }
}

// *************** PIPES *************** //
function updateAndDisplayPipes(){

    // Update the pipe's positions, and display them
    for (var i = 0; i < pipes.length; i++) {
        pipes[i].move();

        // Game over if face hits the pipes 
        if (((faceY < pipes[i].pipeHeight + 30) & (faceX > pipes[i].x && 
            faceX < pipes[i].x + pipes[i].pipeWidth)) || ((faceY > 400 - 
            ((400 - (pipes[i].pipeHeight + (200 - pipes[i].pipeHeight) + 
            (122 - (200 - pipes[i].pipeHeight)))) + 55)) && 
            (faceX > pipes[i].x && faceX < pipes[i].x + pipes[i].pipeWidth))){

            hitPipe = true; 
            clashSound.play();
        }

        if (gameStart == true) {
            pipes[i].draw();
        }

        // Display image on pipe 
        pipes[i].imageNumber = i;
        if ( pipes[i].imageNumber > 11) {
            pipes[i].imageNumber = 0;
        }

        // Display timeline date in between pipes 
        pipes[i].timelineNumber = i; 
        if ( pipes[i].timelineNumber > 11) {
            pipes[i].timelineNumber = 0;
        }
    } 
}

// Method to update position of pipe every frame
function pipesMove() {
    this.x += this.speed;
}
    
// Draw the pipe 
function pipesDraw() {
    var pipe1Height = this.pipeHeight - 30; 
    var pipe2Height = 400 - (this.pipeHeight + (200 - this.pipeHeight) + 
                    (126 - (200 - this.pipeHeight)) ); 

    fill(34,79,82); 
    noStroke(); 

    // Top Pipe 
    rect(this.x, -30, this.pipeWidth, this.pipeHeight); 
    ellipse(this.x + 40, pipe1Height, this.pipeWidth, this.pipeWidth); 
    image(pipeImages[this.imageNumber], this.x + 40, pipe1Height, 60, 60); 

    // Fact in middle of pipe 
    textSize(15);
    textAlign(CENTER); 
    fill(148, 67, 43);
    text(timeline1[this.timelineNumber], this.x + 40, (pipe1Height + 
        ((400 - ((pipe1Height) + (pipe2Height)))/2)) - 2);
    text(timeline2[this.timelineNumber], this.x + 40, (pipe1Height + 
        ((400 - ((pipe1Height) + (pipe2Height)))/2)) + 15);

    // Bottom Pipe 
    push(); 
    fill(34,79,82); 
    translate(0,400); 
    rect(this.x, -pipe2Height, this.pipeWidth, pipe2Height); 
    ellipse(this.x + 40, -pipe2Height, this.pipeWidth, this.pipeWidth);
    image(pipeImages[this.imageNumber], this.x + 40, -pipe2Height, 60, 60);     
    pop(); 
}

function makePipes() {
    var pipe = {x: 650,
                pipeWidth: 80, 
                pipeHeight: random(130, 200),
                speed: -2, 
                move: pipesMove,
                draw: pipesDraw,
                imageNumber: 0,
                timelineNumber: 0
                }
    return pipe;
}

function keyPressed() {
    if (key == 'm'){ 
        faceY -= 20; 

        if (characterOn == true) {
            characterSound.play();
        }
    }
}

// Function that is run when character hits the bottom of the canvas 
function hitBottomGameOver() {
    push();
    gradientSky();

    noStroke();
    fill(72,160,141);
    rectMode(CENTER);
    rect(width/2, height/2, 227, 55);
    textAlign(CENTER);
    textSize(20);
    fill(148, 67, 43);
    text(' Y O U  L O S T  2 0 2 0 ! ', width/2, height/2);

    characterOn = false;
    noLoop();
    pop();
}

// Function that is run when the character hits the pipe 
function gameOver(pipeNumber){
            // Display the gradient sky
            gradientSky();

            // Display game over image depending on which pipe was hit 
            image(gameOverImages[pipeNumber], width/2, height/2, 250, 250); 

            // Display play again button 
            fill(148, 67, 43);
            stroke(180, 67, 43);
            strokeWeight(2);
            push();
            rectMode(CENTER);
            rect(width/2, height/2 + 160, 110, 35);
            pop();
            textSize(15);
            noStroke();
            fill(255);
            text('PLAY AGAIN', width/2, height/2 + 165);

            gameStart = false;
            characterOn = false;
            hitPipe = true; 
            gameOn = false;
}

// Function that is run when the character wins the entire game 
function gameWon() {
    push();

    // "YOU WON 2020" sign in center of screen 
    noStroke();
    fill(72,160,141);
    rectMode(CENTER);
    rect(width/2, height/2, 225, 55);
    textAlign(CENTER);
    textSize(20);
    fill(148, 67, 43);
    text(' Y O U  W O N  2 0 2 0 ! ', width/2, height/2);

    // Play again button
    fill(148, 67, 43);
    stroke(180, 67, 43);
    strokeWeight(2);
    rect(width/2, height/2 + 55, 110, 35);
    textSize(15);
    noStroke();
    fill(255);
    text('PLAY AGAIN', width/2, height/2 + 55);

    characterOn = false;
    pop();
}

function mousePressed() {
    // Press to play again after a user wins the game 
    if (count >= 1689 & gameOn == false) {
        if (mouseX > width/2 - 55 && mouseX < width/2 + 55 && 
            mouseY < height/2 + 72.5 && mouseY > height/2 + 37.5) {
                gameOn = true;
                gameStart = true;
                characterOn = true;
                endGame = false; 
                count = -50;
                score = 0; 
                pipes = []; 
                fire = [];
        }
    }

    // Press to play again when user lost the game by hitting a pipe 
    if (hitPipe == true) {
        if (mouseX > width/2 - 55 & mouseX < width/2 + 55 && 
            mouseY < height/2 + 177.5 && mouseY > height/2 + 142.5) {
                hitPipe = false; 
                endGame = false; 
                gameStart = true;
                gameOn = true; 
                characterOn = true;
                count = -50;
                score = 0; 
                pipes = []; 
                fire = [];
        }
    }
}





original proposal mockup

***on our Autolab submission we use the up arrow to control the character but here we used the letter m because with the up arrow it would move the whole page up and not just the canvas***

Project-11 Landscape

landscape
var highMountain = [];
var lowMountain = [];
var cabin = [];
var trees = [];
var dogImage = [];
var dog = [];
var noiseParam = 0;
var noiseStep = 0.01;

function preload(){
    var filenames = [];
    filenames[0] = "https://i.imgur.com/KGHR8lv.png";
    filenames[1] = "https://i.imgur.com/KGHR8lv.png";
    filenames[2] = "https://i.imgur.com/unhcm3R.png";
    filenames[3] = "https://i.imgur.com/unhcm3R.png";
    filenames[4] = "https://i.imgur.com/J2pZbow.png";
    filenames[5] = "https://i.imgur.com/J2pZbow.png";
    filenames[6] = "https://i.imgur.com/unhcm3R.png";
    filenames[7] = "https://i.imgur.com/unhcm3R.png";
 
    for (var i = 0; i<filenames.length; i++) {
        dogImage[i] = loadImage(filenames[i]);
    }
}

function setup() {
    createCanvas(480, 240); 
    frameRate(24);
    imageMode(CENTER);
    //mountains
    for (var i = 0; i <= width/4; i++) {
      var value = map(noise(noiseParam), 0, 1, 0, height*1.1);
      highMountain.push(value);
      noiseParam += noiseStep;
    }
    for (var i = 0; i <= width/5; i++) {
      var value = map(noise(noiseParam), 0, 1, 0, height*1.9);
      lowMountain.push(value);
      noiseParam += noiseStep;
    }

    // create an initial collection of cabins 
    for (var i = 0; i < 10; i++){
        var rx = random(width);
        cabin[i] = makeCabin(rx);
        var r = random(width);
        trees[i] = makeTree(r);
    }

    //create three walking characters 
    var d = makeCharacter(400, 200);
    dog.push(d);
    
}

function draw() {
    background(200); 
    //sky as a gradient
    var gradPurple = color(134, 123, 198);
    var gradPink = color(240, 198, 209);
    for (var i = 0; i < height; i++){
      var gradient = map (i, 0, (height/3)*1.5, 0, 1);
      var bgColor = lerpColor(gradPurple, gradPink, gradient);
      stroke(bgColor);
      line(0, i, width, i);
    }
    //snow
    stroke(239, 241, 253);
    strokeWeight(2);
    for (var i = 0; i < 750; i++) {
      point(random(0,width), random(-0, height));
    }

    //sun 
    noStroke();
    fill(242, 226, 233, 90);
    circle(width/2, height/2-40, 60);
    fill(242, 226, 233, 75);
    circle(width/2, height/2-40, 50);
    fill(242, 226, 233, 60);
    circle(width/2, height/2-40, 40);
    fill(242, 226, 233);
    circle(width/2, height/2-40, 30);

    //clouds 
    cloud(45, 80);
    cloud(180, 130);
    cloud(360, 160);
    cloud(445, 70);

    //mountains
    drawHighMountain();
    drawLowMountain();

    //snow on the ground
    fill(239, 241, 253);
    rect(0, 180, width, 60);

    //trees
    updateAndDisplayTrees();
    removeTreesThatHaveSlippedOutOfView();
    addNewTreesWithSomeRandomProbability(); 

    //cabins
    updateAndDisplayCabins();
    removeCabinsThatHaveSlippedOutOfView();
    addNewCabinsWithSomeRandomProbability();

    //walking dog 
    for (var i = 0; i < dog.length; i++) { 
        var d = dog[i];
        d.stepFunction();
        d.drawFunction();
    }

    //fence 
    for (var x = 10; x <= width-10; x+=10) {
      stroke(198, 173, 203);
      strokeWeight(5);
      line(x, height, x, height-25);
    }
    line(0, height-15, width, height-15);

}

function drawHighMountain() {
  highMountain.shift();
  var value = map(noise(noiseParam), 0, 1, 0, height*1.1);
  highMountain.push(value);
  noiseParam += noiseStep;
  push();
  fill(170, 190, 250);
  stroke(170, 190, 250);
  beginShape(); 
  vertex(0, height);
  for (var i = 0; i < width/4; i++) {
    vertex(i*5, highMountain[i]);
    vertex((i+1)*5, highMountain[i+1]);
  }
  vertex(width, height);
  endShape();
  pop();
}
function drawLowMountain() {
  lowMountain.shift();
  var value = map(noise(noiseParam), 0, 1, 0, height*1.9);
  lowMountain.push(value);
  noiseParam += noiseStep;
  push();
  fill(193, 205, 246);
  stroke(193, 205, 246);
  beginShape(); 
  vertex(0, height);
  for (var i = 0; i < width/5; i++) {
    vertex(i*5, lowMountain[i]);
    vertex((i+1)*5, lowMountain[i+1]);
  }
  vertex(width, height);
  endShape();
  pop();
}

//cabins 
function makeCabin(birthLocationX) {
    var bldg = {x: birthLocationX,
                breadth: 70,
                speed: -5.0,
                nFloors: round(random(2,8)),
                move: cabinMove,
                display: cabinDisplay}
    return bldg;
}
// method to update position of cabin every frame
function cabinMove() {
    this.x += this.speed;
}
// draw the cabin and some windows
function cabinDisplay() {
    var floorHeight = 8;
    var cHeight = this.nFloors * floorHeight; 
    
    noStroke();
    fill(95, 124, 213);
    push();
    translate(this.x, height - 40);
    //chimmney 
    rect(this.breadth-10, -cHeight - 20, 10, 15);
    //roof
    stroke(239, 241, 253);
    triangle(-7,-cHeight,this.breadth/2,-cHeight-20,this.breadth+7,-cHeight);
    //home
    stroke(95, 124, 213); 
    rect(0, -cHeight, this.breadth, cHeight);

    fill(239, 241, 253);
    noStroke();
    circle(this.breadth/2, -cHeight - 10, 10); 
    rect(this.breadth-10, -cHeight - 20, 10, 5);
    //windows
    fill(148, 178, 249);
    stroke(95, 124, 213);
    for (var i = 0; i < this.nFloors-1; i++) {
        rect(5, -15 - (i * floorHeight), this.breadth - 10, 10);
    }
    pop();
}
function updateAndDisplayCabins(){
    // Update the cabin's positions, and display them.
    for (var i = 0; i < cabin.length; i++){
        cabin[i].move();
        cabin[i].display();
    }
}
function removeCabinsThatHaveSlippedOutOfView(){
  var cabinToKeep = [];
  for (var i = 0; i < cabin.length; i++){
    if (cabin[i].x + cabin[i].breadth > 0) {
        cabinToKeep.push(cabin[i]);
    }
  }
  cabin = cabinToKeep; // remember the surviving cabin
}
function addNewCabinsWithSomeRandomProbability() {
    // With a very tiny probability, add a new cabin to the end.
    var newCabinLikelihood = 0.01; 
    if (random(0,1) < newCabinLikelihood) {
        cabin.push(makeCabin(width));
    }
}

// dog walking character
function makeCharacter(cx, cy) {
    c = {x: cx, y: cy,
         imageNumber: 0,
         stepFunction: characterStep,
         drawFunction: characterDraw
        }
    return c;
}
function characterStep() {
    this.imageNumber++;
    if (this.imageNumber == 8) {
        this.imageNumber = 0;
    }
}
function characterDraw() {
    image(dogImage[this.imageNumber], this.x, this.y);
}

// trees 
function makeTree(birthLocationX) {
    var t = {x: birthLocationX,
                breadth: round(random(20,30)),
                speed: -5.0,
                treeHeight: round(random(30,70)),
                move: treeMove,
                display: treeDisplay}
    return t;
}
function treeMove() {
    this.x += this.speed;
}
function treeDisplay() {
    push();
    translate(this.x, height - 60);
    noStroke(); 
    fill(153, 139, 196);
    rect(-2, 0, 6, 8);
    fill(242, 198, 210); 
    triangle(0, 0, 0, -this.treeHeight, -this.breadth/2, 0);
    fill(223, 186, 209); 
    triangle(0, 0, 0, -this.treeHeight, this.breadth/2, 0);
    stroke(225);
    line(0, 0, 0, -this.treeHeight);
    pop();
}
function updateAndDisplayTrees(){
    for (var i = 0; i < trees.length; i++){
        trees[i].move();
        trees[i].display();
    }
}
function removeTreesThatHaveSlippedOutOfView(){
    var treesToKeep = [];
    for (var i = 0; i < trees.length; i++){
        if (trees[i].x + trees[i].breadth > 0) {
            treesToKeep.push(trees[i]);
        }
    }
    trees = treesToKeep;
}
function addNewTreesWithSomeRandomProbability() {
    var newTreeLikelihood = 0.07; 
    if (random(0,1) < newTreeLikelihood) {
        trees.push(makeTree(width));
    }
}

function cloud(x, y) {
	fill(239, 241, 253, 50);
	arc(x, y, 60, 40, PI + TWO_PI, TWO_PI);
	arc(x + 30, y, 90, 90, PI + TWO_PI, TWO_PI);
	arc(x + 25, y, 40, 70, PI + TWO_PI, TWO_PI);
	arc(x + 50, y, 70, 40, PI + TWO_PI, TWO_PI);
}

For this project, I wanted to created a snowy landscape. I started by drawing two layers of mountains and then added the cabins, the trees, and the running dog. I feel like the cool tone color palette contrasts really well with the dog. The sun and clouds looked pretty plain so I played with the opacity of the shapes to create some visual interest. The hardest part was figuring out how to do the gradient, but I’m happy with the way that it turned out and the way it looks with the snow! I didn’t want to overcrowd the piece too much so the last thing I added was a fence.

initial hand drawn sketch

LO-11 Women Practitioners

Tree of Changes, San Francisco, 2015
Making of the Tree of Changes

Yael Braha is a large-scale dynamic display designer. Braha combines traditional and non-traditional art to creates pieces that combine fine arts and digital fabrication. Braha studied Graphic Design at the European Institute of Design in Rome. After immigrating to the US, she got a Masters of Fine Arts in Cinema at San Francisco State University. Her work has been displayed all across the world for 20 years and is currently based out of Canada.

Braha and her team created Tree of Changes, using 3d-modeling, custom machine learning programs, and fabrication, for the Yerba Buena Center for the Arts for the 2015 Market Street Prototyping Festival. This piece is an interactive sculpture that creates light patterns based on the viewers voice. I admire how this piece speaks to people of all ages because it highlights how Braha’s artistic sensibilities show up in her work. Braha notes her work is inspired by her roots as the daughter of refugees which taught her to value knowledge over belongings. With this piece she not only creates universal interest but also provides them with insight on how cutting-edge technology is being used to create art today. I also admire the aesthetic of the piece in contrast with the night sky.  

Project-10 Sonic Story

sonic story
// Rishi Karthikeyan 
//rkarthik@andrew.cmu.edu
//Section B 

//HW 10 Sonic Story  

let elf; 
    var elfX = 350; var elfY = 225;
let santa; 
    var santaX = 115; var santaY = 225;
let santaWorkshop;
let santaAndReindeer; 
    var santaAndReindeerX = 160; var santaAndReindeerY = 250;
let santaOnChimney;
let kid; 
    var kidX = 100; var kidY = 275;
let tree;

let elfVoice;
let sleighBells;
let santaVoice;
let kidVoice;

var count = 0;
var centerX = 250;
var centerY = 200;
var dx = 30;
var dy = 5;

function preload() {
    //load images
    elf = loadImage('https://i.imgur.com/pu8ed6l.png');
    santa = loadImage('https://i.imgur.com/5rgNTe1.png');
    santaWorkshop =loadImage('https://i.imgur.com/bmjFtUd.png');
    santaAndReindeer = loadImage('https://i.imgur.com/t86lPuw.png');
    santaOnChimney = loadImage('https://i.imgur.com/NoL1xBz.png');
    kid = loadImage('https://i.imgur.com/F06Ekzc.png');
    tree = loadImage('https://i.imgur.com/qOOQlZt.png'); 

    //load sounds
    elfVoice = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/11/ElfVoice.wav");
    sleighBells = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/11/SleighBells.wav");
    santaVoice = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/11/SantaVoice.wav");
    kidVoice = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/11/KidVoice.wav");
}


function setup() {
    createCanvas(500, 400);
    //createDiv("p5.dom.js library is loaded.");
    useSound();
    imageMode(CENTER);
    frameRate(1);
}

function soundSetup() { 
    elfVoice.setVolume(0.5);
    sleighBells.setVolume(0.5);
    santaVoice.setVolume(0.5);
    kidVoice.setVolume(0.5);
}

function draw() {
    background(200);  

    count++;

    switch (count) {
        case 1: elfVoice.play(); break;
        case 6: sleighBells.play(); break;
        case 11: santaVoice.play(); break;
        case 16: kidVoice.play(); break;
    }
    
    if (count >= 0 & count < 5) { 
        workshop();
        dy = 10;
        elfY -= dy;
        if ( elfY <= 215) {
            elfY +=20;
        }
    }
    if (count >= 5 & count < 10) { 
        reindeer(); 
        dy = 20;
        santaAndReindeerX += dx;
        santaAndReindeerY -= dy; 
    }
    if (count >= 10 & count < 15) { 
        chimney(); 
        centerY += dy;
    }
    if (count >= 15 & count < 20) {
        home(); 
        kidY -= 5;
        if ( kidY <= 270) {
            kidY +=10;
        }
    }
    if (count >=20) {
        end(); 
    } 
    
}

function workshop() {
    nightSky();
    //snow 
    fill(255);
    rect(0, 215, width, 200);
    image(santaWorkshop, centerX+80, centerY-30, 300, 100);
    image(elf, elfX, elfY, 130, 130);
    image(santa, santaX, santaY, 206, 200);
}

function reindeer() {
    nightSky();
    //moon
    noStroke();
    fill(208);
    circle(centerX, centerY-30, 200);

    //mountains 
    fill(20, 28, 38);
    beginShape();
    vertex(0, height);
    vertex(0, 270);
    vertex(105, 225);
    vertex(205, 300);
    vertex(290, 250);
    vertex(350, 280);
    vertex(450, 205);
    vertex(width, 225);
    vertex(width, height);
    endShape(CLOSE);

    image(santaAndReindeer, santaAndReindeerX, santaAndReindeerY, 270, 225);
}

function chimney() {
    nightSky();
    //roof
    image(santaOnChimney, centerX, centerY, 230, 230);
    noStroke();
    fill(195, 70, 55);
    rect(0, 315, width, 85); 
}

function home() {
    nightSky();
    
    //wall
    noStroke();
    fill(224, 193, 173);
    rect(0, 0, width, 50);
    rect(0, 0, 50, height);
    rect(250, 0, 250, height);
    rect(0, 150, width, 200);

    //floor
    fill(225, 221, 207);
    rect(0, 300, width, 100);

    //window 
    strokeWeight(5);
    stroke(255);
    noFill();
    rect(50, 50, 100, 100);
    rect(150, 50, 100, 100);
    strokeWeight(2);
    line(50, 100, 250, 100);
    line(100, 50, 100, 150);
    line(200, 50, 200, 150);

    image(kid, kidX, kidY, 150, 125);
    image(tree, 340, 220, 300, 300);
}

function end() {
    nightSky();
    noStroke();
    fill(255);
    textSize(50);
    text('The End', 185, 250);
}

function nightSky() {
    //night sky
    background(6, 20, 34);
    //stars 
    stroke(255);
    for (var i = 0; i < 1000; i++) {
        point(random(0,width), random(-0, height));
    }
}

Four Sounds (1) elf singing (2) sleigh bells (3) Santa talking (4) kid celebrating

I wanted to make something Christmas related. This story shows Santa delivering presents. This project was a lot of fun to create and helped me better understand how to code with images and sound.

LO-10 Computer Music

Explanation and example of Computer Orchestra

Computer Orchestra was created in 2013 by Fragment.in, an artist collective comprised of Laura Perrenoud, David Colombini and Marc Dubois. They used Processing, SimpleOpenNI, and OpenKinect to create this project. Computer Orchestra is an interactive installation that uses multiple computers to allow the user to conduct their own orchestra. Their hand movements are recognized by a Kinect motion controller that transmits data to Processing through the SimpleOpenNI library. Processing then sends a signal to the main computer which gives instructions to the rest of the screens.

I admire how this piece is made to be accessible. Computer Orchestra crowdsources sounds to use and users can upload their own sounds as well. I also admire that there is a visual component to the project, along with the sound the program creates aesthetically pleasing sound waves on the screen that enhance the experience. It’s amazing to see how just one person can create the feel of an entire orchestra.  

Fragment.in states that they use their work as a way to question the impact of the digital on everyday life. These artistic sensibilities are seen in Computer Orchestra through the way it highlights how one person can replace an entire orchestra with the use of digital. 

Project-09 Portrait

project
let portrait;
var radius = 0;
var angle = 0;
var framecount = 1;
var size = 0.5;

function preload() {
  portrait = loadImage('https://i.imgur.com/EquKB8x.png');
}

function setup() {
  createCanvas(480, 480); 
  background(0);
  imageMode(CENTER);
  portrait.loadPixels();
}

function draw() {
  var centerX = width/2;
  var centerY = height/2;
  var circleX = radius * cos(radians(angle));
  var circleY = radius * sin(radians(angle));

  var clrX = random(0, width);
  var clrY = random(0, height);
  var clr = portrait.get(clrX, clrY);

  //pointillize 
  for (var i= 0; i < 10; i++) {
    fill(clr);
    circle(clrX, clrY, size);
  }

  //top left spiral of hearts of image 
  noStroke();
  var clr1 = portrait.get(circleX, circleY);
  fill(clr1);
  drawHeart(circleX, circleY, 15);

  //bottom right spiral of hearts of image 
  push();
  translate(centerX, centerY);
  var clr2 = portrait.get(480-circleX, 480-circleY);
  fill(clr2);
  drawHeart(centerX-circleX, centerY-circleY, 15);
  pop();

  topInstagramBar();
  bottomInstagramBar();

  radius += 0.1;
  angle += 7;

  frameCount += 1;
  if (frameCount > 13000) {
    //"instagram tag" of my nickname where mouse is 
    fill(85);
    triangle(mouseX, mouseY-10, mouseX+10, mouseY-20, mouseX-10, mouseY-20);
    rect(mouseX-40, mouseY-40, 80, 20);
    fill(255);
    text('rishdish', mouseX-20, mouseY-27);

    //stops when portrait is complete
    noLoop();
  }
}

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

function mousePressed() {
  //points get bigger as you click 
  size += 0.5;
  if (size == 7) {
    size = 0.5;
  }
}

function topInstagramBar() {
  //instagram bar 
  fill(240);
  rect(0, 0, 480, 30);

  //profile pic 
  fill(230, 210, 210);
  circle(20, 15, 15);
  stroke(195, 45, 140);
  noFill();
  circle(20, 15, 18);
  noStroke();
  fill(0);
  text('15-104',35, 19);

  //three dots in right hand corner
  fill(10);
  circle(455, 15, 3);
  circle(460, 15, 3);
  circle(465, 15, 3);
}

function bottomInstagramBar() {
  //instagram bar 
  fill(240);
  rect(0, 450, 480, 30);
  
  //"like" icon
  stroke(0);
  fill(255);
  drawHeart(20, 460, 15);
  
  //"comment" icon
  triangle(53, 471, 60, 471, 58, 467);
  circle(50, 465, 16);
  stroke(255);
  circle(55, 468, 5);

  //"share" icon
  stroke(0);
  triangle(75, 457, 92, 457, 80, 463);
  triangle(80, 463, 92, 458, 82,472);

  //"save"icon
  beginShape();
  vertex(454, 457);
  vertex(454, 472);
  vertex(460, 467);
  vertex(466, 472);
  vertex(466, 457);
  endShape(CLOSE);

  //image scroll through icons
  noStroke();
  fill(65, 141, 204);
  circle(230, 465, 5);
  fill(165);
  circle(240, 465, 5);
  circle(250, 465, 5);
}

Start of portrait/halfway completed portrait
Completed portrait

I used a photo of me as a baby for this. Once I had the two spirals of hearts running, I added the icons to make it look like an instagram post. When you click, the size of the points in the background increases. When the portrait is done running, an Instagram tag shows up where the mouse is with my childhood nickname.

LO-09 On Looking Outwards

“Womb” Jennifer Steinkamp, Talley Dunn Gallery, August 23, 2019. Video

When browsing through the 3-D Computer Graphics Looking Outwards, I found Poppy’s post about Jennifer Steinkamp’s digital animation “Womb” very interesting. I was initially intrigued by how she wrote about the piece being centered around femininity. I found my peer’s assessment about how the artist’s sensibilities manifesting in the piece to make a meaningful analogy with her work very insightful.  

I admire the way the artist uses bright “Fruit Ninja” style visuals to create a thought-provoking piece. Steinkamp describes this piece as a way of showing how a womb is a place of transformation that “grows and stretches outward to exercise agency.” With this piece, the viewer acts as the form of agency as the piece’s movement mirrors the viewer’s movement. I agree with my peer’s assessment that this is an interesting way to represent how we as people take up space.

I learned that the wind sound used is taken from the Wizard of Oz tornado scene, which I thought was an interesting choice as it’s unrelated to the subject matter of the piece. I also learned that Steinkamp tailors each of her pieces depending on the space in which she shows, which I thought was very impressive. 

LO-08 Creative Practice of An Individual

Giorgia Lupi’s 2018 Eyeo Talk

Giorgia Lupi is an information designer, partner at Pentagram, co-founder of Accurat, and co-author of two books. She got her master’s degree in Architecture and is based out of Milan and New York. Her work is featured at the MoMA, Cooper Hewitt, and Smithsonian. She describes herself as a data humanizer, she humanizes data through the story behind the data. Her body of work usesdata to visualize aspects of life that we ‘don’t usually associate with numbers.’ Her work ranges from hand-drawn data visualizations to data driven fashion collections to large-scale computer graphics for big names like SNL and IBM. 

I admire how Lupi uses data to better understand human nature because she helps her audience empathize with others. For example, in the project of hers I admire most, Bruises she uses her friend’s daughter’s clinical data to illustrate both the factual and emotional aspect of the experience. Lupi’s presentation strategy is a conversational tone and a ton of visual aids. This is effective because it doesn’t feel dry, she tells a story and uses humor to engage her audience. I can definitely learn from the way she frames the story of her art to help her audience relate more. 

Bruises by Giorgia Lupi (music by Kaki King)
Key for Bruises Visualization

Project-07 Curves

curves
var r = 0;    //red color variable 

var a = 0;    //radius of circle a (inner circle) for parametric function 
var b = 0;    //radius of circle b (fixed circle) for parametric function  
var h = 0;    //distance from center of inner circle

var theta = 0;    //angle variable
 
function setup() {
    createCanvas(480, 480); 
    background(220);
}

function draw() {

	//changing red level of background  
	r = map(mouseX, 0, 480, 0, 255); 
    background(r, 200, 200);

    //creates 9 Hypotrochoid curves
    for (var x = 0; x <= 400; x += 160) {
        for (var y = 0; y <= 400; y += 160) {
            push();
            translate(x+80, y+80);
            drawHypotrochoid();
            pop();
        }
    }

    //creates 16 Hypotrochoid Evolute curves
    for (var x = 0; x <= 480; x += 160) {
        for (var y = 0; y <= 480; y += 160) {
            push();
            translate(x, y);
            drawEvolute();
            pop();
        }
    }
}

function drawHypotrochoid() {

    //curve based on the Hypotrochoid equation 

    strokeWeight(0.5);
    stroke(255);
    noFill();
    beginShape();    

    a = map(mouseX, 0, 480, 1, 70);
    b = map(mouseY, 0, 480, 1, 5);
    h = map(mouseX, 0, 480, 1, 40);

        for (var i = 0; i < 2000; i++) {

            var x = (a-b)*cos(theta) + h*cos((a-b)/b*theta);
            var y = (a-b)*sin(theta) + h*sin((a-b)/b*theta);
            var theta = map(i, 0, 360, 0, TWO_PI);
        
            vertex(x, y);
        }  

    endShape();

    pop();
}

function drawEvolute() {

    //curve based on the Hypotrochoid Evolute equation
    
    strokeWeight(1);    
    stroke(0, 150, 0);

    noFill();
    beginShape();    

    a = 8*map(480-mouseX, 0, 480, 1, 30);
    b = 2*map(480-mouseY, 0, 480, 1, 20);
    h = 3*map(480-mouseX, 0, 480, 1, 10);

        for (var i = 0; i < 4000; i++) {

            var x = (a-b)*cos(theta) + h*cos((a-b)/b*theta);
            var y = (a-b)*sin(theta) + h*sin((a-b)/b*theta);
            var theta = map(i, 0, 480, 0, TWO_PI);
        
            vertex(x, y);
        }  

    endShape();

    pop();
}
screenshots of cursor at different points

For this project, I tried out a few of the equations from the reference and liked the look of the Hypotrochoid the best. Once I had the Hypotrochoid curve working I want to add some visual interest by replicating them with a loop. Once that was done, I added another curve, the Hypotrochoid Evolute and replicated that with a loop too. After the curves were all in place, I added the changing background. The biggest challenge was understanding how changing the values of variables in the equations effected the curve but once I was able to get that it was fun to mess around with! 

LO-07 Computational Visualization

“Flight Patterns” Brightness increases as more flights fly out of that region
Color represents type of aircrafts
White represents low altitude and blue high altitude

“Flight Patterns” was developed by Aaron Koblin, Scott Hessels, and Gabriel Dunne as part of a series of experiments for the project “Celestial Mechanics” initially created in August 2005. The artists parsed and plotted data using the Processing Programming Environment. The frames were then composited on Adobe After Effects and Maya.

I admire the artist’s ability to convey so much information through the different variations and avoids overcrowding the piece. Each element provides a new detail, the brightness increases as flight density increases, color represents type of aircrafts and altitude. I also admire the way he is able to create visual interest out of mundane data; each flight pattern looks like a mini firework.

The creator’s artistic sensibilities manifest in the final form through the story the piece tells. In Koblin’s Ted Talk he talks about how collecting data can make us more human and how he humanizes data through his work. For example, with this specific piece, you can see when people depending on their region are the busiest based on how bright that part of the piece is.