Zee Salman- Final

Directions:

Use your mouse you glide the paddle to save the ball from dropping.

colorful cubes will be dropping at the consistent timed rate, make sure to hit the brick with the ball to increase count.

When you hit the ball, you get one point count.

For a challenge, click on the screen to add more balls, but you must balance all to save the game.

Im pretty happy with how this turned out, I definetly used almost everything I knew or saw in the course. It was really fun to make a remix of brick breaker and I learned even more along the way. I was definetly fun to recreate my child hood game.

 

sketch

//Zee Salman
//SECTION E

var gravity = 0.3;   // downward force
var springy = 0.2; // velocity 
var drag = 0.0001;    // drag 
var np = 1;      // how many particles
var fp= 1;
var particles = [];
var endgame = false;
var timerValue = 3;
var startButton;
var numberBalls = 0;
var x;
var y;
var count = 0;

var block = {
    x: 200,
    y: -25,
    w: 30,
    h: 30,
    color: "blue",
}



function particleStep() {
    this.x += this.dx;
    this.y += this.dy;
    if (this.y >= height) { // bounce off bottom
        endgame = true;
    }
    if (this.x > width) { // bounce off right wall
        this.dx = -this.dx * springy;

    } else if (this.x < 0) { // bounce off left wall
        this.dx = -this.dx * springy;

    } else if (this.y < 0) { // bounce off top
        this.y = -this.y;
        this.dy = -this.dy * springy;
    } 

}

function particleDraw() {
  //draws the ball
	fill(this.colorR, this.colorG, this.colorB);
    ellipse(this.x, this.y, this.size, this.size);
}


// create a ball object 
function makeParticle(px, py, pdx, pdy) {
    p = {x: px, y: py,
         dx: pdx, dy: pdy,
         step: particleStep,
         draw: particleDraw,
         size: 20,
         colorR: randomColor(),
         colorG: randomColor(),
         colorB: randomColor(),
        
        }
    return p;
}

//randone color for each ball
function randomColor(){
	return (random(0,255));
}

function hitBlock(ball, block) {
  //ball characteristics
        const ballLeft = ball.x - ball.size/2;
        const ballRight = ball.x + ball.size/2;
        const ballTop = ball.y - ball.size/2;
        const ballBottom = ball.y + ball.size/2;
//block characteristics
        const blockLeft = block.x - block.w / 2;
        const blockRight = block.x + block.w / 2;
        const blockTop = block.y - block.h / 2;
        const blockBottom = block.y + block.h / 2;
//check for collision
        if (
            ballRight >= blockLeft &
            ballLeft <= blockRight &&
            ballBottom >= blockTop &&
            ballTop <= blockBottom

        ) {
           
            if (ballRight >= blockLeft && ballLeft <= blockLeft) {
            
                return true;
            } else if (ballLeft <= blockRight & ballRight >= blockRight) {
               
                return true;
            } else if (ballBottom >= blockTop & ballTop <= blockTop) {
                return true;
            } else if (ballTop <= blockBottom & ballBottom >= blockBottom) {
               
                return true;
            }

        }

        return false;
      }
function collide() {
    for (let i = 0; i < particles.length; i++) {
    //if the ball hits the brick, irt disappears
      if(hitBlock(particles[i], block)){
        count +=1;
        block.y = -300;
        block.color = randomColor();
      }
     
    }


  }



function setup() {
    createCanvas(600, 400);
    for (var i = 0; i < np; i++) {
        // make a particle
        var p = makeParticle(80, 20, 7,7);       
        // push the particle onto particles array
        particles.push(p);
  
    }
    setInterval(timeIt, 1000);

}


function draw() {
    //console.log(count);
    background("pink");

    
    //drawing the board
    stroke(0);
    fill('black');
    rectMode(CENTER);
    rect(mouseX,(11*height/12)+10,100, 10);
        
    //if the game is not over...
    if (endgame == false){
        //console.log("Working draw func");
        collide();
        fill(block.color);
        noStroke();
        rectMode(CENTER);
        rect(block.x, block.y, block.w, block.h);
        if (block.y >= (11*height/12)) {
          endgame = true;
                }
    // draw all particles in the particles array
        for (var i = 0; i < particles.length; i++) { // for each particle
            var p = particles[i];
            p.step();
            p.draw();
          //if the ball touches the board, 
          //it bounces at a slighlty different angle everytime
       		if (near(p.x,p.y,p.size)){
       		 	p.dx = -p.dx + (PI/3);
       		 	p.dy = -p.dy;
                springy = 1.01;
                push();
                translate(width,height);
                rotate(PI/3);
                p.draw();
                p.step();
                pop();
                if((block.y >= height)){
                  endgame = true;
                }
    		}
        }   
        //timer to display when the next ball is going to disperse
            fill("black");
            textStyle(BOLD);
            text("Count: " + count, width / 2.3, height / 3);
            textStyle(NORMAL);
            if (timerValue >= 10) {
              fill(0);
              textSize(20);
              text("0:" + timerValue, width / 2, height / 2);
            }
            if (timerValue < 10) {
              fill(0);
              textSize(20);
              text('0:0' + timerValue, width / 2, height / 2);
            }
            if (timerValue == 0) {
              if(block.y < 0){
                block.y = random(20, 350);
                block.x = random(20, 350);  
              }else{
                block.y +=20;
                fill(0);
                textSize(20);
                timerValue = 3;  
              }
              
            //}
            }
        //if the game is over...
        }else if (endgame == true){
                count = 0;
                // transperent background
                fill(0,0,0,100);
                rect(0,0,width*2, height*2);

                //Game Over display
                fill(0);
                rect(width/2, height/2, 200, 70);
                fill(255);
                textSize(32);
                text("Game Over!", width / 2.8, height / 1.9);

                //Restart button w/ a hover effect
                fill(255);
                noStroke();
                rect(width/2, height/1.5, 100, 50);
                fill(0);
                textSize(20);
                text("Restart", width / 2.25, height / 1.47);

                if ((mouseX > (width/2)-50) & (mouseY < (height/1.5)+25) &&
                 (mouseX < (width/2)+50 && (mouseY > (height/1.5) - 25))) {
                    fill("green");
                    rect(width/2, height/1.5, 100, 50);
                    rect(width/2, height/1.5, 100, 50);
                    fill(255);
                    textSize(20);
                    text("Restart", width / 2.25, height / 1.47);


            }
            
                }

    }


function timeIt() {
  //timer
  if (timerValue > 0) {
    timerValue--;
  }
}
function mouseClicked() {
    //for users that want a challenge, click.
    var newp = makeParticle(mouseX, mouseY,
                                random(4, 6), random(4, 6));
        particles.push(newp);
    if(endgame == true) {
    //if restart button clicked, reset game 
        if (mouseX > 250 & mouseX < 550 && mouseY < 291 &
                 mouseY > 241) {
            endgame = false;
            particles = [];
            var p = makeParticle(80, 20, 7,7);
            particles.push(p);
            timerValue = 3;
            block.y = -25;

        }
    }
}


function near(x,y,size) {
//constant testing to see if the ball is touching the board
      if ((block.y + block.h) >= (11*height/12)) {
        //count +=1;
        block.x = random(20,300);
        block.y = -25;
      }

    	if((y + size/2) >= (11*height/12) &
       (x >= mouseX-50 && x <= mouseX + 100)) {

    		return true;

    	}
    	else{
    		return false;

    	}

    }






Margot Gersing – Final Project

The goal of my final project was to create some interactive project that is simple but playful. When working on this project I took it very incrementally, building on the previous versions and trying to add more elements or making the previous ones better. As I worked my way through the code, I got more comfortable and became more efficient with the way I wrote it. I enjoyed working this way because I found it a lot less intimidating. I really enjoyed making this project. I think if I wanted to I could continue to add on to this. I think a logical next step could be incorporating sound.

Note: I made the canvas size larger then what wordpress can handle because I liked the look of it being a larger area. Below is a zip file of the full size files. The one on wordpress is basically the same just with a few composition adjustments.

Instructions: just use the mouse to play around with the scene. Type into the input bar and hit enter to change the type displayed on the screen.

sketch

//Margot Gersing - Final project - mgersing@andrew.cmu.edu - Section E

//for blob shapes
//object one
var xOne;
var yOne;
var dOne;
var opaCityOne = 120;
var overOne = false;
//object two
var xTwo;
var yTwo;
var dTwo;
var opaCityTwo = 120;
var overTwo = false;
//object three
var xThree;
var yThree;
var dThree;
var opaCityThree = 110;
var overThree = false;
//object four
var xFour;
var yFour;
var dFour;
var opCityFour = 110;
var overFour = false;
//object five
var xFive;
var yFive;
var dFive;
var opCityFive = 110;
var overFive = false;

//snake -- code refrenced and modified from p5js examples
//https://p5js.org/examples/interaction-follow-3.html --- link to code refrenced
var snakeX = []; //arrays for snake
var snakeY = [];
var snakeJoint = 10; //amount of joints
var snakeLength = 10; //length between

for (let i = 0; i < snakeJoint; i++) { //loading the joints and lenght into to x and y arrays
  snakeX[i] = 0;
  snakeY[i] = 0;
}

//for "pong" letters on screen
var letterString = "hello!"; //starting string
var playArray = []; //store the objects (letters and their movements)
var d = [-1, 1]; //direction of array
var input; //for typing

function setup() {
  createCanvas(800, 600);
  noStroke();
  //for type
  textFont("Courier New"); //type style
  textSize(60); //typesize
  //for input bar 
  input = createInput(); //create the bar to type into
  input.position(50, 20);
  //for bouncing type, put string into array
  for(var i = 0; i < letterString.length; i++){ //load array with objects
    var x = map(i, 0, letterString.length, 100, width - 100); //map letters to begin
    var vvx = random(1, 5) * d[round(random(0, 1))]; //random velocity in x direction
    var vvy = random(1, 5) * d[round(random(0, 1))]; //random velocity in y direction
    playArray[i] = createLetter(x, width/2, letterString.charAt(i), vvx, vvy) //call object
  }
}

function draw() {
  background(250, 120, 110);
  //call blob shapes
  thingOne(); 
  thingTwo();  
  thingThree();
  thingFour();
  thingFive();
  //for snake
  stroke(87, 156, 128);
  strokeWeight(10);
  dragSnake(0, mouseX, mouseY); //make snake move with mouse
  for (let i = 0; i < snakeX.length - 1; i++) { //create the moevment quality for the snake
    dragSnake(i + 1, snakeX[i], snakeY[i]);
  }
  //get the letters to move around
  noStroke();
  fill("black");
  for(var i = 0; i < playArray.length; i++){ 
    var t = playArray[i]; 
    if(dist(mouseX, mouseY, t.x, t.y) < 30){ //when mouse hovers over letter
       t.vx = random(1, 5) * d[round(random(0, 1))]; //get new random velocity x
       t.vy = random(1, 5) * d[round(random(0, 1))]; //get new random velocity x
       }
    if(t.x > width -30 || t.x < 30 || t.y > height - 30 || t.y < 30){ //when the letters hit the walls
       t.vx = -t.vx; //reverse the direction  
       t.vy = -t.vy;
    }
    t.x = t.x + t.vx; //update the location of the letters
    t.y = t.y + t.vy;
    text(t.letter, t.x, t.y); //display it
  }
}

function thingOne(){
  //activation circle
  xOne = 25; //location
  yOne = 100;
  dOne = 600; //diameter
  // Test if the cursor is over the box
  if(dist(mouseX, mouseY, xOne, yOne) < dOne / 2){ //if the mouse is over the activation ellipse -- var over one is true
    overOne = true;
    if (overOne == true) { //if true, then change value of opacity which chnages fill color of blob  
      opaCityOne = 200;
      fill(250, opaCityOne, 110); //changing the fill
    }
  } else { //other wise go back to orignal color
    if (opaCityOne >= 120) opaCityOne = opaCityOne - 3; //fades out 
    fill(250, opaCityOne, 110);
    overOne = false;
  }
  drawOne();
}

function drawOne(){ //location and shape of blob in top left
  noStroke();
  ellipse(25, 100, 620, 700);
  ellipse(50, 300, 450, 320);
}

function thingTwo(){ //same as function thingOne but for bottom right blob
  xTwo = 650;
  yTwo = 600;
  dTwo = 500;
  // Test if the cursor is over the box
  if(dist(mouseX, mouseY, xTwo, yTwo) < dTwo / 2){
    overTwo = true;
    if (overTwo == true) { 
      opaCityTwo = 200; 
      fill(250, opaCityTwo, 110);
    }
  } else {
    if (opaCityTwo >= 120) opaCityTwo = opaCityTwo - 5;
    fill(250, opaCityTwo, 110);
    overTwo = false;
  }
  drawTwo();
}

function drawTwo(){ //location and shape of blob in bottom right
  noStroke();
  ellipse(650, 600, 600, 500);
  ellipse(800, 450, 400, 400);
}

function thingThree(){ //same as function thingOne but for top right blob
  xThree = 700;
  yThree = -20;
  dThree = 500;
  // Test if the cursor is over the box
  if(dist(mouseX, mouseY, xThree, yThree) < dThree / 2){
    overThree = true;
    if (overThree == true) { 
      opaCityThree = 255; 
      fill(250, 120, opaCityThree);
    }
  } else {
    if (opaCityThree >= 110) opaCityThree = opaCityThree - 3;
    fill(250, 120, opaCityThree);
    overThree = false;
  }
  drawThree();
}

function drawThree(){ //location and shape of blob in top right
  noStroke();
  ellipse(700, -20, 550, 300);
}

function thingFour(){ //same as function thingOne but for center blob
  xFour = 475;
  yFour = 225;
  dFour = 200;
  // Test if the cursor is over the box
  if(dist(mouseX, mouseY, xFour, yFour) < dFour / 2){
    overFour = true;
    if (overFour == true) {
      opCityFour = 255;  
      fill(250, 120, opCityFour);
    }
  } else {
    if (opCityFour >= 110) opCityFour = opCityFour - 3;
    fill(250, 120, opCityFour);
    overFour = false;
  }
  drawFour();
}

function drawFour(){ //location and shape of blob in center
  noStroke();
  ellipse(475, 225, 225, 200);
}

function thingFive(){ //same as function thingOne but for bottom left
  xFive = 150;
  yFive = 600;
  dFive = 200;
  // Test if the cursor is over the box
  if(dist(mouseX, mouseY, xFive, yFive) < dFive / 2){
    overFive = true;
    if (overFive == true) {
      opCityFive = 255;  
      fill(250, 120, opCityFive);
    }
  } else {
    if (opCityFive >= 110) opCityFive = opCityFive - 3;
    fill(250, 120, opCityFive);
    overFive = false;
  }
  drawFive();
}

function drawFive(){ //location and shape of blob in bottom left
  noStroke();
  ellipse(150, 600, 225, 200);
}

function dragSnake(i, xin, yin) { // for snake
  const dx = xin - snakeX[i];
  const dy = yin - snakeY[i];
  const angle = atan2(dy, dx);
  snakeX[i] = xin - cos(angle) * snakeLength;
  snakeY[i] = yin - sin(angle) * snakeLength;
  joint(snakeX[i], snakeY[i], angle);
}

function joint(x, y, a) { //for snake
  push();
  translate(x, y);
  rotate(a);
  line(0, 0, snakeLength, 0);
  pop();
}

function createLetter(x, y, letter, vx, vy){ //object for each letter from the array, with loction, velocity
  return{x: x, y: y, letter: letter, vx: vx, vy: vy}
}

function keyPressed(){ //when hit enter key call word function
  if(keyCode == ENTER) word();
}

function word() { //for changing the letters on the screen
  playArray = []; //empty array that holds letters from string
  letterString = input.value(); //make the value of the input the new string (what you type)
  //print(input.value());
  //reload the newstring into the array
  for(var i = 0; i < letterString.length; i++){ //load array with objects
    var x = map(i, 0, letterString.length, 100, width - 100); //map letters to begin
    var vvx = random(1, 5) * d[round(random(0, 1))]; //random velocity 
    var vvy = random(1, 5) * d[round(random(0, 1))];
    playArray[i] = createLetter(x, width/2, letterString.charAt(i), vvx, vvy) //call object
  }
}

Charmaine Qiu – Final Project


sketch

For my final project, I created an ice-cream decoration game inspired by cooking games that I play as a child. The game allows users to drag around and place the decorations on top of the ice-cream, which changes flavors when being pressed. This project encourage me to create my own illustrations and incorporated them to my code to create a game. I had fun figuring out the interactions the players can explore on the canvas. 

//Charmaine Qiu
//Section E
//charmaiq@andrew.cmu.edu
//Final project

//Array of links for icecream images
var iceLinks = [
    "https://i.imgur.com/RQVGPb3.png",
    "https://i.imgur.com/3NURceZ.png",
    "https://i.imgur.com/gk7Jbfc.png",
    "https://i.imgur.com/KzfjvaF.png",
    "https://i.imgur.com/RyVDAlq.png",
    "https://i.imgur.com/OoltZSF.png",
    "https://i.imgur.com/DLpHcUh.png",
    "https://i.imgur.com/Jd0Hx3a.png",
    "https://i.imgur.com/G4zwiYV.png"]
//initialize index and empty array
var iceindex = 0;
var ice = [];
//set dragging for each decorationa as false
var bdragging = false;
var kdragging = false;
var sdragging = false;
var chdragging = false;
var fdragging = false;
var codragging = false;

var finishgame = false;
//set initial coordinate values for the decorations
var bx = 460;
var by = 60;
var kx = 540;
var ky = 100;
var sx = 490;
var sy = 140;
var chx = 160;
var chy = 50;
var fx = 160;
var fy = 125;
var cox = 80;
var coy = 100;
//initiallize offset values
var offsetX;
var offsetY;
var offsetX2;
var offsetY2;
var offsetX3;
var offsetY3;
var offsetX4;
var offsetY4;
var offsetX5;
var offsetY5;
var offsetX6;
var offsetY6;

function preload(){
    //preload the icecream images and store them into the arrays
      for (var i = 0; i < iceLinks.length; i++){
        //store the index from the links to the new arrays
        ice[i] = loadImage(iceLinks[i]);
      }
      //preload the images for decorations
      blueberry = loadImage("https://i.imgur.com/BbD4FQd.png");
      kiwi = loadImage("https://i.imgur.com/pwxFml9.png");
      strawberry = loadImage("https://i.imgur.com/qwPbN2l.png");
      cookie = loadImage("https://i.imgur.com/Eo3TG0F.png");
      cherry = loadImage("https://i.imgur.com/FI8MUOH.png");
      flower = loadImage("https://i.imgur.com/PDusCa3.png");
      star = loadImage("https://i.imgur.com/09DBgK0.png");
}

function setup() {
    createCanvas(600, 600);
}

function draw() {
    background(255, 217, 214);
    ellipse(width/2, height, 280, 250);
    //draw table
    drawCurtain();
    //draw the text for instructions
    fill(255);
    text("Drag to decorate", 240, 90);
    text("Click to change a flavor", 210, 120);
    //draw the icream base
    imageMode(CENTER);
    image(ice[iceindex], width / 2, 375);
    //draw image of decorations
    image(blueberry, bx, by);
    image(kiwi, kx, ky);
    image(strawberry, sx, sy);
    image(cookie, cox, coy);
    image(cherry, chx, chy);
    image(flower, fx, fy);
    //when the decoration is being dragged, apply offset values
    if (bdragging){
        bx = mouseX + offsetX;
        by = mouseY + offsetY;
    }
    if(kdragging){
        kx = mouseX + offsetX2;
        ky = mouseY + offsetY2;
    }
    if(sdragging){
        sx = mouseX + offsetX3;
        sy = mouseY + offsetY3;
    }
    if(chdragging){
        chx = mouseX + offsetX4;
        chy = mouseY + offsetY4;
    }
    if(fdragging){
        fx = mouseX + offsetX5;
        fy = mouseY + offsetY5;
    }
    if(codragging){
        cox = mouseX + offsetX6;
        coy = mouseY + offsetY6;
    }
    //draw the buttons for finish and reset
    fill(255);
    noStroke();
    ellipse(80, 500, 100, 40);
    ellipse(80, 550, 100, 40);
    fill(217, 118, 115);
    textSize(20);
    text("Finish!", 52, 506);
    text("Reset", 52, 556);
    //when the game is finished, draw shape that displays the icecream
    if(finishgame){
        finishGame();
    }
}

function mousePressed(){
    //when the mouse is pressed on the icecream
    if(mouseY > 250 & mouseX > 180 && mouseX < 420) {
      //set the current ice equal to current index
      var currentIce = iceindex;
      while(currentIce === iceindex){
        //randomnize the next image
        iceindex = floor(random(0, 8));
      }
    }
    //when the mouse is pressed on each decoration, dragging becomes true and
    //offset values are applied
    if(mouseX > bx - 21 & mouseX < bx + 21 && mouseY > by - 17 && mouseY < by + 17){
        bdragging = true;
        offsetX = bx - mouseX;
        offsetY = by - mouseY;
    }
    if(mouseX > kx - 28 & mouseX < kx + 28 && mouseY > ky - 27 && mouseY < ky + 27){
        kdragging = true;
        offsetX2 = kx - mouseX;
        offsetY2 = ky - mouseY;
    }
    if(mouseX > sx - 32 & mouseX < sx + 32 && mouseY > sy - 40 && mouseY < sy + 40){
        sdragging = true;
        offsetX3 = sx - mouseX;
        offsetY3 = sy - mouseY;
    }
    if(mouseX > chx - 26 & mouseX < chx + 26 && mouseY > chy - 34 && mouseY < chy + 34){
        chdragging = true;
        offsetX4 = chx - mouseX;
        offsetY4 = chy - mouseY;
    }
    if(mouseX > fx - 22 & mouseX < fx + 22 && mouseY > fy - 29 && mouseY < fy + 29){
        fdragging = true;
        offsetX5 = fx - mouseX;
        offsetY5 = fy - mouseY;
    }
    if(mouseX > cox - 49 & mouseX < cox + 49 && mouseY > coy - 86 && mouseY < coy + 86){
        codragging = true;
        offsetX6 = cox - mouseX;
        offsetY6 = coy - mouseY;
    }
    //when the finish button is pressed, the icecream is displayed
    if(mouseX > 30 & mouseX < 130 && mouseY > 480 && mouseY < 520){
        finishgame = true;
        finishGame();
    }
    //reset the game when button is pressed
    reset();
}

function mouseReleased(){
    //when the mouse is released, dragging is nolonger happening
    bdragging = false;
    kdragging = false;
    sdragging = false;
    chdragging = false;
    codragging = false;
    fdragging = false;
}

function reset(){
    //set cordinates to initial values when the reset button is pressed
    if(mouseX > 30 & mouseX < 130 && mouseY > 530 && mouseY < 570){
        bx = 460;
        by = 60;
        kx = 540;
        ky = 100;
        sx = 490;
        sy = 140;
        chx = 160;
        chy = 50;
        fx = 160;
        fy = 125;
        cox = 80;
        coy = 100;
        finishgame = false;
    }
}

function drawCurtain(){
    //when game is not finished, display the curtains
    if (!finishgame){
        ellipseMode(CENTER);
        fill(217, 118, 115);
        rect(0 ,0, width, 190);
        for(var i = 25; i < width; i+= 50){
            fill(217, 118, 115);
            ellipse(i, 190, 50, 50);
        }
    }
}

function finishGame(){
        //creates shape that covers the background to display ice
        fill(255, 217, 214);
        beginShape();
        vertex(0, 0);
        vertex(0, 525);
        vertex(158, 525);
        vertex(158, 150);
        vertex(440, 150);
        vertex(440, 600);
        vertex(600, 600);
        vertex(600, 0);
        vertex(0, 0);
        endShape();
        //draws the text Enjoy
        fill(217, 118, 115);
        text("Enjoy~", 280, 140);
        //displays stars
        frameRate(8);
        starX = random(width);
        starY = random(height);
        starX2 = random(width);
        starY2 = random(height);
        image(star, starX, starY);
        image(star, starX2, starY2);
}

William Su – Final Project –

sketch

// William Su
// Section E
// wsu1@andrew.cmu.edu
// Final Project

var timer = 0;

let x = 0; //person position x
let x1 = 0;
let ender = 0; //variable used to end a scene.
let years;  //calculate random years.
let miles; //calculate random miles.

function setup() {
    createCanvas(windowWidth, 750); //Full window
    textSize(14);
    frameRate(60);

    //preload images
    Store = loadImage('https://i.imgur.com/L4qngWN.png');

    walkin = loadImage('https://i.imgur.com/W2fK1A3.png');
    walkout = loadImage('https://i.imgur.com/C5LA4S1.png');
    broken = loadImage('https://i.imgur.com/aebY3Uk.png');

    iphone = loadImage('https://i.imgur.com/iI6LMAU.png');
    iphoneInside = loadImage('https://i.imgur.com/pL2oAbJ.png');
    battery = loadImage('https://i.imgur.com/9oZjkzY.png');

    numberGen(); //Generate random years and miles.
}

function draw() {
    background(255);

    //timer starts and 0 and increments every frame of draw. 
    if (timer > 0) {
        background(255);
        image(Store, 500, windowHeight/2 - 100, Store.width / 2, Store.height / 2);

        image(walkin, 800 - x, windowHeight/2 - 8, 20, 30);

        if(x <= 240) {
            x += 2;
        } else {
            x = 590;
        }
    }

    if (timer > 110) {
        push();
        strokeWeight(2);
        fill(255);
        beginShape();
        vertex(558, windowHeight/2 - 8);
        vertex(565, windowHeight/2 - 12);
        vertex(565, windowHeight/2 + 24);
        vertex(558, windowHeight/2 + 20);
        endShape(CLOSE);
        pop();
    }

    if (timer > 130) {
        background(255);
        image(Store, 500, windowHeight/2 - 100, Store.width / 2, Store.height / 2);
    }

    if (timer > 140) {
        background(255);
        image(Store, 500, windowHeight/2 - 100, Store.width / 2, Store.height / 2);
        push();
        fill(100);
        text("Billions of smartphones are sold every year...", windowWidth/2 - 150, windowHeight/2 + 50);
        pop();
    }

    if (timer > 340) {
        background(255);
        image(Store, 500, windowHeight/2 - 100, Store.width / 2, Store.height / 2);
        push();
        fill(100);
        text("But how far can one get...", windowWidth/2 - 150, windowHeight/2 + 50);
        pop();
    }

    if (timer > 480) {
        background(255);
        image(Store, 500, windowHeight/2 - 100, Store.width / 2, Store.height / 2);
        push();
        fill(100);
        text("before needing to get a new one?", windowWidth/2 - 150, windowHeight/2 + 50);
        pop();
    }

    if (timer > 620) {
        background(255);
        image(Store, 500, windowHeight/2 - 100, Store.width / 2, Store.height / 2);
        push();
        fill(100);
        text("Lets find out >\n(Right Arrow Key)", windowWidth/2 - 150, windowHeight/2 + 50);
        pop();
    }

    if (timer > 620 & x1 > 0) {
        background(255);
        image(Store, 500 - x1, windowHeight/2 - 100, Store.width / 2, Store.height / 2);
        push();
        fill(100);
        text(x1.toString(), windowWidth/2 - 35, windowHeight/2 + 50);
        pop();

        image(walkout, 590, windowHeight/2 - 8, 20, 30);
        if (keyIsDown(RIGHT_ARROW)) {
            x1 += 10;
        }
    }

    if (x1 >= totalMiles) {
        background(255);
        //image(Store, 500, windowHeight/2 - 100, Store.width / 2, Store.height / 2);
        image(broken, 590, windowHeight/2 - 11, 20, 30);
        push();
        fill(100);
        text("This person has traveled " + totalMiles.toString() + " miles.", windowWidth/2 - 150, windowHeight/2 + 50);
        text("Their phone lasted " + years.toString() + " years.", windowWidth/2 - 150, windowHeight/2 + 65);
        text("What makes them fail and\nwhy do companies design it that way?", windowWidth/2 - 150, windowHeight/2 + 95);
        pop();

        ender += 1;
    }

    //Landscape Line
    push();
    strokeWeight(3);
    line(0, windowHeight/2 + 20, windowWidth, windowHeight/2 + 20);
    pop();

    //Borders
    push();
    noStroke();
    fill(255);
    rect(0,0, windowWidth/2 - 200, windowHeight);
    pop();

    push();
    noStroke();
    fill(255);
    rect(windowWidth/2 + 200,0, windowWidth, windowHeight);
    pop();

    if (ender > 450) { //End scene and move to parts of a phone.
        background(255);
        //image(Store, 500, windowHeight/2 - 100, Store.width / 2, Store.height / 2);
        image(iphone, windowWidth/2 - 150, windowHeight/2 - 200, 300,400);
        image(iphoneInside, windowWidth/2 + 50, windowHeight/2 - 200, 300,400);
        image(battery, windowWidth/4, windowHeight/2 - 200, 300,400);


        push();
        noStroke();
        fill(255);
        rect(windowWidth/2 + 100, windowHeight/2 - 60, 110,250);
        pop();

        if (mouseX >= windowWidth/4 + 70 & mouseX <= windowWidth/4 + 70 + 115) {
            push();
            noFill();
            stroke(255,0,0);
            strokeWeight(4);
            rect(windowWidth/4 + 70, windowHeight/2 - 95, 115,270);
            pop();

            text("Lithium-Ion Battery | Lifespan: 3-5 years\nOne of the most common areas of failure.\nThese also contain many of toxic substances that make it difficult to recycle.", windowWidth/2 - 250, windowHeight/2 +200);
        }

        if (mouseX >= windowWidth/2 - 90 & mouseX <= windowWidth/2 + 90) {
            push();
            noFill();
            stroke(0,255,0);
            strokeWeight(4);
            rect(windowWidth/2 - 90, windowHeight/2 - 180, 180, 360);
            pop();

            text("Screen / Outer Hardware | Lifespan: Varies\nThe most common failure point.\nNowadays, if a screen is cracked, its often cheaper to buy a new phone.", windowWidth/2 - 250, windowHeight/2 +200);
        }

        if (mouseX >= windowWidth/2 + 120 & mouseX <= windowWidth/2 + 160 + 115) {
            push();
            noFill();
            stroke(0, 0, 255);
            strokeWeight(4);
            rect(windowWidth/2 + 120, windowHeight/2 - 180, 160, 360);
            pop();

            text("Circuitboard / Chips | Obsolete: 2 years\nNot common to fail, in fact chips are designed to work for 20-30 years.\nHowever, the technology quickly becomes obsolete due to newer, more powerful chipsets being developed.", windowWidth/2 - 250, windowHeight/2 +200);
        }
    }
    
    timer += 1; //increment timer per call of draw.
}

function keyPressed() { //Detect right arrow key press.
    if (keyCode === RIGHT_ARROW) {
        x1 += 1;
  }
}

function numberGen() { //Generate numbers.
    let years1 = random(1, 4);
    years = nf(years1, 1, 2);
    miles1 = int(random(1300, 1500));
    miles = nf(miles1, 4, 2);

    totalMile1 = (miles * years);
    totalMiles = nf(totalMile1, 4, 2);
}

function mousePressed() { //Detect mouse press. 
    return true;
}

For my project, I wanted to highlight the issue of planned obsolescence in our everyday electronics through a simple interactive “scene”. Basically I wanted to show the briefness with which our phones are usable relate it to approximately how many miles we could walk before they utterly fail or become obsolete. At the end, the scene changes and shows some of the places where a phone is designed to fail in 2-3 years.

This project was made to fit the full view width of a window so it might not be completely viewable on wp.

Final_Project copy

Video Link: https://youtu.be/2isZoRcILBk

Final Project — Yoshi Torralva

sketch

//Yoshi Torralva
//yrt@andrew.cmu.edu
//Final Project 
//Section E

//storefront mockup
var mockup;
//input of camera variable
var cameraData;
//points for the hypotrochoid
var pixelPoints = 1000;
//location of hypotrochoid x position array
var placeCurvesX = [];
//location of hypotrochoid y position array
var placeCurvesY = [];
// array to store list of colors at 16 points
var getColors = [];
// array to store brightness of pixels at 16 points
var getBrightness =[];
// varible to move the 16 hypotrochoid's in indivudal rotation
var hypotrochoidsRotate = -1;
// scaled canvas for the interaction in the mockup
var SquareW = 154;

//lading the storefront mockup image
//vector image created by me with Adobe Illustrator 
function preload() {
    mockup = loadImage("https://i.imgur.com/pTMuI2T.png");
}
//connecting cameraData variable to createCapture
function setup() {
    createCanvas(600, 480);
    cameraData = createCapture(VIDEO);
    cameraData.hide();
    noStroke();
}

function draw() {
    background(172, 34, 104);
    //loading pixels into the canvas
    cameraData.loadPixels();
    //reflecting and scaling the camera data
    //when visitors move to the right/left, it properly reflects  movements
    push();
    translate(150, 0);
    scale(-1, 1);
    //sizing cameraData to the screen
    image(cameraData, 0,0, 154, 154);
    pop();
    //calling the hypotrochoids function to draw 16 hypotrochoids
    hypotrochoids(); 
    //scaling the image from imgur
    //placing it above hypotrochoids so that hypotrochoids dont go over the screen
    push();
    scale(0.24);
    image(mockup, 0, 0);
    pop();
}
//creating a function to draw a singular hypotrochoid
//setting four parameters for x position, y position, the curves color at one of the 16 points
//and the brightness of one of the 16 points
function drawCurve(x,y, curveColor, curveBrightness) {
    push();
    //translating with x and y for call parameters
    translate(x, y);
    //using the brightness in the map() of h and a to adjust size based on camera
    var h = map(curveBrightness - 100, 0, height, 0, curveBrightness + 100);
    var a = map(curveBrightness - 50, 0 , width, 0, curveBrightness + 50);
    //adding curveBrightness with 50 for more detail
    curveBrightness = curveBrightness + 50;
    var b = a / curveBrightness;
    //a small strokeWeight for clarity between curveBrightness
    strokeWeight(0.05);
    noFill();
    //using the curveColor parameter 
    //adding red and blue to display the hypotrochoids as pink/red
    stroke(curveColor + 200, 0, curveColor + 30);
    //calling all the variables in a nested for loop to draw the hypotrochoid
    beginShape();
    // adding 5 extra points to close the hypotrochoid
    for(var i = 0; i < pixelPoints + 5; i++) {
        var t = map(i, 0, pixelPoints, 0, TWO_PI); {
        x = (a-b) * cos(t) + h * cos(((a-b)/ b) * t)
        y = (a-b) * sin(t) - h * sin(((a-b) / b) * t)
        rotate(hypotrochoidsRotate);
        //slight rotation implemented
        hypotrochoidsRotate = hypotrochoidsRotate + 0.0001;
        //scale of hypotrochoids set to normal
        vertex(x, y);
    }
}
    endShape();
    pop();
}
// using nested for loops to place replicate hypotrochoid into 16 hypotrochoids
function hypotrochoids(){
//translating hypotrochoids function to fit into the square screen
push();
translate(302, 185);
    //4 in the width of the screen
    for(var i = 0; i < 4; i++) {
        //4 in the height of the screen
        for(var t = 0; t < 4; t++) {
            //locationofLoopi and LocationofLoopt space out the hypotrochoids
            var locationofLoopi = i * SquareW/4 + 10;
            var locationofLoopt = t * SquareW/4 + 10;
            //getting the color of locationofLoopi and LocationofLoop 2
            var colorLoop = get(locationofLoopi, locationofLoopt);
            //getting the brightness of the colors
            var sizeLoop = brightness(colorLoop);
            rectMode(CENTER);
            //implementing spacing into 16 hypotrochoids
            drawCurve(locationofLoopi, locationofLoopt, sizeLoop + 20, sizeLoop + 20);
            //pushing list into the arrays of location, color, and brightness of hypotrochoids
            placeCurvesX.push(locationofLoopi);
            placeCurvesY.push(locationofLoopt);
            getColors.push(colorLoop);
            getBrightness.push(sizeLoop);
        }
    }
//loop to call the length of the 16 points and implement the parameters from draw curve
    for(var a = 0; a < locationofLoopi.length; a++) {
        drawCurve(locationofLoopi[a], locationofLoopt[a], getColors[a], getBrightness[a]);
    }
pop();
}

For my final project, I wanted to explore the opportunities in which I can apply computational practices into a physical space. I decided to focus on creating a reactive storefront using a camera to show the movement of visitors in a visually intriguing way. To inspire a creative direction for the storefront, I created an Athletic Clothing brand called ové. Ové derives from the french word of l’oeuvre meaning work. Additionally, Ové is the shared characters of Move and Woven. To make the hypotrochoid curves reactive to the camera’s data, I used 16 specific points of pixel brightness and color. Through this, I create an interactive storefront that reacts to traffic and ultimately would entice people to enter the store.

Gretchen Kupferschmid & Sarah Choi – Final Project: Pittsburgh Ambience

final project: pittsburgh ambience

Our project’s canvas is too big for wordpress, so we are uploading the zip file and instructions to acess it. 

Click the link to the google drive to find the zip file. Download the zip file to find the sketch.js file “finalproject.js”  as well as the html file “finalproject.html” and the sound files. Because the project is outside of wordpress and there is a sound aspect, the project needs to be opened on the local host from your server, so the instructions must be followed bellow…

  1.  Open a Terminal in OS X or a command window (cmd) in Windows.
  2. Change your current directory to the directory you want to serve: Type cd path-to-your-directory (ex. cd Desktop/104final )
  3. Type in Terminal:
    python -m SimpleHTTPServerOr if you are using Python 3, type:
    python -m http.server
  4. Visit the URL http://localhost:8000 in your browser to test your sketch.

How it works:

Using your mouse, scroll through the four different neighborhoods presented in illustrations. 

Click on the music button to hear the song we chose to represent the ambience of the area and click on the pause button to stop the song.

Click on the different buttons for the cafes and restaurants shown to see a photo of the location, a description, and a link to the website. 

For our final project, we wanted to portray the distinct atmospheres of different neighborhoods in Pittsburgh. As two students who weren’t familiar with Pittsburgh before Carnegie Mellon, we took the initiative to venture out and explore the different areas and what Pittsburgh has to offer. As people who are interested in immersing and inspiring ourselves through our surroundings, we wanted to introduce our favorite places to other students. Because music is also a very big part of our everyday lives, we wanted to complete the Pittsburgh ambiance by selecting specific songs that we felt captured in each area the best. 

We really enjoyed the interactive visual aspect of this project. In the beginning, we brainstormed ways to depict our shared interests and goals through a project, and we felt that this achieved exactly what we envisioned. Making 2D objects into an interactive platform, we saw the potential to incorporate layers of our project into future uses. Our favorite part of the project was probably the music and design aspect of it. We had a lot of fun planning and collaborating together.  Below, we have attached a video and screenshots of each different area included in our project.

Jina Lee – Final Project


Here is a video to see how the maze works. 

Word Press doesn’t show you the camera screen so it is difficult to navigate the dots because of the file being so big.  Please download this html to play the maze with the camera if you want to see where your tracker is: jinal2-final project. It works best on Chrome. Thank you!

What is it? I know that we did not have much experience with using our webcam into our code, but I really wanted to implement an interactive game that used it. For my project, I wanted to create a camera censored maze game that was moved through your pupils. Before creating the final result, I researched how to sensor your pupils so that the dots follow wherever you look when you are playing the game. However, that was way too ambitious as I struggled to understand how to implement that concept into my code. In the end, I made the camera sensor for any objects that are red so that it will create dots on them.

Due to me not being able to make the game restart when you hit the maze lines, I made the dots go back to the start position when the red tracker is out of the webcam frame. I know that this class is over now, but there are things that I would like to add to my project.  Because it took so much longer to try to get the pupil sensors to work, I did not get to work more on other things that I wanted to get to. First, I want to make an “ERROR” sign when the dots hit the white lines of the maze. In addition, I would want to create an intro that would help people calibrate their red object with the screen so that it is easier to control. I had a lot of fun with this project while also pushing myself to try to create something that was way out of my comfort zone.

sketch

// Jina Lee
// jinal2@andrew.cmu.edu
// Section E
// 15-104 Final Project

// This codes uses the webcam to create red dots that will
// follow any red color on the camera.

var myCaptureDevice;
// This array stores the x value of the red positions
var xarray = [];
// This array stores the y value of the red positions
var yarray = [];
// Limits of x and y arrays
var sizeLimit = 10;
var redCircle = 1;

function setup() {
    createCanvas(500, 500);
    // Camera
    myCaptureDevice = createCapture(VIDEO);
    myCaptureDevice.size(500, 500);
}

function isColor(c) {
    // Tests the colors
    return (c instanceof Array);
}


function draw() {
    // Background
    fill(0);
    rect(0, 0, 500, 500);
    // Maze
    stroke(255);
    strokeWeight(10);
    line(250, 250, 250, 320);
    line(250, 250, 250, 320);
    line(250, 320, 320, 320);
    line(320, 320, 320, 190);
    line(320, 320, 320, 180);
    line(320, 180, 180, 180);
    line(180, 180, 180, 370);
    line(180, 370, 380, 370);
    line(380, 370, 380, 130);
    line(380, 130, 135, 130);
    line(135, 130, 135, 420);
    line(135, 420, 430, 420);
    line(430, 420, 430, 70);
    line(430, 70, 70, 70);
    //boxes
    fill(255);
    noStroke();
    rect(65, 65, 50, 50, 10);
    rect(-5, 150, 100, 100, 10);
    rect(60, 300, 80, 80, 10);
    rect(-10, 430, 70, 70, 10);
    // Starting Spot
    fill(255);
    rect(225, 230, 50, 50, 10);
    textSize(10);
    textFont('Helvetica');
    fill('green');
    text("START", 235, 260);
    // Exit Spot
    fill(255);
    rect(440, 470, 50, 50, 10);
    textSize(10);
    textFont('Helvetica');
    fill('red');
    text("END", 455, 495);

    // Red dots are created because of camera sensor
    var xMin = 0;
    var xMax = 500;
    var yMin = 0;
    var yMax = 500;
    // For loop that draws the dots
    for (var a = 0; a < xarray.length; a++){
        // Ellipse
        if (redCircle === 1){
            noStroke();
            fill('red');
            ellipse(xarray[a], yarray[a], 5, 5);
        }
    }
    for (var i = 0; i < width; i += 5){
        for (var q = 0; q < height; q += 5) {
            var currentColor = myCaptureDevice.get(i, q);
            var r = red(currentColor) - 255;
            var g = green(currentColor) - 0;
            var b =  blue(currentColor) - 0;
            // If rbg matches criteria
            // than get the center point of the red area
            // and put it in an array
            if (isColor(currentColor)){
                var dist = sqrt(sq(r) + sq(g) + sq(b));
                if (dist < 120) {
                    // Finds the location of the red areas on the web cam
                    // Finds the smallest x location
                    if (i > xMin){
                        xMin = i;
                    }
                    // Find the biggest x location
                    if (i < xMax){
                        xMax = i;
                    }
                    // Finds the smallest y loctaion
                    if (q > yMin){
                        yMin = q;
                    }
                    // Finds the biggest y location
                    if (q < yMax){
                        yMax = q;
                    }
                }
    		    }
    	  }
    }
    // Push the new x of red from camera into the array
    xarray.push((xMin + xMax) / 1.9);
    // Push the new y of red from camera into the array
    yarray.push((yMin + yMax) / 1.7);
    // When array is full, it will pop something out from the beginning
    while (xarray.length > sizeLimit) {
        xarray.shift();
        yarray.shift();
    }
}

Final Project – Alice Cai

sketch

//Alice Cai
//Section E
//alcai@andrew.cmu.edu
//Final Project

//global variables for letter A
//starting point of angles for turning
var angleA = 0; 
var angleA2 = 0;
//starting velocity of turning
var velA2 = -1;
//starting location
var y = 0; 
//color variables
var r;
var g;
var b;

//global variables for letter B
var angleC = 70;
var angleC2 = 90;
var cVel1 = 0;
var cVel2 = 0;
//varying starting sizes of circles
var size1 = 1;
var size = 0;
var rC;
var gC;
var bC;

function setup() {
  createCanvas(600, 300);
  frameRate(10); //slow down framerate
}

function draw() {
    //background is random color
    background(g, r, r);
    //constrain black circle to height of canvas
    let cc = constrain(mouseX, 0, height);
    //keep ellipse concentric to either center of A or the entire A
    if (mouseX > width / 5) {
        var cy = height / 2;
    } else {
        cy = height / 2 + 30;
    }
    fill(0);
    ellipse(width /4, cy, cc,cc);

    //draw A cap/serif. Made up of  a varying amount of circles controlled by mouseX
    for (var i = 0; i <= mouseX; i ++) {
        noStroke();
        push();
        translate(width /4, height / 2 - 110 + 75);
        rotate(radians(angleA2));
        fill(r, b, g);
        ellipse(50, y, 30, 30);
        pop();
        angleA2 = angleA2 + velA2;

    //constrain rotating movment of ellipses to the shape of a 
    if (angleA2 < -135 ) {
        velA2 = velA2 * -1;
    }

    //if surpasses "semicircle", stop rotating and move downwards.
    if (angleA2  >= 0 & velA2 === 1) {
        angleA2 = 0;
        y = y + velA2;
    }

    //if hits bottom, come back up
    if (y > 115) {
        y = y - 115;
        velA2 = velA2 * -1;
        angleA2 = angleA2 + velA2;
    }
  }

    //draw circle made up of more circles, quantity/length controled by mouseX
    for (var i = 0; i <= mouseX / 2; i ++) {
        noStroke();
        push();
        translate(width / 4, height / 2 + 30);
        rotate(radians(angleA));
        let xc = constrain(mouseX, 0, 50); //constrain diameter of circle
        fill(r, b, g);
        ellipse(xc, 0, 30, 30);
        pop();
        angleA = angleA - i * 0.8;
    }

    //draw C, made up of circles
    for (var i = 0; i <= 45; i ++) {
        noStroke();
        push();
        translate(width / 4 * 3, height / 2);
        //C is constantly roatating
        rotate(radians(angleC + i * 5));
        //constrain size of C based on mouseX
        let xc = constrain(mouseX / 4, 0 , width / 8);
        fill(bC, rC, gC);
        ellipse(xc, 0, 30, 30);
        angleC = angleC - cVel1;
        pop();
    }

    //constrain rotation of C to go back and forth and not become a circular motion
    if (angleC2 < 90) {
        cVel1 = cVel1 * -1;
    }

    if (angleC2 > 0) {
        cVel1 = cVel1 * -1;
    }

    //create two rings of circles around and inside C
    let numb = constrain(mouseX, 0, 50)
    for (var a = 0; a <= numb / 5; a ++) {
        noStroke();
        push();
        translate(width / 4 * 3, height / 2);
        rotate(radians(a * 25 + angleC2 ));
        fill(rC, bC , gC );
        size1 = size1 + cVel1
        let xc = constrain(size1, 0, width / 8)
        var size = 5;
        //ellipse size varies with sin and cosine to create explosive firework effect when clicked
        ellipse(xc - 30, 0,  size + xc / 3 * cos(xc) / 2,  size + xc / 3 * cos(xc) / 2);
        angleC2 = angleC2 + cVel1;
        pop();
    }

    for (var a = 0; a <= 20; a ++) {
        noStroke();
        push();
        translate(width / 4 * 3, height / 2);
        rotate(radians(a * 20 + angleC2 - 30));
        fill(bC, gC, rC);
        size1 = size1 + cVel2
        let xc = constrain(size1, 0, width / 5)
        var size = 5;
        ellipse(xc, 0,  size + xc / 3 * sin(xc),  size + xc / 3 * sin(xc));
        angleC = angleC - cVel2 ;
        pop();
    }
}

function mousePressed() {
    loop(); //starts loop
    r = random(100, 255); // r is a random number between 100 - 255
    g = random(100, 255); // g is a random number betwen 100-255
    b = random(100, 255); //b is a random number betwen 100 - 255

    //set velocity and size for movement
    size1 = 1
    cVel2 = 1
    cVel1 = 0.5
    rC = random(100, 255);
    gC = random(30, 200);
    bC = random(100, 255);
}

function mouseReleased() {
    noLoop(); //freezes loop
    //set velocity and sizes to 0
    size1 = 0
    cVel2 = 0
    cVel1 = 0
    angleC = 70 //set C angle to standard
}

This is a fun animation of my initials AC; I wanted to get more into creating motion graphics and decided it would be useful to begin brainstorming animations for a logo or graphic for my website. Interact by clicking and releasing, as well as dragging your mouse across the page. Colors change every click. The motif of circles runs throughout the construction of each letter; every line is drawn with ellipses. Thus, at any frame of the animation, the composition is somewhat unified.

Angela Lee – Final Project

Instructions: Please expand your browser window as much as possible since my canvas is at the width of 600! Thank you 🙂
To interact with my speculative comic panel, here are a couple of interactions you can try.
To speed up the subway car, press the “f” key.
To slow down the subway car, press the “s” key. (Note: you cannot make the subway go backwards. If you press this key too many times, it will reset to its original speed.)
To reset the subway car speed, press the “r” key.
In frames 1 & 2, click your mouse around to see some falling stars.

sketch

/*
 * Angela Lee
 * Section E
 * ahl2@andrew.cmu.edu
 * Final Project
 */

// FRAME 1 VARIABLES
var f1height = 270; // height of first frame
var f1width = 275; // width of first frame
var f1x = 22.5; // upper left x-pos of first frame
var f1y = 12.5; // upper left y-pos of first frame
var f1spacing = 9; // spacing to the right of the first frame
var accelerate = 0; // how much the subway car speeds up/slows down
var skyBlue; // top of the sky color
var skyYellow; // bottom of the sky color
var waterYellow; // top of the water color
var waterBlue; // bottom of the water color
var shootingStarArray = []; // array for shooting stars
var ripples = []; // array for the ripples

// FRAME 2 VARIABLES
var f2width = 270; // width of second frame
var f2height = 130; // height of second frame
var f2spacing = 11.5; // spacing below the second frame
var planetx = 400; // x position of the big planet
var subwayStarArray = []; // array for subway stars


function setup(){
	createCanvas (600, 300);
    background(27, 34, 45);

    // gradient for the sky in frame 1
    skyBlue = color(2, 76, 98); // top of the sky color
    skyYellow = color(233, 255, 191); // bottom of the sky color

    // gradient for the water in frame 1
    waterYellow = color(252, 239, 194); // top of the water color
    waterBlue = color(59, 98, 115); // bottom of the water color

    sub = makeSubway(); // creates a subway object in frame 1

    // RIPPLES IN FRAME 1
    // boundaries for ripples in frame 1
    var top = 212; // top boundary
    var bottom = height; // bottom boundary
    // first ripples that come into frame 1
    for (var i = 0; i < 10; i++) {
        var rippleX = random(width);
        var rippleY = random(top, bottom);
        ripples[i] = makeRipples(rippleX, rippleY);
    }
}

function draw(){
    // FRAME 1
    f1scene(); // the sky
    // moving the subway
    sub.move();
    sub.draw();
    // moving the ripples
    for (var i = 0; i < ripples.length; i++) {
        ripples[i].move();
        ripples[i].draw();
    }
    removeRipple();
    addRipple(); 


    // FRAME 2
    f2scene(); // background in frame 2
    // moving the shooting stars
    for (var i = 0; i < shootingStarArray.length; i++) {
        shootingStarArray[i].move();
        shootingStarArray[i].draw();
    }
    removeShootingStar(); // keeps shooting stars that are in frame
    addShootingStar(); // adds shooting stars when mouse is clicked
    f2car(); // subway car features in frame 2
    astroboy(); // astronaut boy in frame 2


    // FRAME 3
    f3scene(); // background in frame 3


    // TEXT
    // words in frame 1
    var f1text = "every night i take the subway home..."
    // words in frame 2
    var f2text = "i wonder..."
    // words in frame 3
    var f3text = "if our worlds will ever collide again."
    noStroke();
    fill("white");
    textSize(14);
    textStyle(ITALIC);
    text(f1text, f1x + 10, 279); // frame 1 text
    text(f2text, 490, 87); // frame 2 text
    text(f3text, f1x + f1width + 25, 279); // frame 3 text


    // BORDERS AROUND THE FRAMES
    noStroke();
    fill(27, 34, 45);
    rect(0, 0, width, f1y); // top border
    rect(0, 0, f1x, height); // border left of frame 1
    rect(f1x + f1width, 0, 17, height); // border right of frame 1
    rect(0, 0, f1x + f1width, f1y); // border above frame 1
    // border below frame 1
    rect(0, f1y + f1height, width, width - f1y - f1height);
    // border below frame 2
    rect(f1x + f1width, f1y + f2height, width - f1x - f1width, f2spacing);
    // border right of frame 2
    rect(f1x + f1width + f1spacing + f2width, 0, 
        width - f1x + f1width + f1spacing + f2width, height);
}

// ---------------- FUNCTIONS & OBJECTS FOR FRAME 1 ------------------

// FRAME 1 BACKGROUND
function f1scene(){
    // GRADIENT SKY
    noFill();
    for (var s = f1y; s < f1y + f1height; s++) {
        var skyInter = map(s, f1y, f1y + f1height, 0, 1);
        var skyStroke = lerpColor(skyBlue, skyYellow, skyInter);
        stroke(skyStroke);
        line(f1x, s, f1x + f1width, s);
    } 
    planets(); // planets in the sky
    surface(); // surface of the moon
    clouds(); // clouds in the sky
    constellations(); // constellations in the sky 

    // TWINKLING STARS
    fill(242, 242, 158);
    for (var st = 0; st < 8; st++) {
        // x positions for the twinkling stars
        starX = [32, 77, 129, 177, 190, 205, 242, 276];
        // y positions for the twinkling stars
        starY = [91, 66, 63, 27, 79, 53, 25, 90];
        // sizes for the twinkling stars
        starSize = [5, 4, 4, 5, 4, 5, 4, 5];
        star(starX[st], starY[st], 
             starSize[st] - random(1, 3), starSize[st], 5);
    }

    // SUBWAY TRACK
    stroke(79, 94, 94);
    fill(130, 142, 134);
    // pillars holding the track
    for (var i = 0; i < 6; i++) {
        rect(34 + i * 48, 148, 14, 67);
    }
    rect(0, 148, f1x + f1width, 13); // bridge above the water

    // GRADIENT WATER
    noFill();
    for (var w = 212; w < f1y + f1height; w++) {
        var waterInter = map(w, 212, f1y + f1height, 0, 1);
        var waterStroke = lerpColor(waterYellow, waterBlue, waterInter);
        stroke(waterStroke);
        line(f1x - 1, w, f1x + f1width, w);
    }
}

// CONSTELLATIONS iN FRAME 1
function constellations() {
    stroke(131, 178, 177); 
    strokeWeight(1);

    // LIBRA CONSTELLATION
    // the following lines join together to create libra
    line(98.5, 90.5, 98.5, 84.5);
    line(98.5, 84.5, 89.5, 70.5);
    line(89.5, 70.5, 111.5, 60.5);
    line(111.5, 60.5, 112.5, 74.5);
    line(89.5, 70.5, 96.5, 56.5);
    line(96.5, 56.5, 111.5, 60.5);

    // LEO CONSTELLATION
    // the following lines join together to create leo
    line(146, 84, 142, 75);
    line(142, 75, 154, 79);
    line(154, 79, 146, 84);
    line(146, 84, 148, 97);
    line(148, 97, 156, 95);
    line(156, 95, 156, 89);
    line(156, 89, 161, 88);
    line(161, 88, 172, 95);
    line(172, 95, 169, 100);

    // AQUARIUS CONSTELLATION
    // the following lines join together to create aquarius
    line(258, 49, 241, 63);
    line(241, 63, 246, 65);
    line(246, 65, 254, 63);
    line(241, 63, 241, 67);
    line(241, 67, 239, 69);
    line(239, 69, 247, 83);
    line(247, 83, 249.5, 77.5);
    line(249.5, 77.5, 258, 76);
    line(258, 76, 268, 82);

}

// CLOUDS IN FRAME 1
function clouds() {
    noStroke();

    // YELLOW CLOUDS
    fill(210, 216, 130, 70);
    var ycloudx = [92, 67, 103, 117, 122, 159]; // x coordinates
    var ycloudy = [92, 88, 53, 56, 52, 70]; // y coordinates
    var ycloudw = [56, 48, 27, 35, 19, 25]; // cloud width
    var ycloudh = [9, 8, 5, 4, 3.5, 3]; // cloud height
    // for loop generates 6 yellow clouds
    for (var y = 0; y < 6; y++) {
        ellipse(ycloudx[y], ycloudy[y], ycloudw[y], ycloudh[y]);
    }

    // PINK CLOUDS
    fill(242, 136, 158, 150);
    var pcloudx = [251, 65, 48, 183, 196, 212]; // x coordinates
    var pcloudy = [109, 58, 55, 43, 46, 41]; // y coordinates
    var pcloudw = [80, 30, 25, 35, 31, 29]; // cloud width
    var pcloudh = [12, 7, 3, 4, 7, 9]; // cloud height
    // for loop generates 6 pink clouds
    for (var p = 0; p < 6; p++) {
        ellipse(pcloudx[p], pcloudy[p], pcloudw[p], pcloudh[p]);
    }
}

// SURFACE OF THE MOON IN FRAME 1
function surface() {
    strokeWeight(1);
    stroke(31, 73, 72);
    fill(38, 104, 119);

    // x positions of the moon surface's shape
    var moonx = [f1x, 61, 107, 152, 202, 248, f1x + f1width, 
                 f1x + f1width, f1x];
    // y positions of the moon surface's shape
    var moony = [187, 185, 191, 180, 188, 190, 195, 237, 237];

    // SURFACE OF THE MOON
    beginShape(); 
    vertex(moonx[0], moony[0]);
    // for loop for curved parts of the shape
    for (var m = 0; m < 7; m++) {
        curveVertex(moonx[m], moony[m]);
    }
    // straight parts of the moon surface shape
    // these points can't be seen because they are
    // blocked by the water
    vertex(moonx[7], moony[7]);
    vertex(moonx[8], moony[8]);
    vertex(moonx[0], moony[0]);
    endShape();

    // MOON CRATERS
    noStroke();
    fill(22, 81, 91);
    ellipse(58, 192, 29, 6);
    ellipse(96, 202, 19, 3);
    ellipse(146, 188, 19, 4);
    ellipse(201, 201, 20, 3);
    ellipse(241, 200, 34, 5);
}

// PLANETS IN THE SKY OF FRAME 1
function planets () {
    // LARGEST PLANET
    strokeWeight(2);
    stroke(30, 76, 114);
    fill(32, 111, 142);
    ellipse(217, 164, 142, 142);

    // SECOND LARGEST PLANET
    strokeWeight(1);
    stroke(24, 76, 119);
    fill(18, 114, 130, 95);
    ellipse(95, 119, 75, 75);

    // RINGED PLANET
    noStroke();
    fill(234, 106, 124);
    ellipse(63.5, 45, 51, 12); // rings
    strokeWeight(0.5);
    stroke(140, 65, 83);
    fill(242, 136, 158);
    ellipse(64, 44, 25, 25); // planet itself

    // CRESCENT MOON
    strokeWeight(1);
    stroke(234, 96, 119);
    fill(242, 242, 158);
    ellipse(144.8, 40, 30, 30); // crescent part
    noStroke();
    fill(31, 94, 104);
    ellipse(143, 37, 24, 24); // shadow part

}

// MAKING THE STARS IN FRAME 1 + 3
// called in f1scene() and f3scene()
function star(x, y, radius1, radius2, npoints) {
    var angle = TWO_PI / npoints;
    var halfAngle = angle / 2.0;
    beginShape();
    for (let a = 0; a < TWO_PI; a += angle) {
        var sx = x + cos(a) * radius2;
        var sy = y + sin(a) * radius2;
        vertex(sx, sy);
        sx = x + cos(a + halfAngle) * radius1;
        sy = y + sin(a + halfAngle) * radius1;
        vertex(sx, sy);
    }
  endShape(CLOSE);
}

// ------------- FUNCTIONS & OBJECTS FOR FRAME 2 -------------------

// FRAME 2 BACKGROUND
function f2scene() {
    // WINDOW BEHIND THE RIDER
    strokeWeight(4);
    stroke(255);
    fill(9, 74, 89);
    rect(302, 29, 291, 85);

    // NIGHTTIME SCENERY
    // stars that come into frame when the subway is "moving"
    noStroke();
    fill(242, 242, 158);
    for (var i = 0; i < subwayStarArray.length; i++) {
        subwayStarArray[i].move();
        subwayStarArray[i].draw();
    }
    removeSubwayStar(); // keeps stars in the keepSubwayStar array
    addSubwayStar(); // based on a small probability, adds new stars

    // big planet
    strokeWeight(2);
    stroke(30, 76, 114);
    fill(32, 111, 142);
    ellipse(planetx, 100, 130, 130); 
    planetx += 0.05 // planet slowly moves right as time passes

    // MORE WINDOW FEATURES
    // glass dividers on the subway windows
    strokeWeight(3);
    stroke(255);
    var divideh = 24; // height of vertical dividers
    var dividex = 335; // initial x position of vertical dividers
    var dividey = 32; // y position of all vertical dividers
    var dividespace = 70; // horizontal spacing in between
    // 4 vertical dividers are created through lines
    for (var d = 0; d < 4; d++) {
        line(dividex + dividespace * d, dividey,
             dividex + dividespace * d, dividey + divideh);
    }
    // horizontal line beneath the vertical dividers
    line(302, 32 + divideh, 302 + f2width, 32 + divideh);

    // window reflections
    noStroke();
    fill(253, 255, 255, 40);
    // first reflection shape
    beginShape();
    vertex(330, 112);
    vertex(326, 95); 
    vertex(381, 31);
    vertex(399, 31);
    vertex(330, 112);
    endShape();
    // second reflection shape
    beginShape();
    vertex(355, 95);
    vertex(409, 31);
    vertex(418, 31);
    vertex(364, 95);
    vertex(355, 95);
    endShape();
    // third reflection shape
    beginShape(); 
    vertex(423, 98);
    vertex(472, 30);
    vertex(509, 30);
    vertex(465, 98);
    vertex(423, 98);
    endShape();
    // fourth reflection shpae
    beginShape();
    vertex(512, 102);
    vertex(555, 31);
    vertex(572, 31);
    vertex(531, 102);
    vertex(512, 102);
    endShape();
}

// FEATURES OF THE SUBWAY CAR IN FRONT OF THE WINDOW
function f2car() {
    // subway car wall above the window
    noStroke();
    fill(143, 169, 183);
    rect(f1x + f1width + 10, 0, f2width, 29);
    // subway car wall below the window
    rect(f1x + f1width + 10, 114, f2width, 40);

    // subway chairs
    // back of the chair that the rider leans against
    stroke(73, 122, 121);
    strokeWeight(1);
    fill(174, 199, 206);
    rect(320, 92, 247, 77, 15); 

    // indents in the chair 
    noStroke();
    fill(153, 181, 188); 
    var indentx = 333; // initial x positions of indents
    var indenty = 103; // y positions of indents
    var indentw = 66; // width of indents
    var indenth = 65; // height of indents
    var indentround = 15; // roundness of indents
    var indentspacing = 79; // spacing between each indent
    // for loop creates 3 evenly spaced indents in the chair 
    for (var c = 0; c < 3; c++) {
        rect(indentx + indentspacing * c, indenty, indentw, 
             indenth, indentround);
    }

    // handles above the window
    noFill();
    strokeWeight(3);
    stroke(200, 214, 219);
    var handlespacing = 82; // horizontal spacing between handles
    // for loop creates 3 evenly spaced handles
    for (var h = 0; h < 3; h++) {
        ellipse(360 + handlespacing * h, 20, 10, 40);
    }
}

// ASTRONAUT BOY SITTING IN THE SUBWAY
function astroboy() {
    // arms
    noStroke();
    fill(160, 116, 87);
    rect(397, 137, 13, 15); // right arm

    // shirt
    fill(194, 160, 224);
    ellipse(379, 118.5, 66, 19); // shoulders
    rect(356, 117, 47, 35); // body
    rect(346, 119, 15, 19); // left sleeve
    ellipse(354, 137.5, 16, 5); // bottom of left sleeve
    rect(400, 119, 12, 19); // right sleeve
    ellipse(404, 137.5, 16, 5); // bottom of right sleeve
    // pocket on shirt
    fill(234, 132, 132); 
    rect(385, 130, 11, 13); 
    ellipse(390, 143, 11, 3);

    // right arm
    noStroke();
    fill(160, 116, 87);
    quad(346, 138, 360, 138, 366, 150, 352, 150); // left arm

    // astronaut helmet
    // neck piece
    noStroke();
    fill(100);
    ellipse(377, 111, 27, 6);
    rect(364, 100, 27, 11);
    // helmet
    strokeWeight(1);
    stroke(184, 209, 208);
    fill("white");
    ellipse(377, 83, 48, 48);
    // glass piece
    noStroke();
    fill(33, 51, 68);
    ellipse(376, 95.5, 34, 11);
    quad(357, 85, 395, 85, 393, 96, 359, 96);
    ellipse(376, 85, 38, 11);
    // highlights on glass piece
    fill(100, 107, 109);
    ellipse(371, 84, 22, 2); // top highlight
    ellipse(376, 98, 18, 1); // bottom highlight
}

// ------------------ FUNCTIONS/OBJECTS IN FRAME 3 ---------------------

// FRAME 3 BACKGROUND
function f3scene() {
    // the dark night sky
    noStroke();
    fill(18, 38, 76);
    rect(307, 12 + f2height + f2spacing, f2width, f2height); 

    // clouds
    // darkest clouds
    fill(39, 49, 117, 170);
    ellipse(382, 193, 69, 48);
    ellipse(534, 201, 95, 47);
    // purple clouds
    fill(118, 86, 153, 120);
    ellipse(348, 230, 107, 71);
    ellipse(445, 220, 142, 77);
    ellipse(531, 225, 98, 52);
    // darker pink clouds
    fill(219, 127, 154, 130);
    ellipse(374, 247, 76, 50);
    ellipse(520, 251, 135, 50);

    // glow behind the star in figure's hand
    noStroke();
    var glowx = 491; // x position of the glow center
    var glowy = 216; // y position of the glow center 
    // controls how the glowing portion of the star 
    // grows and shrinks, so it looks like it's flickering
    var osc = 10 + sin(millis()) * 1;
    // outer glow
    fill(239, 125, 172, 100);
    ellipse(glowx, glowy, 20 + osc, 20 + osc);
    // middle glow
    fill(249, 155, 189, 120);
    ellipse(glowx, glowy, 10 + osc, 10 + osc);
    // inner glow
    fill(237, 102, 138);
    ellipse(glowx, glowy, 4 + osc, 4 + osc);

    starfigure(); // mysterious figure in the clouds

    // light pink clouds
    noStroke();
    fill(242, 165, 165, 200);
    ellipse(337, 273, 76, 50);
    ellipse(535, 276, 94, 50);
    fill(242, 165, 165); // cloud covering the figure is opaque
    ellipse(430, 278, 135, 50);


    // twinkling stars
    fill(242, 242, 158);
    for (var st = 0; st < 5; st++) {
        // x positions for the twinkling stars
        starX = [366, 413, 491, 493, 514];
        // y positions for the twinkling stars
        starY = [240, 213, 216, 245, 178];
        // sizes for the twinkling stars
        starSize = [8, 9, 6, 8, 6];
        star(starX[st], starY[st], 
             starSize[st] - random(4, 5), starSize[st], 5);
    }
}

function starfigure() {
    // body
    fill(224, 184, 140);
    ellipse(466, 195.5, 2, 5); // right ear
    ellipse(443, 194.5, 2, 5); // left ear
    rect(450, 203, 8, 9); // neck
    quad(470, 235, 485, 219, 490, 220, 475, 240); // arm
    ellipse(488.5, 219.5, 7, 3); // palm
    //forefinger
    beginShape();
    vertex(491, 220);
    vertex(491, 218);
    vertex(494.2, 217.6);
    vertex(494.5, 218.5);
    vertex(492, 220);
    vertex(491, 220);
    endShape();
    ellipse(494.5, 216.5, 1, 4);// upper finger

    //hair
    fill(248, 252, 195);
    ellipse(454.5, 194, 23, 24);
    hairx = [443, 466, 464, 463, 462, 452, 450, 449, 445, 445, 
             444, 441, 443, 443]; // x positions of hair coordinates
    hairy = [194, 194, 208, 205, 208, 208, 206, 208, 207, 205, 
             207, 207, 199, 194]; // y positions of hair coordinates
    beginShape();
    for (var h = 0; h < 14; h++) {
        vertex(hairx[h], hairy[h]);
    }
    endShape();

    // cape
    fill(30, 30, 124);
    ellipse(454, 217, 35, 13);
    quad(426, 266, 437, 218, 472, 218, 482, 266);
    // folds in the cape
    fill(16, 16, 94);
    triangle(438, 259, 443, 221, 443, 259);
    triangle(467, 260, 465, 222, 472, 260);
    // constellations of the cape
    strokeWeight(1);
    stroke(113, 108, 183);
    // first constellation
    line(443, 216, 448, 224);
    line(448, 224, 452, 225);
    line(452, 225, 445, 235);
    // second constellation
    line(455, 236, 453, 244);
    line(453, 244, 459, 246);
    line(459, 246, 461, 242);
    line(461, 242, 465, 250);
    // constellation on the edge of the cape
    line(432, 243, 434, 245);
    line(434, 245, 429, 251);
}

// ------------- FUNCTIONS & OBJECTS FOR THE MOVING SUBWAY IN FRAME 1 -------------

//  PRESSING KEYS TO CONTROL SUBWAY CAR SPEED
function keyPressed() {
    // everytime the "f" key is pressed, the subway car's 
    // speed will increase by 1
    if (key == "f") {
        accelerate += 1;
    }

    // likewise, everytime "s" is pressed, the subway car's
    // speed will reduce by 1
    if (key == "s") {
        accelerate -= 1; 
    }

    // upon pressing the r key, it resets the accelerate to 0
    // helpful in case you make the train way too fast and don't
    // want to click s multiple times to get back to the original speed
    if (key === "r") {
        accelerate = 0; 
    }
}

// SUBWAY OBJECT
function makeSubway() {
    var subwaycar = {x: -200,
                     y: 119,
                     speed: 4,
                     move: moveSubway,
                     draw: drawSubway}
    return subwaycar; 
}

function drawSubway() {
    // subway cars
    var sublength = 75; // length of subway car
    var subheight = 28; // height of subway car
    var subx = -200; // x position of subway car
    var suby = 119; // y position of subway car
    var round = 7.5; // rounded edges of rect

    // subway car windows
    var windowY = 125; // y position of subway window
    var windowW = sublength / 4 // length of subway window
    var windowH = 10; // height of subway window
    var wspace = 4.5; // spacing between subway windows

    // creating the subway car
    for (var s = 0; s < 5; s++) {
        stroke(125, 121, 132);
        fill(230, 230, 252); 
        rect(this.x + sublength * s, suby, sublength, subheight, round);
        for (var i = 0; i < 3; i++) {
            fill(21, 68, 76);
            rect(this.x + wspace * (i + 1) + windowW * i + sublength * s, 
                windowY, windowW, windowH);
        }
    }
}

function moveSubway() {
    this.x += (this.speed + accelerate); 
    // to prevent the subway car from moving backwards, 
    // if accelerate becomes too big that its absolute value
    // is greater than the speed, it resets to 0, which means
    // the subway train will pause right before and upon pressing
    // the s key again, it will reset to the original speed
    if (accelerate < this.speed * -1) {
        accelerate = 0;  
    }
    // reset the x position of the last subway car to -500
    // if the last subway car exits the frame
    if (this.x > f1x + f1width) {
        this.x = -500; 
    }
}

// ----------- FUNCTIONS & OBJECTS FOR FALLING STARS IN FRAME 1 & 2 ----------

// FALLING STARS IN FRAME 1 & 2 WHEN MOUSE IS PRESSED
function mousePressed() {
    // when mouse is pressed, the function will 
    // to add a shooting star will be called with 
    // the x and y inputs of the mouse position
    if (mouseX < width && mouseX > f1x &&
        mouseY < f1y + f1height && mouseY > f1y) {
        addShootingStar(mouseX, mouseY);
    }
}

// SHOOTING STAR OBJECT 
function makeShootingStar(xPos, yPos) {
    var shootingStar = {x: xPos,
                        y: 0, 
                        size: random(7, 20),
                        speed: random(3, 8),
                        move: moveShootingStar,
                        draw: drawShootingStar}
    return shootingStar; 
}

// DRAWING THE SHOOTING STAR
function drawShootingStar() {
    // controls how the glowing portion of the star 
    // grows and shrinks, so it looks like it's flickering
    var osc = 10 + sin(millis()) * 1;

    noStroke();

    // glowing part of the star
    fill(244, 242, 168, 75);
    ellipse(this.x, this.y, this.size + osc, this.size + osc);

    // inner part of the star
    fill(239, 235, 91);
    ellipse(this.x, this.y, this.size, this.size);
    
}

// MOVING THE SHOOTING STAR
function moveShootingStar() {
    this.y += this.speed; 
}

// ADDING SHOOTING STARS
function addShootingStar(xPos, yPos) {
    shootingStarArray.push(makeShootingStar(xPos, yPos));
}

// REMOVING SHOOTING STARS 
function removeShootingStar() {
    // array for keeping the stars
    var keepStar = [];
    // as long as the shooting stars are in bound of frame 1
    // they will be pushed into the array for keeping the stars
    for (var i = 0; i < shootingStarArray.length; i++) {
        if (shootingStarArray[i].y < f1y + f1height + 
            shootingStarArray[i].size) {
            keepStar.push(shootingStarArray[i]);
        }
    }
    shootingStarArray = keepStar;  
}

// ------------- FUNCTIONS & OBJECTS FOR THE RIPPLES IN FRAME 1 --------------

// RIPPLE OBJECT
function makeRipples(xPos, yPos) {
    var makeRipple = {x: xPos,
                      y: yPos,
                      // longer ripples are in the front, shorter ones in the back
                      length: map(yPos, 212, height, 5, 75),
                      // thinner ripples in the back, thicker ones in the front
                      weight: map(yPos, 212, height, 1, 4),
                      // faster ripples in the front, slower ripples in the back
                      speed: map(yPos, 212, height, 0.5, 1),
                      move: moveRipple,
                      draw: drawRipple}
                    return makeRipple; 
}

// MOVING THE RIPPLE
function moveRipple() {
    // x position changes by speed
    this.x += this.speed; 
    // if the ripple leaves the frame, reset x position
    // to the left side of the frame
    if (this.x > width + this.length) {
        this.x === -this.length;
    }
}

// ADDING RIPPLES
// using a tiny probability, add ripples
function addRipple() {
    if (random(0, 1) < 0.025) {
        ripples.push(makeRipples(-75, random(212, height)));
    }
}

// REMOVING RIPPLES
function removeRipple() {
    // an array for ripples to keep
    var keepRipples = [];
    // as long as ripples are within the bounds of frame 1, 
    // keep them in the keepRipples array
    for (var i = 0; i < ripples.length; i++) {
        if (ripples[i].x < f1x + f1width) {
            keepRipples.push(ripples[i]);
        }
    }
    ripples = keepRipples;
}

// DRAWING THE RIPPLE
function drawRipple() {
    strokeWeight(this.weight);
    stroke(255, 255, 255, 75);
    line(this.x, this.y, this.x + this.length, this.y);
}

// ------------- FUNCTIONS & OBJECTS FOR THE STARS IN FRAME 2 -------------

// SUBWAY STAR OBJECT
function makeStar(xPos, yPos) {
    var subwayStar = {x: xPos,
                      y: yPos,
                      radius1: random(5, 10),
                      npoints: 5,
                      speed: random(0.5, 2),
                      move: moveSubwayStar,
                      draw: drawSubwayStar}
                    return subwayStar; 
}

// MOVING THE SUBWAY STARS
function moveSubwayStar() {
    // x position changes by speed
    this.x += this.speed; 
    // if the star leaves the frame, reset x position
    // to the left side of the frame
    if (this.x > width) {
        this.x === f1x + f1width + 10;
    }
}

// ADDING SUBWAY STARS
// using a tiny probability, add subway stars
function addSubwayStar() {
    if (random(0, 1) < 0.02) {
        subwayStarArray.push(makeStar(f1x + f1width + 10, 
                             random(30, 100)));
    }
}

// REMOVING SUBWAY STARS
function removeSubwayStar() {
    // an array for subway stars to keep
    var keepSubwayStar = [];
    // as long as the stars are within the width, 
    // keep them in a separate array
    for (var i = 0; i < subwayStarArray.length; i++) {
        if (subwayStarArray[i].x < width) {
            keepSubwayStar.push(subwayStarArray[i]);
        }
    }
    subwayStar = keepSubwayStar;
}

// DRAWING THE SUBWAY STARS
function drawSubwayStar() {
    fill(242, 242, 158);
    var angle = TWO_PI / this.npoints;
    var halfAngle = angle / 2.0;
    beginShape();
    for (let a = 0; a < TWO_PI; a += angle) {
        var sx = this.x + cos(a) * (this.radius1 - 5);
        var sy = this.y + sin(a) * (this.radius1 - 5);
        vertex(sx, sy);
        sx = this.x + cos(a + halfAngle) * this.radius1;
        sy = this.y + sin(a + halfAngle) * this.radius1;
        vertex(sx, sy);
    }
  endShape(CLOSE);
}



For my final project, I created a speculative cartoon panel. It’s speculative in the sense that the it forces the viewer to use their imagination to weave a story of what might have happened–the possibilities are endless. In this project, I enjoyed exploring my interests in illustration, concept art, and storytelling. To convey this dreamy, mysterious, magical atmosphere, I worked within a range of cool/teal tones, with pink and yellow highlights.

To begin, I created a moodboard of images/animations on Pinterest. Then, I sketched out/digitized my idea using simple shapes on Illustrator. Not only did I enjoy creating the static images, I think programming the interactions made the comic scene much more fun and engaging.

Moodboarding: I drew a lot of inspiration from space/water concept art. What I love about concept art is its ability to inspire your imagination as you create stories in your head about the piece.
My sketch on Illustrator, which helped save time when visually styling the scene.