Yoo Jin Shin- Final Project

Final

// Yoo Jin Shin
// yoojins@andrew.cmu.edu
// Section D
// Final Project

// All scenes global variables
var yaxis = 1;
var xaxis = 2;
var frames = []; 
var frameCounts = 0;
var textSpeed = 3;
var slider;
var sliderValue;

// Yeosu global variables
var c1, c2, c3, c4;
var terrainSpeed1 = 0.0001; 
var terrainDetail1 = 0.003;
var terrainSpeed2 = 0.00012;
var terrainDetail2 = 0.0045;
var terrainSpeed3 = 0.00007;
var terrainDetail3 = 0.0006;
var clouds = []; 
var trees = [];
var textX = 1;
var sunMouse; 
var birdLoad;
var bird1;
var birds = [];

// Grand Canyon global variables
var terrainSpeed4 = 0.00003;
var terrainDetail4 = 0.0005;
var terrainSpeed5 = 0.00012;
var terrainDetail5 = 0.007;
var terrainSpeed6 = 0.00007;
var terrainDetail6 = 0.0065;
var textX3 = 1;
var c9;

// Las Vegas global variables
var c5, c6, c7, c8;
var buildings = [];
var snowflakes = [];
var drops = [];
var textX2 = 1;
var buildingMouse;
var carLoad;
var car1;
var cars = [];

function preload(){
    // Loading sprite sheet for girl character via direct links from imgur
    // Creds to https://www.gameart2d.com/freebies.html
    var filenames = [];
      filenames[0] = "https://i.imgur.com/oEJGbCm.png";
      filenames[1] = "https://i.imgur.com/QsZoWMJ.png";
      filenames[2] = "https://i.imgur.com/kyo4lhi.png";
      filenames[3] = "https://i.imgur.com/XmPYK4a.png";
      filenames[4] = "https://i.imgur.com/w80hQ6A.png";
      filenames[5] = "https://i.imgur.com/0BiNaBy.png";
      filenames[6] = "https://i.imgur.com/WL7kjhR.png";
      filenames[7] = "https://i.imgur.com/z6agkKq.png";
      filenames[8] = "https://i.imgur.com/Vxi5Eu6.png";
      filenames[9] = "https://i.imgur.com/mrjcctG.png";
      filenames[10] = "https://i.imgur.com/oDP8zsr.png";
      filenames[11] = "https://i.imgur.com/DsNFucK.png";
      filenames[12] = "https://i.imgur.com/8Y3e8BW.png";
      filenames[13] = "https://i.imgur.com/aQUjC1v.png";
      filenames[14] = "https://i.imgur.com/6yQMDLO.png";
      filenames[15] = "https://i.imgur.com/fJZPSul.png";
      filenames[16] = "https://i.imgur.com/vghvvNP.png";
      filenames[17] = "https://i.imgur.com/oh2WsXz.png";
      filenames[18] = "https://i.imgur.com/yPh4Upu.png";
      filenames[19] = "https://i.imgur.com/k0wd9HK.png";

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

    // Loading bird and car images created using illustrator
    birdLoad1 = "https://i.imgur.com/RQSUNmm.png";
    bird1 = loadImage(birdLoad1);

    carLoad1 = "https://i.imgur.com/yKCei98.png";
    car1 = loadImage(carLoad1);
}

function setup() {
  createCanvas(480, 480);
  drawSlider();

  // Yeosu: Define gradient colors
  c1 = color(225, 217, 199); // sky top color - grey
  c2 = color(254, 180, 144); // sky bottom color - orange
  c3 = color(50, 50, 2); // ground left - lighter green
  c4 = color(27, 20, 2); // ground right - dark green

  // Yeosu: Initialize clouds
  for (var i = 0; i < 3; i++) {
      var cloudX = random(width); 
      clouds[i] = drawClouds(cloudX);
  }

  // Yeosu: Initialize trees
  for (var i = 0; i < 7; i++) {
    var treeX = random(width);
    var t = drawTrees(treeX);
    trees[i] = t;
  }

  // Yeosu: Initialize birds
  for (var i = 0; i < 7; i++) {
    var birdX = random(width);
    birds[i] = drawBird(birdX);
  }

  // Las Vegas: Define gradient colors
  c5 = color(0, 0, 0); // sky top color - black
  c6 = color(61, 48, 52); // sky bottom color - purpley

  // Las Vegas: Initialize buildings
  for (var i = 0; i < 6; i++) {
      var rx = random(width);
      buildings[i] = drawBuilding(rx);
  }  

  // Las Vegas: Initialize cars
  for (var i = 0; i < 10; i++) {
    var carX = random(width);
    cars[i] = drawCar(carX);
  }
    imageMode(CENTER);
}

function drawSlider() {
  slider = createSlider(5, 60, 30);
  slider.style(200, '100px');
  slider.position(20, 450);
}

function draw() {
  // Set slider to adjust frameRate
  var frameRateSpeed = slider.value();
  frameRate(frameRateSpeed);
  
  // Change scene every 160 frames, then loop
  // Loop: Yeosu - GrandCanyon1 - LasVegas - GrandCanyon2
  if (frameCounts <= 160) { // 160
      drawYeosu();
  }
  else if (frameCounts >= 160 & frameCounts < 320) {
      drawGrandCanyon(); // transition: light to dark sky
  } 
  else if (frameCounts >= 320 & frameCounts < 480) {
    drawLasVegas();
  }
  else if (frameCounts >= 480 & frameCounts < 640) {
    drawGrandCanyon2(); // transition: dark to light sky
  }
  else if (frameCounts === 640) {
    frameCounts = 0;
  }  
  frameCounts++;
}

// =====================================================================
// ==============================YEOSU==================================
// =====================================================================

function drawYeosu() {
  setGradient(0, 0, width, height / 2 + 70, c1, c2, yaxis); // sky
  drawSun();
  drawMountain1();
  drawMountain2();
  drawOcean();
  drawSunReflection();
  updateClouds();
  removeClouds();
  addClouds();
  setGradient(0, 390, width, 480, c3, c4, xaxis); // ground
  updateTrees();
  removeTrees();
  addTrees();
  drawMe();
  drawFog();
  updateBird();
  removeBird();
  addBird();
  writeText();
}

function setGradient(x, y, w, h, c1, c2, axis) {
  // Top to bottom gradient
  if (axis == yaxis) { 
    for (var i = y; i <= y + h; i++) {
      var inter = map(i, y, y + h, 0, 1);
      var c = lerpColor(c1, c2, inter);
      stroke(c);
      line(x, i, x + w, i);
    }
  }
  // Left to right gradient
  else if (axis == xaxis) {
    for (var i = x; i <= x + w; i++) {
      var inter = map(i, x, x + w, 0, 1);
      var c = lerpColor(c1, c2, inter);
      stroke(c);
      line(i, y, i, y + h);
    }
  }
}

function drawMountain1(){
  noStroke();
  fill(204, 157, 135); 
  beginShape(); 
  for (var x = 0; x < width; x++) {
    var t = (x * terrainDetail1) + (millis() * terrainSpeed1);
    var y = map(noise(t), 0, 1.8, height / 4 - 10, height);
    vertex(x, y); 
  }
  vertex(width, height);
  vertex(0, height);
  endShape();
}

function drawFog() { 
  for (var y = 200; y < height + 5; y++) {
    var f = map(y, height / 2 - 40, 550, 0, 100);
    stroke(255, f);
    line(0, y, width, y);
  }
}

function drawMountain2(){
  fill(147, 126, 115); 
    noStroke();
    beginShape(); 
      for (var x = 0; x < width; x++) {
            var t = (x * terrainDetail2) + (millis() * terrainSpeed2);
            var y = map(noise(t), 0, 2, height / 4 + 60, height);
            vertex(x, y); 
      }
      vertex(width,height);
      vertex(0,height);
    endShape();
}

function drawOcean() {
    fill(170, 146, 121); 
    noStroke();
    beginShape(); 
      for (var x = 0; x < width; x++) {
            var t = (x * terrainDetail3) + (millis() * terrainSpeed3);
            var y = map(noise(t), 0, 2, height - 230, height);
            vertex(x, y); 
      }
      vertex(width, height);
      vertex(0, height);
    endShape();
}

function drawSun() {
	// All sun parts based on mouseY
  	sunMouse = constrain(mouseY, 0, height / 3 + 30);
    noStroke();
    // Sunglow
    // Color less intense and size smaller as sun goes farther up
    fill(253, 254 - sunMouse, 221 - sunMouse, 20 - sunMouse*0.02);
    ellipse(width / 3, sunMouse + 40, sunMouse - 10, sunMouse - 10);
    fill(253, 254 - sunMouse, 221 - sunMouse, 20 - sunMouse*0.03);
    ellipse(width / 3, sunMouse + 40, sunMouse*1.1, sunMouse*1.1);
    fill(253, 254 - sunMouse, 221 - sunMouse, 20 - sunMouse*0.05);
    ellipse(width / 3, sunMouse + 40, sunMouse*1.2 + 10, sunMouse*1.2 + 10);
    // Sunrays
    fill(253, 254, 221, 30);
    triangle(width / 3, sunMouse + 20, width / 3 - 40, height / 3 + 90, width / 3 - 10, height /3 + 80); // left big
    triangle(width / 3, sunMouse + 20, width / 3 + 40, height / 3 + 80, width / 3 + 10, height /3 + 70); // right big
    triangle(width / 3, sunMouse + 20, width / 3 - 50, height / 3 + 90, width / 3 - 30, height /3 + 80); // left small
    triangle(width / 3, sunMouse + 20, width / 3 + 50, height / 3 + 80, width / 3 + 30, height /3 + 70); // right small
    triangle(width / 3, sunMouse + 20, width / 3 - 70, height / 3 + 70, width / 3 - 80, height /3 + 70); // left small2
    triangle(width / 3, sunMouse + 20, width / 3 + 70, height / 3 + 90, width / 3 + 80, height /3 + 80); // right small2
    // Sun
    fill(253, 254, 221, sunMouse + 30);
    ellipse(width / 3, sunMouse + 40, sunMouse - 20, sunMouse - 20);
}

function drawSunReflection() { 
	// Size of reflection on ocean corresponds to sun size/distance away
    var x = 10;
    var y = 47;
    var w = sunMouse;
    var h = 10;
    noStroke();
    // Outer darker glow
    fill(248, 172, 137, 100);
    push();
    rectMode(CENTER);
    rect(width / 3, 2 * height / 3 + y, w, h, 10);
    rect(width / 3, 2 * height / 3 + y - 13, w * 0.8, h, 10);
    rect(width / 3, 2 * height / 3 + y - 26, w * 0.6, h, 10);
    // Inner lighter glow
    fill(253, 254, 221, 50);
    rect(width / 3, 2 * height / 3 + y - 4, w * 0.9, h * 0.8, 10);
    rect(width / 3, 2 * height / 3 + y - 17, w * 0.7, h * 0.8, 10);
    rect(width / 3, 2 * height / 3 + y - 30, w * 0.5, h * 0.8, 10);
    pop();
}

function updateClouds(){
  for (var i = 0; i < clouds.length; i++) {
    clouds[i].move();
    clouds[i].display();
  }
}

function removeClouds(){
  var keepClouds = [];
  for (var i = 0; i < clouds.length; i++) {
      if (clouds[i].x - this.width / 2 < width) {
        keepClouds.push(clouds[i]);
      }
  }
  clouds = keepClouds;
}

function addClouds(){
  var newCloud = 0.01;
  if (random(0, 1) < newCloud) {
    clouds.push(drawClouds(random(width)))
  }
}

function moveClouds(){
  this.x += this.speed;
}

function displayClouds(){
  var b = random(165, 180);
  fill(255, 240, 240, 30);
  ellipse(this.x, this.y, this.width, this.height);
  ellipse(this.x + 50, this.y + 10, this.width - 20, this.height - 70);
  ellipse(this.x - 50, this.y - 5, this.width - 70, this.height - 10);
}

function drawClouds(newCloudX) {
  var cloud = {
    x: newCloudX,
    y: random(30, height / 2),
    width: random(150, 250), 
    height: random(20, 50),
    speed: random(0.5, 1.5),
    move: moveClouds,
    display: displayClouds
  }
  return cloud;          
}

function updateTrees() {
  for (var i = 0; i < trees.length; i++) { 
    trees[i].move();
    trees[i].display();
  }
}

function removeTrees() {
  var keepTrees = [];
  for (var i = 0; i < trees.length; i++) {
    if (trees[i].x < width) {
      keepTrees.push(trees[i]);
    }
  }
  trees = keepTrees;
}

function addTrees() {
  var newTree = 0.02;
  if (random(0, 1) < newTree) {
    trees.push(drawTrees(random(width)));
  }
}

function moveTrees() {
  this.x += this.speed;
}

function displayTrees() {
  // Tree height based on mouseY, mapped to a reasonable extent
  var treeMouse = map(mouseY, 0, height, height / 3, 0);
  var treeMouseCon = constrain(treeMouse, 0, height)
  // Trunk
  stroke(c4);
  strokeWeight(4);
  line(this.x, this.y + 20 - treeMouseCon, this.x, this.y + 40);
  // Leaves
  fill(c3, 20);
  stroke(c3);
  strokeWeight(7);
  strokeJoin(ROUND);
  triangle(this.x, this.y - treeMouseCon, this.x + 20, this.y + 25, this.x - 20, this.y + 25);
  // Add snow if on Grand Canyon scene
  if (frameCounts >= 160 & frameCounts < 320 || frameCounts >= 480 && frameCounts < 640) {
    fill(250);

This project was inspired by my love of taking photos, especially of nature and cityscapes. I tried recreating the feel and weather of three shots below in a looping generative landscape. Follow me! You can speed up or slow my pace using the slider and control objects like the sun and buildings using the mouse.

Scene 1 – Sunrise at Yeosu (Javascript, my photo, sketch)
Scene 2 – Snowy Grand Canyon
Scene 3 – Rainy Las Vegas

Since this doesn’t load properly on WP, access zip file here!

Lan Wei-Final Project-MUSICAL UNIVERSE

sketch

//Lan Wei
//lanw@andrew.cmu.edu
//Section // D
//Final Project-Musical Universe

var snd = []; //this array will hold the sounds
var amplitude = [];
var ampTotal; //Total amplitude to change background color
var mouseXArray = []; //the array of X values
var mouseYArray = []; //the array of y values
var balls = []; //the array of ball objects
var np = 26; //the number of background lines
var pArray = []; //the array of particles

//preload the sounds
function preload() {
    var PREFIX = "https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/";
    for (var i = 1; i < 10; i++){
        var sound = loadSound(PREFIX + "sound" + i + ".wav"); //load sounds
        sound.setVolume(0.5);
        snd.push(sound); //push the sounds in snd. var sound will never be used again
    }
}

//make ball objects
function makeBalls(bx, by, br){
    var ball = {x: bx,
                y: by,
                r: br, //radius of the ball
                update: updateBall, //update the size of the balls
                sound: whichSound, //which amplitude is the ball related to
                draw: drawBall,
                play: playSound};
    return ball; //return the new object
}

function drawBall(){
    stroke(255);
    //ellipse(this.x, this.y, this.r, this.r);
    var total = 20; //control the density of the points on the balls
    for (var i = 0; i < total; i++){
        var longtitude = map(i, 0, total, -PI, PI);
        for (var j = 0; j < total; j++){
            var latitude = map(j, 0, total, -HALF_PI, HALF_PI);
            //fold the longtitude-latitude panel into a sphere
            var ptX = this.r * sin(longtitude) * cos(latitude);
            var ptY = this.r * sin(longtitude) * sin(latitude);
            var ptZ = this.r * cos(longtitude);
            push();
            stroke(255);
            translate(ptX, ptY, ptZ);
            sphere(0.1);
            pop();
        }
    }
}

function whichSound(){
    if (zone(this.x, this.y) == 1){
        return snd[0];
    }
    if (zone(this.x, this.y) == 2){
        return snd[1];
    }
    if (zone(this.x, this.y) == 3){
        return snd[2];
    }
    if (zone(this.x, this.y) == 4){
        return snd[3];
    }
    if (zone(this.x, this.y) == 5){
        return snd[4];
    }
    if (zone(this.x, this.y) == 6){
        return snd[5];
    }
    if (zone(this.x, this.y) == 7){
        return snd[6];
    }
    if (zone(this.x, this.y) == 8){
        return snd[7];
    }
    if (zone(this.x, this.y) == 9){
        return snd[8];
    }
}

function playSound(){
    var soundLocal = this.sound();
    soundLocal.play();
}

function updateBall(){
    var amp = amplitude[zone(this.x, this.y) - 1];
    var level = amp.getLevel(); //get the level
    this.r = 30 + 300 * level; //the size of balls
}

//particles to make background lines
function makeParticle(px, py, pdx, pdy){
    p = {x: px,
         y: py,
         dx: pdx,
         dy: pdy,
         update: pUpdate,
         draw: pDraw}
    return p;
}

function pUpdate(){
    this.x += this.dx;
    this.y += this.dy;

    //limit the lines in a certain area of 500 * 500 using 'bouncing'
    if (this.x < -250){ //left boundary
        this.x = -this.x - 250;
        this.dx = -this.dx - 250;
    }
    else if (this.x > 250){ //right boundary
        this.x = 300 - this.x;
        this.dx = 300 - this.dx;
    }
    if (this.y < -250){ //downward boundary
        this.y = -this.y - 250;
        this.dy = -this.dy - 250;
    }
    else if (this.y > 250){ //upward boundary
        this.y = 300 - this.y;
        this.dy = 300 - this.dy;
    }
}

function pDraw(){
    ellipse(this.x, this.y, 5, 5);
}
/////////////////////////////////////////////////////////////////////////////////////
function setup() {
    createCanvas(450, 450, WEBGL); // 3D mode. (0, 0)locates in the center of the canvas
    background(0);
    stroke(255);
    perspective(); //perspective view

    for (var i = 0; i < 9; i++){
        amplitude[i] = new p5.Amplitude();
        amplitude[i].setInput(snd[i]); //get independent amplitude
    }
    ampTotal = new p5.Amplitude(); //total amplitude

    for (var i = 0; i < np; i++){
        //the boundary of particles is a little bigger than the canvas
        var p = makeParticle(random(-300, 300), random(-300, 300), random(-2, 2), random(-2, 2));
        pArray.push(p);
    }
}

function draw() {
    var levelTotal = ampTotal.getLevel();
    var col = map(levelTotal, 0, 1, 0, 100);//background color
    background(col, 0, 2 * col);
    //draw background lines
    for (var i = 0; i < np; i++){
        pArray[i].update();
        pArray[i].draw();
        //lines between particles
        for (var j = 0; j < np/2; j++){
            stroke(random(0, 150));
            strokeWeight(0.2);
            line(pArray[j].x, pArray[j].y, pArray[j + np/2].x, pArray[j + np/2].y);
        }
    }
    //the canvas is divided by a 3*3 grid
    strokeWeight(1);
    stroke(random(20, 70));
    line(-75, -225, -75, 225);
    line(75, -225, 75, 225);
    line(-225, -75, 225, -75);
    line(-225, 75, 225, 75);

    if (mouseXArray.length != 0){ //after the 1st mouse press
        stroke(255);
        fill(255);
        // draw all the balls
        for (i = 0; i < balls.length; i ++){
            balls[i].update(); //update the radius of the balls
            push();
            translate(balls[i].x, balls[i].y, 0);
            //rotate with randomness
            rotateX(frameCount * 0.05 + i);
            rotateY(frameCount * 0.05 + i * 5);
            rotateZ(frameCount * 0.05 + i * 5);
            balls[i].draw(); //draw the balls
            pop();
        }
    }
}

//To identify which zone is the ball in
//translate the coordinate to normal mode
function zone(x, y){
    if ((y > -225 )& (y < height/3 - 225)){
        if ((x > -225) && (x < width/3 - 225)){ //position 1
            return 1;
        }
        if ((x > width/3 - 225) & (x < 2 * width/3 - 225)){ //position 2
            return 2;
        }
        if ((x > 2 * width/3 - 225) & (x < width - 225)){ //position 3
            return 3;
        }
    }

    if ((y > height/3 - 225) & (y < 2 * height/3 - 225)){
        if ((x > -225) && (x < width/3 - 225)){ //position 4
            return 4;
        }
        if ((x > width/3 - 225) & (x < 2 * width/3 - 225)){ //position 5
            return 5;
        }
        if ((x > 2 * width/3 - 225) & (x < width - 225)){ //position 6
            return 6;
        }
    }

    if ((y > 2 * height/3 - 225) & (y < height - 225)){
        if ((x > -225) && (x < width/3 - 225)){ //position 7
            return 7;
        }
        if ((x > width/3 - 225) & (x < 2 * width/3 - 225)){ //position 8
            return 8;
        }
        if ((x > 2 * width/3 - 225) & (x < width - 225)){ //position 9
            return 9;
        }
    }
}

//when mouse is clicked, a sound will be played and a ball will be drawn
function mouseClicked(){
    var clickOn; //to check whether click on an existing ball
    var newBalls = [];
    var newMouseXArray = [];
    var newMouseYArray = [];

    if (mouseXArray.length == 0){ //when there is no existing balls
        clickOn = 0;
        newBalls.push(balls[0]);
        newMouseXArray.push(mouseXArray[0]);
        newMouseYArray.push(mouseYArray[0]);

    }

    // a ball will be removed when clicked again
    else{ //after the 1st click
        for (i = 0; i < balls.length; i++){
            var distance = dist(mouseX - 225, mouseY - 225, balls[i].x, balls[i].y);
            //is clicked
            if (distance <= 20){ //minimum distance
                var soundBall = balls[i].sound();
                soundBall.setVolume(0); //stop the sound of the ball that's clicked
                // balls.splice(i, 1); //remove the ball
                // mouseXArray.splice(i, 1);
                // mouseYArray.splice(i, 1);
                clickOn = 1;
            }
            //is not clicked
            else{
                clickOn = 0;
                newBalls.push(balls[i]);
                newMouseXArray.push(mouseXArray[i]);
                newMouseYArray.push(mouseYArray[i]);
            }
        }
        balls = newBalls;
        mouseXArray = newMouseXArray;
        mouseYArray = newMouseYArray;
    }
    if (clickOn == 0){
        mouseXArray.push(mouseX - 225);//translate to WEBGL coordinates
        mouseYArray.push(mouseY - 225);
        // initial radius: 30
        //make ball objects
        var newBall = makeBalls(mouseX - 225, mouseY - 225, 30);
        balls.push(newBall);

    }
    //play sound using the object
    for (i = 0; i < balls.length; i++){
        var soundLocal = balls[i].sound();
        balls[i].play();
        soundLocal.loop();
    }
}

//YOU'VE GOT A MUSICAL UNIVERSE!!!

[How to play]: Click on different positions of the canvas to get different mixtures of ‘UNIVERSE MUSIC’,click the balls again to remove them as well as the sound (in the opposite order you created the balls). Play slowly! (Sometimes it doesn’t work well if played fast, maybe it’s my browser’s problem.)

My initial idea of the project is to create a simple instrument that enables users to mix several pieces of music. In the proposal stage, I have imagined many sound effects and interaction methods, but have only achieved part of them. The actual coding process is much harder than I thought, and the final result becomes more visual than auditory. But actually, I’m very happy about the project because it is really a reviewing as well as a learning process. I’m not very good at using objects and particles so I tried to include these contents in my project. And I also watched some online tutorials to create the 3D effect of the balls. The final result is very satisfying for me, especially the 3D ball effect!

Have Fun!

 

Jessica Timczyk – Final Project

Final Project

// Jessica Timczyk
// Section D
// jtimczyk@andrew.cmu.edu
// Final-Project

///////// GLOBAL VARIABLES ///////////
var terrainDetail = 0.005;
var intercols = [];

/////// snow globals //////
var snow = [];
var gravity;
var zOff = 0;
var mt = [];

///// elephant globals //////
var frames = []; // store images
var frameIndex = 0;
var system; // particle system for elephants water
var trunkUp = false;
var counter = 0;
var click = -1;

//////// sound globals ////////
var elephantSound;
var birdSound;
var tigerSound;

///////// water waves globals /////////////
var t = 0;

///////// bush globals //////////////
var bush;
var bush1y= 240;

var bush2y = 350;

function preload() {
    ////////////////// ELEPHANT IMAGE PRELOADS ////////////////
    // urls for each elephant image of movement cycle
    var filenames = [];
    filenames[0] = "https://i.imgur.com/PSJnCjQ.png";
    filenames[1] = "https://i.imgur.com/PbWyeNh.png";
    filenames[2] = "https://i.imgur.com/s0o7oWG.png";

    // stores each picture into an array
    for (var i = 0; i < 3; i++) {
        frames[i] = loadImage(filenames[i]);
    }

    ////////////////// BUSH PRELOADS ////////////////////
    bush = loadImage("https://i.imgur.com/jdxSCfo.png");

    ////////////////// SOUND PRELOADS //////////////////
    elephantSound = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/139875__y89312__44-1.wav");
    elephantSound.setVolume(0.5);
    birdSound = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/238144__m4d3r0__small-bird-jungle-sounds-v3-2.wav");
    birdSound.setVolume(0.5);
    tigerSound = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/439280__schots__angry-tiger-4.wav")
    tigerSound.setVolume(0.5);
}



function setup() {
    createCanvas(600, 400);
    background(219, 245, 250);


    ////////////// MOUNTAIN SET UP /////////////
    for (var x = 0; x < width; x++) { // createse a random static mountain range using noise every time refreshed
        var t = (x * terrainDetail) 
        var y = map(noise(t), 0,1, 0, height/ 1.5);
        mt.push([x,y,height]); // pushes the array into the mt array
    }

    //////////// ELEPHANT WATER PARTICLE SYSTEM ///////////
    system = new ParticleSystem(createVector(260, 230)); // creates a particle system for the elephants water

    //////////// SNOWFLAKE SET UP ////////////
    gravity = createVector(0, 0.2); // gravity vector
    for (var i = 0; i < 200; i++){ // creates the 200 snowflakes and gives them random starting positions
        var x = random(width);
        var y = random(height);
        snow.push(new Snowflake(x,y)); // pushes new snowflake into the array
    }
}

function draw() {
    background(25, 28, 84);

    drawNorthernLights(); 

    drawScenary();

    drawTrees();

    drawBush();

    ////////////////// ELEPHANT /////////////////////
    drawElephant();
    if (frameIndex === 2) { // water comes out of his trunk when its in the up position
            elephantWater();
        }
    if (mouseIsPressed & trunkUp == false && mouseX > 150 && mouseX < 300 && mouseY > 190 && mouseY < 350 && (click%2 === 0)) { // if mouse pressed on elephant trunk moves up
        counter = 0
        trunkUp = true
        }
    if (trunkUp == true) {
        moveUp()
    }
    if (mouseIsPressed && counter > 32 && (click%2 != 0) && mouseX > 150 && mouseX < 300 && mouseY > 190 && mouseY < 350) { // when the elephant is clicked a second time it puts and keeps trunk down
            counter = 0;
            frameIndex = 0;
            trunkUp = false;
    }

    drawTrees2(); // second set of trees that are infront of the elephant

    drawBush2(); // second set of bushes that are infront of elephant
     if (mouseX > 400 & mouseX < 480 && mouseY > 220 && mouseY < 260) { // the bush hops when mouse is scrolled over it
        bush1y += random(-5, 5);
    }
    if (mouseX > 30 & mouseX < 130 && mouseY > 345 && mouseY < 385) { // the bush hops when mouse is scrolled over it
        bush2y += random(-5, 5);
    }
   
  



    ////////////////// DRAWING SNOWFLAKE /////////////

    zOff += 0.01; // z offset

    for (flake of snow) { //the position, gravity and wind forces are unique to each snowflake
        var xOff = flake.pos.x / width;
        var yOff = flake.pos.y / height;
        var wAngle = noise(xOff, yOff, zOff) * TWO_PI;
        var wind = p5.Vector.fromAngle(wAngle);
        wind.mult(0.2);
        flake.applyForce(gravity);
        flake.applyForce(wind);
        flake.update();
        flake.render();
    }
  

    mySounds(); // plays sounds

}

function drawScenary() {

    /////////// MOUNTAINS /////////////
    stroke(76, 86, 109); // Grey color of mountains
    noFill();
    beginShape(); 
    for (var x = 0; x < width; x++) { // createse a random static mountain range using noise every time refreshed
        line(mt[x][0], mt[x][1], mt[x][0], mt[x][2]); 
    }
    endShape();
    
    ////////////// GROUND ///////////////
    noStroke();
    fill(196, 157, 112);
    rect(0, height / 2 + 20, width, height / 2 - 20);

    /////////// POND ///////////////
    noStroke();
    fill(69, 156, 223);
    ellipse(width - 40, height - 60, 500, 150);


}

///////////////////// BUSHES ///////////////////////
function drawBush() { // draws bush images that sit behind the elephant
  

    imageMode(CENTER);
    image(bush, 440, bush1y, 150, 70); // variable so that it wiggles up and down
    image(bush, 320, 260, 150, 70);
    image(bush, 85, 240, 190, 70);

   
}

function drawBush2() { // draws bush images that sit infront of the elephant
    imageMode(CENTER);
    image(bush, 310, 380, 180, 70); 
    image(bush, 70, bush2y, 150, 70); // variable so that bush shakes when moused over
}

/////////////////// ELEPHANT /////////////////////
function drawElephant() { // elephant picture
    imageMode(CENTER);
    image(frames[frameIndex], 230, 270, 200, 200);
}


function moveUp() { // moves trunk all the way up when clicked once
    if (click%2 == 0) { // when the elephant is clicked an odd time the trunk just goes down
        counter += 1;
        if (counter == 15) {
            frameIndex = 1;
        }
        if (counter == 30) {
            frameIndex = 2;
        }
    } 
}

function mousePressed() { // increases click count when mouse is pressed once and mouse on elephant
if (mouseX > 150 & mouseX < 300 && mouseY > 190 && mouseY < 350){
    click += 1;
    }
}

function elephantWater() { // run particle system
    system.addParticle();
    system.run();
}

// simple particle class
var Particle = function(position) {
    this.acceleration = createVector(0, 0.05);
    this.velocity = createVector(random(-1, 1), random(-1, 0));
    this.position = position.copy();
    this.lifespan = 255;
}

Particle.prototype.run = function() {
    this.update();
    this.display();
}

// updating position
Particle.prototype.update = function() {
    this.velocity.add(this.acceleration);
    this.position.add(this.velocity);
    this.lifespan -= 2;
}

// display method for particles
Particle.prototype.display = function() {
    stroke(190, 244, 250, this.lifespan);
    strokeWeight(2);
    fill(143, 239, 250, this.lifespan);
    ellipse(this.position.x, this.position.y, 12, 12);
}

// check if particle is still useful
Particle.prototype.isDead = function() {
    return this.lifespan < 0;
}

var ParticleSystem = function(position) { // position of particles
    this.origin = position.copy();
    this.particles = [];
}

ParticleSystem.prototype.addParticle = function() { // pushes a new particle into the system at the starting point
    this.particles.push(new Particle(this.origin));
}

ParticleSystem.prototype.run = function() { // gets rid of particles that have expired
    for (var i = this.particles.length-1; i >= 0; i--) {
        var p = this.particles[i];
        p.run();
        if (p.isDead()) {
            this.particles.splice(i, 1);
        }
    }
}


////////////////// TREES ////////////////////////
function drawTrees() { // trees that are positioned begind elephant
    push();
    translate(550, 250);
    drawBranch(0, map(mouseY, 0, 400, 15, 20));
    pop();

    push();
    translate(150, 250);
    drawBranch(0, map(mouseY, 0, 400, 15, 20));
    pop();

    push();
    translate(100, 290);
    drawBranch(0, map(mouseY, 0, 400, 15, 20));
    pop();
}

function drawTrees2() { // trees that are positioned in front of elephant also rotate opposite way
    push();
    translate(100, 350);
    drawBranch2(0, map(mouseY, 0, 400, 15, 20));
    pop();

    push();
    translate(500, 260);
    drawBranch2(0, map(mouseY, 0, 400, 15, 20));
    pop();

}

function drawBranch2(depth, len) { // same as drawtree but these trees are positioned infront of the elephant
    strokeWeight(depth / 3 + 1);
    stroke(random( 15, 180), random(100, 244), random(10, 100)); // continuously changing green color
    line(0, 0, 0, -len);
    push();
    translate(0, -len);
    drawTree2(depth + 1, len);
    pop();
}

function drawTree2(depth, len) { // same as drawtree but these trees are positioned infront of the elephant
    if (depth < 8) {
        rotate(radians(map(mouseX, 400, 0, -5, 5))); // allows trees to rotate with mouse
        drawBranch2(depth, len);
        rotate(radians(30));
        drawBranch2(depth, len);
    }
}

function drawBranch(depth, len) { // idividual branch details
    strokeWeight(depth / 3 + 1);
    stroke(random( 15, 180), random(100, 244), random(10, 100)); // continuously changing green color
    line(0, 0, 0, -len);
    push();
    translate(0, -len);
    drawTree(depth + 1, len);
    pop();
}

function drawTree(depth, len) { // draws the two sides of the tree branches
    if (depth < 8) {
        rotate(radians(map(mouseX, 0, 400, -30, -20))); // allows trees to rotate with mouse
        drawBranch(depth, len);
        rotate(radians(30));
        drawBranch(depth, len);
    }
}

////////////////// NORTHERN LIGHTS ///////////////////
function drawNorthernLights() {
    noStroke();
    fill(random( 15, 180), random(10, 100), random(100, 244), 190); // make colors change and transparent to look like northern lights

  // make a x and y grid of circles
  for (let x = 0; x <= width; x = x + 30) {
    for (let y = 0; y <= height / 2 + 20; y = y + 30) {

      // starting point of each circle depends on mouse position
      let xAngle = map(mouseX, 0, width, -4 * PI, 4 * PI, true);
      let yAngle = map(mouseY, 0, height, -4 * PI, 4 * PI, true);

      // also varies based on the particle's location
      let angle = xAngle * (x / width) + yAngle * (y / height);

      // each particle moves in a circle
      let myX = x + 20 * cos(2 * PI * t + angle);
      let myY = y + 20 * sin(2 * PI * t + angle);

      ellipse(myX, myY, 10); // draw particle
    }
  }

  t = t + 0.01; // update time
}

////////////////// SNOWFLAKES ///////////////////

function getRandomSize() { // makes a randomly sized snowflake that is more likely to be smaller
    var r = pow(random(0, 1), 5);
    return constrain(r * 32, 2, 36);
}

class Snowflake { 

    constructor() { // snowflake object
        var x = random(width);
        var y = random(-100, -10);
        this.pos = createVector(x, y);
        this.vel = createVector(0, 0);
        this.acc = createVector();
        this.angle = random(TWO_PI);
        this.dir = (random(1) > 0.5) ? 1 : -1;
        this.xOff = 0;


        this.r = getRandomSize(); // random sizr of snowflakes
    }

    applyForce(force) {
        // parallax effect hack
        var f = force.copy();
        f.mult(this.r);

        this.acc.add(f); // add acceleration force
    }

    randomize() { // gives flakes a random starting poisiton above the canvas
        var x = random(width);
        var y = random(-100, -10);
        this.pos = createVector(x, y);
        this.vel = createVector(0, 0);
        this.acc = createVector();
        this.r = getRandomSize();
    }

    update() { // updates positions, speeds, angles and etc of snow

        this.xOff = sin(this.angle * 2) * 2 * this.r; // sine wave making snow move in addition to perlin noise

        this.vel.add(this.acc);  // velocity calculations
        this.vel.limit(this.r * 0.2);

        if (this.vel.mag() < 1) {
            this.vel.normalize();
        }

        this.pos.add(this.vel);
        this.acc.mult(0);

        if (this.pos.y > height + this.r){ //gives them a new random position when they reach the bottom of the screen
            this.randomize();
        }

        // flakes wrap left and right
        if (this.pos.x < -this.r) {
            this.pos.x = width + this.r;
        }

        this.angle += this.dir * this.vel.mag() / 200; // spin of flakes goes different directions, also dependent on their speed
    }

    render(){ // draw the snowflakes
        stroke(255);
        push();
        strokeWeight(this.r);
        translate(this.pos.x + this.xOff, this.pos.y);
        point(0, 0);
        pop();
    }
}


function mySounds() {
    // elephant sound
    // plays sound when elephants clicked on and only when trunk us down
    if (mouseIsPressed & mouseX > 150 && mouseX < 300 && mouseY > 190 && mouseY < 350 && click%2 == 0) {
        elephantSound.play();
    }

    // bird sound
    // plays sound when bush is clicked on
    if (mouseIsPressed & mouseX > 30 && mouseX < 130 && mouseY > bush2y - 20 && mouseY < bush2y + 20) {
        birdSound.play();
    }

    // tiger sound
    // plays sound when bush is clicked on
    if (mouseIsPressed & mouseX > 400 && mouseX < 480 && mouseY > bush1y - 20 && mouseY < bush1y + 20) {
        tigerSound.play();
    }
}













After following the instructions for uploading sounds onto WP, my program will run perfectly but the sounds for some reason are still not working. Therefore, I have additionally included a zip of my final project so that you can see the working sounds. All you need to do is unzip the file and open it using a local server. After troubleshooting many times, I am still unsure why the sound will not work over WordPress. Final Project

I really enjoyed doing this project, it forced me to use new techniques and functions that I never had before. I really wanted this to be an interactive picture, using many complex computations to make things move and etc.

John Legelis – Final Project

sketch

// John Legelis
// Section D
// FINAL

// Board dimensons
var b
var boardW = 200
var boardH = 350
var boardX = 10
var boardY = 10

// Oscillator dimensions/variables
var oscSpacing = boardH/4.1
var numberboxS = 20

// Dial dimensions/variables
var dR = 17
var dialStroke = 4
var dialSpacing = boardW/5
var theta = Math.PI/2
var overDial = false
var moving = false
var iy
var oldTheta
var thetaMin = -1/4*Math.PI
var thetaMax = 5/4*Math.PI

var pressed = false
var oscOn = false
var c = 2**(1/12) // Frequency diffrence between half steps
var cf = 2 **(1/100/12) // Frequency diffrence between cents
var volKnob = 0 // Index of volume knob
var pitchKnob = 1 // Index of pitch knob
var fineKnob = 2 // Index of fine knob

var wList = ["z", "x", "c", "v", "b", "n", "m", ",", ".", "/" ]
var bList = ["s", "d", false, "g", "h", "j", false, "l", ";"]



function setup() {
    createCanvas(600, 385);
    background(0);
    noFill()

    // Draw the board
    b = new ThreeWaveBoard(boardX, boardY, boardW, boardH)
    b.drawBoard()
}

function ThreeWaveBoard(X,Y,W,H) {
    // Board Dimensions
    this.bX = X
    this.bY = Y
    this.bW = W
    this.bH = H
    this.bStroke = 4
    this.oscillators = []

    // Draw board border ==> rectangle
    this.drawBoard = function() {
        stroke(255)
        strokeWeight(this.bStroke)
        rect(this.bX, this.bY, this.bW, this.bH)

        strokeWeight(2)
        rect(this.bW/2,this.bY + 50, 10,10)
        strokeWeight(1)
        textSize(15)
        text("FAT", this.bW/2 + 15, this.bY+60)
        textSize(29)
        text("3 Wave Oscillator", this.bX+8, this.bY+40)

        // Make 3 oscillators
        for (osc=0; osc<3; osc++){
            var oX = this.bX
            var oY = this.bY + oscSpacing*(osc+1)
            var oW = this.bW
            var oNum = osc + 1
            this.oscillators.push(new oscillator(oX, oY, oW, oNum))
            this.oscillators[osc].drawoscillator()
        }
    }

    // Update board
    this.updateBoard = function() {
        stroke(255)
        strokeWeight(this.bStroke)
        rect(this.bX, this.bY, this.bW, this.bH)

        strokeWeight(1)
        textSize(26)
        text("3 Wave Oscillator", this.bX+8, this.bY+40)

        // Update 3 oscillators
        for (osc=0; osc<3; osc++){
            this.oscillators[osc].updateOscillators()
        }
    }
}

function oscillator(X,Y,W,num) {
    // Oscillator Dimensions
    this.oX = X
    this.oY = Y
    this.oW = W
    this.oStroke = 4
    this.oTextStroke = 1
    this.oTextSize = 15
    this.number = num
    this.dials = []
    this.wave = new p5.Oscillator()
    this.f = false
    this.fine = 0
    this.volume = 0

    // Draw oscillator ==> line, number box, number
    this.drawoscillator = function() {
        //line and number box
        stroke(255)
        strokeWeight(this.oStroke)
        line(this.oX, this.oY, this.oX+this.oW, this.oY)
        rect(this.oX, this.oY, 20, 20)

        // Number text
        strokeWeight(this.oTextStroke)
        textSize(this.oTextSize)
        text(num, this.oX+5, this.oY+15)

        // Make volume dial
        var dX = this.oX + dR + this.oW/5 * (2)
        var dY = this.oY + oscSpacing/2
        this.dials.push(new Dial(dX, dY, dR, theta, -12, 12, "Volume"))
        this.dials[0].drawDials()

        // Make pitch dial
        var dX = this.oX + dR + this.oW/5 * (3)
        var dY = this.oY + oscSpacing/2
        this.dials.push(new Dial(dX, dY, dR, theta, -24, 24, "    Pitch"))
        this.dials[1].drawDials()

        // Make fine dial
        var dX = this.oX + dR + this.oW/5 * (4)
        var dY = this.oY + oscSpacing/2
        this.dials.push(new Dial(dX, dY, dR, theta, -12, 12, "    Fine"))
        this.dials[2].drawDials()
    }

    this.updateOscillators = function() {

        // Line and number box
        stroke(255)
        strokeWeight(this.oStroke)
        line(this.oX, this.oY, this.oX+this.oW, this.oY)
        rect(this.oX, this.oY, 20, 20)

        // Number text
        strokeWeight(this.oTextStroke)
        textSize(this.oTextSize)
        text(num, this.oX+5, this.oY+15)

        for (i=0; i<this.dials.length; i++){
            this.dials[i].drawDials()
        }
    }
}

function Dial(X, Y, R, theta, min, max, kind) {
    // Dial dimensions
    this.dialX = X
    this.dialY = Y
    this.dialR = R
    this.stroke = 4
    this.l1X = X
    this.l1Y = Y
    this.l2X = X + R*cos(theta)
    this.l2Y = Y - R*sin(theta)
    this.theta = theta
    this.over = false
    this.kind = kind
    // Ranges of each dials
    this.min = min
    this.max = max
    this.val = 0 // value of dial initialized to 0
    
    // Turns a decimal radian angle into a number rounded to a specific fration of the circle related to the ticks of the dial
    this.inc = function(dec) {
        this.range = (this.max - this.min)/2
        var increment = 3/4*Math.PI/(this.range)
        var d = dec/increment
        var r = round(d)
        var num = r * increment
        return num // Returns radian value along tick marker
    }

    // Draw dial shape and text
    this.drawDials = function() {
        this.l2X = X + R*cos(this.theta)
        this.l2Y = Y - R*sin(this.theta)
        strokeWeight(this.stroke)
        stroke(255)
        ellipse(this.dialX, this.dialY, this.dialR * 2, this.dialR * 2) // Dial circle
        line(this.l1X, this.l1Y, this.l2X, this.l2Y) // Dial line
        strokeWeight(1)
        text(this.val, this.dialX - 4, this.dialY + 32)
        text(this.kind, this.dialX - 25, this.dialY - 22)
        stroke(0)
        
    }
}

function draw() {

    stroke(255)
    noFill()
    background(0)
    b.updateBoard()
    drawPiano(230,20)

    // If a key is being pressed freq is its frequnncy
    if (keyIsPressed & !oscOn && typeof whichKey() == "string" && whichKey() == key) {
        oscOn = true

        for(waves = 0; waves < b.oscillators.length; waves++) {
            // Math for adjusting pitch and volume according to knobs
            b.oscillators[waves].f = frequencyfunc(whichKey()) * (c ** (b.oscillators[waves].dials[pitchKnob].val)) * (cf ** (b.oscillators[waves].dials[fineKnob].val))
            b.oscillators[waves].volume = 0.5 + map(b.oscillators[waves].dials[volKnob].val, -12, 12, -0.5 ,0.5)

            // Update oscilator parameters
            b.oscillators[waves].wave.amp(b.oscillators[waves].volume)
            b.oscillators[waves].wave.freq(b.oscillators[waves].f)
            b.oscillators[waves].wave.start()
        }
    }

    if (oscOn){
        // If another key is pressed while one is already down, priortize newest key
        if (whichKey != key){
            for(waves = 0; waves < b.oscillators.length; waves++) {
                b.oscillators[waves].f = frequencyfunc(whichKey()) * (c ** (b.oscillators[waves].dials[pitchKnob].val)) * (cf ** (b.oscillators[waves].dials[fineKnob].val))
                b.oscillators[waves].wave.freq(b.oscillators[waves].f)

                b.oscillators[waves].volume = 0.5 + map(b.oscillators[waves].dials[volKnob].val, -12, 12, -0.5 ,0.5)
                b.oscillators[waves].wave.amp(b.oscillators[waves].volume)

            }
        }

        if (!keyIsPressed){
            oscOn = false
        }
    }
    // Turn sounds off if oscillator is off --> no key is pressed
    if (!oscOn) {
        for(waves = 0; waves < b.oscillators.length; waves++) {
            b.oscillators[waves].wave.stop()
        }
    }

    // Check each dial in each oscillator
    for(osc=0; osc < b.oscillators.length; osc++){
        for(d=0; d < b.oscillators[osc].dials.length; d++){

            // If clicked / dragged on dial adjust the angle of specific dial respectively
            if ((dist(mouseX, mouseY, b.oscillators[osc].dials[d].dialX, b.oscillators[osc].dials[d].dialY)) <= (dR) & (mouseIsPressed) && !moving) {
                b.oscillators[osc].dials[d].over = true
                moving = true
                iy = mouseY
                oldTheta = b.oscillators[osc].dials[d].theta
            }
            if (!mouseIsPressed) {
                b.oscillators[osc].dials[d].over = false
                moving = false
            }
            if (b.oscillators[osc].dials[d].over == true) {
                var rawTheta = (oldTheta - map((iy - mouseY)*3, 0, height, 0, 2*Math.PI)) // Smooth theta from mouse
                var cTheta = constrain(rawTheta, thetaMin, thetaMax)    // Constrained theta based on min and max
                var iTheta = b.oscillators[osc].dials[d].inc(cTheta)    // Theta chopped to increment
                b.oscillators[osc].dials[d].val = round((map(iTheta, thetaMin, thetaMax, b.oscillators[osc].dials[d].max, b.oscillators[osc].dials[d].min)))
                b.oscillators[osc].dials[d].theta = iTheta
            }
        }
    }
}


// Return string value of a keyboard key, otherwise false
function whichKey(){
    if ( keyIsDown(90) || keyIsDown(83) || keyIsDown(88) || keyIsDown(68) || keyIsDown(67) || keyIsDown(86) || keyIsDown(71) || keyIsDown(66) || keyIsDown(72) || keyIsDown(78) || keyIsDown(74) || keyIsDown(77) || keyIsDown(188)|| keyIsDown(76) || keyIsDown(190)|| keyIsDown(186)|| keyIsDown(191) ) {
        k = key
    }
    if ((keyIsPressed) && (key == k)) {
        return str(k)
    }

    else{
        return false 
    }
}

// Function that draws the piano shapes and text
function drawPiano(x,y){
    var whiteW = 35
    var whiteH = 200
    var blackW = 20
    var blackH = 115

    // White Keys
    for (i=0; i<10; i++){
        push()
        translate(x,y)
        fill(255)
        rect(i*whiteW, 0, whiteW, whiteH)
        
        fill(0)
        textSize(25)
        text(wList[i],i*whiteW+ whiteW/2 - 6, whiteH - 30)
        pop()
    }

    // Black Keys
    for (j=0; j<9; j++){
        if ((j != 2) & (j != 6)) { // Exclude black key between E and F, and B and C
            push()
            translate(x,y)
            fill(0)
            rect((j+1) * whiteW - blackW/2, 0, blackW, blackH)

            fill(255)
            textSize(22)
            text(bList[j],(j+1) * whiteW - blackW/2 + 6 , blackH - 15)

            pop()
        }
    }
    fill(255)
    stroke(255)
    textSize(30)
    text("Click and Drag Dials!", 250,290)
    text("Play piano with keyboard!", 250, 340)
    noFill()
    strokeWeight(5)
    stroke(255)
    rect(240,250,340,110)
}

// Return the frequency of a keyboard key given string of key pressed
function frequencyfunc(str) {
    if (str == "z") {
        f = 523.25 // C5
    } else if (str == "s") {
        f = 554.37 // C#5
    } else if (str == "x") {
        f = 587.33 // D5
    } else if (str == "d") {
        f = 622.25 // Eb5
    } else if (str == "c") {
        f = 659.25 // E5
    } else if (str == "v") {
        f = 698.46 // F5
    } else if (str == "g") {
        f = 739.99 // F#5
    } else if (str == "b") {
        f = 783.99 // G5
    } else if (str == "h") {
        f = 830.61 // Ab5
    } else if (str == "n") {
        f = 880.00 // A5
    } else if (str == "j") {
        f = 932.33 // Bb5
    } else if (str == "m") {
        f = 987.77 // B5
    } else if (str == ",") {
        f = 1046.50 // C6
    } else if (str == "l") {
        f = 1108.73 // C#6
    } else if (str == ".") {
        f = 1174.66 // D6
    } else if (str == ";") {
        f = 1244.51 // Eb6
    } else if (str == "/") {
        f = 1318.51 // E6
    }
    
    else {
        f = false
    }

    return f
}

 

Click within the project to start!

My final project took a lot of effort on the back end side especially on the dial interaction. In order to make them clickable and draggable and easy to interface with, a lot of thinking and debugging had to be done in order for them to behave properly. Then the p5.Oscillators were used as a basis of the sound of my Oscillator. That package took some troubleshooting but eventually I worked out how to create this monophonic synthesizer. I am very proud of this instrument as it is highly functional and can be used as a real live instrument. It was lots of fun to work on and see it come together.

Min Jun Kim and Han Yu- Final Project

Supreme Invasion!

/*
Min Jun Kim & Han Yu
minjunki && hyu1
15104
Final Project 

*/

//This Project is a spaceship fighting game.

var px = 0; //x location of ship
var py = 240; //starting y location of our ship
var psize = 50; //size of ourship
var keys = []; //for controlling sihp
var Selfprojectiles = []; //array of our projectiles 
var projectileCounter = 100; //for controlling flow of projectiles
var SOE = []; //array of enemy ships
var shipSpeed = 3; //our speed for moving ship
var distanceOne = []; //distance between projectile and enemy
var distanceTwo = []; //distance between projectile and enemy
var SOEnumb = 2; //number of enemies
var timer = 0; //for controlling flow of game
var gameStage = 1; //starting stage of the game
var timerIncrement = 1; //another variable for controlling flow of game
var enemyProjectiles = []; //array of enemy bullets
var EPdistance = []; //distance between enemy bullets and our ship
var ourHealth = 10; //how much health we have left
var gameOver = 0; //activated when user dies
var clickStart = 0; //for controlling start of game
var clickCount = 0; //for making sure no double clicks
var stars = []; //array of stars in background
var imageOne; //image
var imageTwo; //image
var imageThree; //image

//making background stars
function makeStars(x, y, size) {
    return {x: x, y: y, size: size, draw: starRender, update: starUpdate}
}

//draws stars as ellipse
function starRender() {
	fill(255);
    ellipse(this.x, this.y, this.size);
}

//scrolling speed of stars
function starUpdate() {
    this.x -= 3;
}

//making our bullets
function makeOurProjectile(x, y, fast, powerful) {
	return {x: x, y: y, speed: fast, power: powerful, 
		drawIt: OPrender, updateIt: OPupdate}
}

//making enemy bullets
function makeEnemyProjectile(x,y, speedo,status) {
	return {x: x, y: y, speed: speedo, boss: status, 
		updir: random(-5,5), drawIt: EPrender, updateIt: EPupdate}
}

//draws bullets, depending on type of enemy
function EPrender() {
	//if boss, have balls instead of lines
	if (this.boss == true) {
		fill(0);
		stroke(250,0,0);
		ellipse(this.x, this.y, 10, 10);
	}
	//standard line bullets from enemy
	else {
		stroke(250, 0, 0);
		strokeWeight(3);
		line(this.x, this.y, this.x-30, this.y);
	}
	
}

//controls the movement of enemy bullets depends on enemy type
function EPupdate() {
	//if boss, then make bullets bounce and have random speed
	if (this.boss == true) {
		this.y += this.updir;
		this.x -= this.speed*random(1,2);
		//bounces bullets
		if (this.y > height || this.y < 0) {
			this.updir = -this.updir;
		}
	}
	//all other enemies have one dimensional attacks
	else {
		this.x -= this.speed;
	}
	
}

//draws our bullets as green lines
function OPrender() {
	stroke(0,200,0);
	strokeWeight(this.power);
	line(this.x, this.y, this.x+30, this.y);
}

//moves the bullet across map
function OPupdate() {
	this.x += this.speed;
}

//the specs of enemy ships
function makeSOEnemy(x, y, fast, sizing) {
	return {x: x, y: y, big: sizing, speed: fast, 
		drawIt: SOErender, update: SOEupdate, DMG: 0}
}

//controls the movement of enemy ships depending on size and location
function SOEupdate() {
	//moves the enemy along the map
	if (gameStage == 1)	 {
		this.y += this.speed;
	}
	if (gameStage == 2)	 {
		this.y += this.speed;
	}
	//if 2nd round 1st enemy, move back as game goes on
	if (gameStage == 2 & this.x < 500) {
		this.x += 0.1;
	}

	if (this.y > height) {
		this.speed = -this.speed;
	}
	if (this.y < 0) {
		this.speed = -this.speed;
	}
	
	//first round enemy with easy projectiles
	if (gameStage == 1) {
		if (random(1) > 0.98 & this.y < 600) {
			enemyProjectiles.push(makeEnemyProjectile(this.x, this.y,5));	
		}

	}
	//2nd round enemy, with more frequent bullets
	if (gameStage == 2 & this.big < 90) {
		if (random(1) > 0.93 && this.y < 600) {
			enemyProjectiles.push(makeEnemyProjectile(this.x, this.y, 5));	
		}

	}
	//pulls our ship closer and make projectiles more frequently
	if (gameStage == 2 & this.big > 90) {
		if (random(1) > 0.95 && this.y < 600) {
			enemyProjectiles.push(makeEnemyProjectile(this.x, this.y, 
				random(4,6,1), true));	
		}
		px+=0.3;

	}
}

//draws the enemies depending on the size input.
function SOErender() {
		fill(100)
		//draw the boss and health bar
		if (gameStage == 2 & this.big > 90) {
			push();
			fill(250,0,0);
			rectMode(CORNER);
			rect(5,5,width-this.DMG*6.4,20);
			pop();
			
			push();
			translate(this.x-85, this.y-60);
			scale(0.37, 0.37);
			image(imageThree,0, 0);
			pop();

		}
		//the standard enemy 
		else {
			//health bar
			push();
			fill(220, 0, 0);
			rect(this.x, this.y - this.big/1.5, 
				this.big - this.DMG*this.big/20, 10);
			pop();
			
			//standard enemy
			push();
			translate(this.x-this.big*1.4, this.y-this.big*0.5);
			scale(0.0035*this.big, 0.0035*this.big);
			image(imageTwo, 0, 0);
			pop();
		}
}


//==============================================================

//sets up the keys and the initial stars
function setup() {
   createCanvas(640,480)
   keys = [false, false, false, false, false]
   for (i = 0; i < 200; i++) {
        stars[i]=makeStars(random(width), random(height), random(5));
    }
   imageOne = loadImage("https://i.imgur.com/GHtg2vU.png");
   imageTwo = loadImage("https://i.imgur.com/svK8OkI.png");
   imageThree = loadImage("https://i.ibb.co/n0G0yGC/eae.png");
}

function draw() {
	//basic initial specs
    background(17,21,61);
    rectMode(CENTER);
    noStroke();
    fill(100);
    
    //projectile counter increases by 1
	projectileCounter += 1;
	
	//increase timer by timer increment
	timer += timerIncrement;

	//makes first round of enemies when timer is 1
	if (timer == 1 & gameStage == 1) {
		for (i = 0; i < SOEnumb; i ++) {
    		SOE[i] = makeSOEnemy(500,random(height), random(5), random(40,60));
    	}
    	timer += 1;

	}

	//states the first round
	if (timer > 1 & timer < 40) {
		textAlign(CENTER);
		push();
		stroke(255);
		fill(255);
		textSize(20);
		text("Round One!", width/2, height/2-100);
		pop();
		
	}

	//pauses the increase in timer
	if (timer > 41) {
		timerIncrement = 0;
	}

	//resumes timer once 1st round is finished
	if (gameStage == 2) {
		timerIncrement = 1;
	}

	//once timer has started moving again, write text stating the stage
	if (timer > 50 & timer < 150) {
		push();
		noStroke();
		fill(255);
		textSize(20);
		text("Final Stage!", width/2, height/2-100);
		pop();
	}

	//makes 2 more enemies once timer reaches 60
	if (timer == 60 & gameStage == 2) {
    	SOE[0] = makeSOEnemy(400,random(height), 6.5, 30);
    	SOE[1] = makeSOEnemy(550, height/2, 3, 100);
    	timer += 1;

	}

	//move right if rightarrow is pressed
    if (keys[0] == true) {
    	px += shipSpeed;	
    }

    //move left if leftarrow is pressed
    if (keys[1] == true) {
    	px -= shipSpeed;
    }

    //move up if uparrow is pressed
    if (keys[2] == true) {
    	py -= shipSpeed;
    }

    //move down if downarrow is pressed
    if (keys[3] == true) {
    	py += shipSpeed;
    }

    //add projectiles once shift is pressed and reset counter
    if (keys[4] == true & projectileCounter > 5) { //8
    	Selfprojectiles.push(makeOurProjectile(px+50, py, 10, 3))
    	projectileCounter = 0;
    }
    
    //for teleporting up and down once limit is reached
    if (py > height) {
    	py = 0;
    }
    if (py < 0) {
    	py = height;
    }
    //prevents user from going behind map by pushing back
    if (px < -5) {
    	px += 3;
    }
    //prevents user from moving too forward by pushing back
    if (px > width/2) {
    	px -= 3.3;
    }

    //makes random star objects, and initializes the functions
    noStroke()
    for (i = 0; i < 1; i++) {
        stars.push(makeStars(width, random(height), random(5)));
    }
    for (i = 0; i < stars.length; i++) {
        stars[i].draw();
        stars[i].update();

        //if stars if too far behind map, shift the array
        if (stars[i].x < -500) {
            stars.shift();
        }

    }
    

    //initializes the enemoies
    for (i = 0; i < SOEnumb; i ++) {
    	SOE[i].drawIt();
    	SOE[i].update();
    	
    }
    
    //initializes the enemy projectiles
    for (i = 0; i < enemyProjectiles.length; i++) {
    	enemyProjectiles[i].drawIt();
    	enemyProjectiles[i].updateIt();
    }

    //initializes our projectiles
    for (i = 0; i < Selfprojectiles.length; i++) {
    	Selfprojectiles[i].drawIt();
    	Selfprojectiles[i].updateIt();
    		//calculates distance if the array is nonzero and projectiles
    		//are still on map
			if (Selfprojectiles.length > 0 & Selfprojectiles[i].x < 700) {
				//calculate the distance between enemy 1 and projectile
				distanceOne[i] = dist(Selfprojectiles[i].x, 
				Selfprojectiles[i].y, SOE[0].x, SOE[0].y);
				//calculate distance between enemy 2 and projectiles
				distanceTwo[i] = dist(Selfprojectiles[i].x, 
				Selfprojectiles[i].y, SOE[1].x, SOE[1].y);

				//if off map shift distances and projectiles
				if (Selfprojectiles[i].x > width) {
					Selfprojectiles.shift();
					distanceOne.shift();
					distanceTwo.shift();
				}
				//if distance is smaller than the size of enemy 1 shift
				if (distanceOne[i] < SOE[0].big) {
					Selfprojectiles.shift();
					distanceOne.shift();
					distanceTwo.shift();
					SOE[0].DMG ++;
				}
				//if distance is smaller than the size of enemy 2 shift
				if (distanceTwo[i] < SOE[1].big) {
					Selfprojectiles.shift();
					distanceOne.shift();
					distanceTwo.shift();
					SOE[1].DMG ++;
				}
				//if enemy 1 reach damage cap, move off map prevent shooting
				if (SOE[0].DMG > 20) {
					SOE[0].y = 700;
					SOE[0].x = 700;
					SOE[0].speed = 0;
				}
				//if enemy 2 reach damage cap, move off map prevent shooting
				if (SOE[1].DMG > 20 & gameStage == 1) {
					SOE[1].y = 700;
					SOE[1].x = 700;
					SOE[1].speed = 0;
				}
				//if boss reach damage cap move off stage
				if (SOE[1].DMG > 100 & gameStage == 2) {
					SOE[1].y = 700;
					SOE[1].x = 700;
					SOE[1].speed = 0;
				}
				//if both enemies are killed during 2nd stage, end game
				if (SOE[0].y == 700 & SOE[1].y == 700 && SOE[1].DMG > 99) {
					gameStage = 3;
				}
				//if both enemnies killed during 1st stage, move up stage
				if (SOE[0].y == 700 & SOE[1].y == 700 && gameStage == 1) {
					gameStage =2;
				}

				stroke(255);
		}    
	}

    
    for (z = 0; z < enemyProjectiles.length; z++) {
    		//if enemy projectile array is nonzero, calculate distance
			if (enemyProjectiles.length > 0) {
				EPdistance[z] = dist(enemyProjectiles[z].x, enemyProjectiles[z].y,
					px, py);
				//if bullet goes off map, shift distance and bullet
				if (enemyProjectiles[z].x < 0) {
					enemyProjectiles.shift();
					EPdistance.shift();
				}
				
				//if projectile too close to ourship, shift arrays and do damage
				if (EPdistance[z] < 20 & timer != 0) {
					enemyProjectiles.shift();
					EPdistance.shift();
					ourHealth -= 1;
				}
				//if health is zero, end game
				if (ourHealth === 0) {
					gameOver = 1;
				}

			}
		}

	//draw the health bar
	push();
    rectMode(CENTER);
    noStroke();
    fill(0,200,0);
    rect(px+psize/2, py-psize/2, ourHealth*5,5);
    pop();

    //draw our ship
    push();
    noStroke();
    fill(100);
    translate(px-37,py-25);
    scale(0.15,0.15);
    image(imageOne, 0, 0);
    pop();


    //initial stage specs. Gives instructions
	if (clickStart == 0) {
		timer = 0;
		timerIncrement = 0;
		//black background
		push();
		fill(0);
		rect(width/2, height/2, 1000, 1000);
		pop();

		//design and title
		push();
		fill(255, 253, 83, 140);
		noStroke();
		triangle(150, 100, 280, 600, 550, 500);
		triangle(110, 100, 20, 500, 130, 500);
		triangle(170, 105, 700, 50, 700, 300);
		textFont("futura");
		textStyle(ITALIC);
		fill(0);
		textSize(50);
		text("Supreme", 500-85, 130-3);
		text("Invasion", 541-85, 180-3);
		pop();

		//decoration
		push();
		translate(-150,90);
		scale(0.6, 0.6);
		rotate(-0.5);
		image(imageOne, 0, 100);
		pop();

		//instructions
		push();
		noStroke();
		fill(0,0,200);
		fill(255);
		textSize(30);
		text("click to begin!", width/2+25, 440);
		fill(0);
		textSize(20);
		text("Arrow Keys to move", width/2+5, 390);
		text("Or WASD to move", width/2+5, 410);
		text("Shift to shoot", width/2-35, 370);
		pop();
	}

	//once clicked, the opening screen is changed
	if (mouseIsPressed & clickCount == 0) {
		clickCount += 1;
		timerIncrement = 1;
		clickStart = 1;	
	}
	//if game over is active, then cover screen with message
	if (gameOver > 0) {
		fill(0);
		rect(0,0, width*2, height*2);
		fill(255);
		noStroke();
		textSize(50);
		text("Game Over!", width/2, height/2);
		textSize(30);
		text("Press 'r' to restart", width/2, height/2+50);
		push();
		translate(350, 10);
		scale(0.5, 0.5);
		image(imageTwo, 0, 0);
		pop();
	}
	//if end game is activated then cover screen and say end message
	if (gameStage == 3) {
		fill(0);
		rect(width/2, height/2, 1000,1000);
		push();
		translate(460, 200);
		rotate(0.3);
		scale(0.8, 0.8);
		image(imageThree, 0, 0);
		pop();
		push();
		noStroke();
		fill(255);
		textSize(50);
		text("You Win! :)", width/2, height/2);
		textSize(20);
		text("Press 'r' to play again", width/2, height/2+50);
		pop();
	}	
}

//Controls======================================================
//==============================================================

//if key is pressed changes the array for controls
function keyPressed() {
	if (key == "ArrowRight" || key == "d" || key == "D") {
		keys[0] = true;
	}
	if (key == "ArrowLeft" || key == "a" || key == "A") {
		keys[1] = true;
	}
	if (key == "ArrowUp" || key == "w" || key == "W") {
		keys[2] = true;
	}
	if (key == "ArrowDown" || key == "s" || key == "S") {
		keys[3] = true;
	}
	if (key == "Shift") {
		keys[4] = true;
	}
	//resets game if press r
	if (key == "r" || key == "R") {
		gameOver = 0;
		gameStage = 1;
		timer = 0;
		timerIncrement = 1;
		clickCount = 0;
		clickStart = 0;
		ourHealth = 10;
		px = 0;
		py = 240;
	}
}

//releases the keys
function keyReleased() {
	if (key == "ArrowRight" || key == "d" || key == "D") {
		keys[0] = false;
	}
	if (key == "ArrowLeft" || key == "a" || key == "A") {
		keys[1] = false;
	}
	if (key == "ArrowUp" || key == "w" || key == "W") {
		keys[2] = false;
	}
	if (key == "ArrowDown" || key == "s" || key == "S") {
		keys[3] = false;
	}
	if (key == "Shift") {
		keys[4] = false;
	}
}

Instructions:
To play the game, click the initial screen to start the game. You can use either the arrow keys or “w” to move up, “a” to move left, “s” to move down, and “d” to move right (WASD). To shoot projectiles press SHIFT. The point of the game is to defeat all enemies. If you hit the enemy with your projectiles their health will go down, and once the enemy’s health hits zero, the enemy is considered defeated. On the second stage, the boss will pull the user towards the right side of the map, so be mindful. Lastly, the user cannot go horizontally beyond certain limits, and once the user reaches the top of the map the user will be teleported to the bottom of the map.

Statements:
For this final project, I focused a lot on the structure and gameplay (the backbone of the game), while my partner focused on the design aspect and some functionality of the game. My partner personally drew all the characters, which made the game look more unique. The code spans around 600 lines, and in my perspective, it was certainly difficult because of the number of objects (4) and their interaction with each other. I made sure to limit the number in arrays to make the gameplay run more smoothly and cleanly.

The gameplay is slightly different from the initial proposal, but for good reason. We initially planned to have three stages in the game, however that either made the game extremely long or difficult. We also didn’t implement a sound source for this game, because we wanted the users to be able to play the game directly in the browser. Despite these minute changes, the main concept and paradigms are true to the original.

All in all, this project was extremely fun to program. It certainly did take hours and hours to get it right, but in the end it all paid off because of the aesthetics and fun gameplay.

Han Yu Project 12 Proposal

For the final project of this class, I’m going to collaborate with Min Jun Kim. We are thinking about doing a simple video game which draws inspiration from Gradius. We are planning to make three different stages of the game: first two are competing against small enemies and the last stage will be against the boss, a black hole. We use be using objects and interactive background and sound. The game will be set in a carefully designed galaxy theme that varies with each stage. There will be multiple different characters to choose from and about 10 kinds of enemy ships.

A simple draft of the first two stages of our game.

Han Yu Looking Outwards 12

Inspiration 1: Lighting Changes with SoundThe Company by Nocte at Bring to Light Festival NYC

Nocte is a London-based team of expert that creates and designs light installations for different kinds of situations. They have been commissioned by multiple famous bands and exhibitions to create interactive visual connections that amplify the overall effects of the event. The Company is made up of 76 tungsten lamps the the brightness and on/off switch of each light bulb is interactive with the background music. The overall effect of this project is magnificent and I’d love to see it in person one day.

I think Nocte’s interactive lighting installation will be a good inspiration for my project because it will help me to build a more atmospheric background for my game-based project. I want to make the lighting and graphics of my project change along with the progression the game.

Inspiration 2: City Icon by Marcin Ignac at the Sustainable City Initiative at London in 2012.

Marcin Ignac is a Polish Artist and Programmer based in London. His work often involves visualizing data, creating artificial life forms and combining technology with art. City Icon is a series of six simulations representing different representation of London. This project is very relevant to my project because it can be incorporated into making themes and backgrounds of the game I’m making which is supposedly galaxy based.

Yoo Jin Shin-LookingOutwards-12

Interactive Resume (2018) by Robby Leonardi

The first project is an interactive resume by Robby Leonardi. It’s colorful, fun, and highly interactive. The user scrolls up or down to move the figure through Robby’s resume. It really shows off the designer’s personality. One thing is that there is no link to a portfolio of his actual works/projects.

Interactive Resume (2018) by Charles Richard

The second project is another online resume by Charles Richard. Similarly, the user scrolls up or down to navigate through Charles’s resume. However, this project uses more muted colors. One thing I noticed is that he uses circles throughout his project so it’s difficult to tell at a glance which ones are buttons and which ones are simply for aesthetics. Overall, I think both of these projects are fun, interactive ways to display one’s resume.

 

Yoo Jin Shin-Project-12-Proposal

For the final project, I would like to create some sort of interactive display or animation for the home page of my personal website. I saw one in which the home screen included an interactive color wheel in which each color block led to a different project once clicked. Another included an animation that told a short story of the portfolio owner’s background/works. For the first idea, a lot would depend on the mouse positions and including images while the second idea would be some sort of generative landscape with mostly nonrandom elements.

Sketches

Lan Wei-Project 12-Proposal

What I want to do for the final project is something about music but also has visual effects. I want the project to be interactive, meaning that people can create their own music (probably unconsciously). The detailed effect that I’ve imagined is that in the canvas of ‘universe’, people can create planets every time they click, and each zone of the canvas is related to a related piece of rhythm. By clicking in different areas of the canvas different sound effects are created. The visual effect of the planets needs some planning. I’m thinking that when a point is clicked, some repulsion is generated from this point and shapes are pushed away from the point, and thus a planet is generated. It would be nice if the planets can rotate from its position in a 3D mode and also oscillate with the volume of the rhythm. Other effects might be added to make the project more interactive and playful. I’m really looking forward to it.