Alice Fang and Jaclyn Saik – Final Project

This program uses the typeface ‘Avara’ from fontlibrary. Some of the words might not be perfectly aligned if the typeface isn’t installed. If so, here’s the zip file that includes the typeface: acfang_jsaik

And here’s a screen capture of all of the interactions: video

sketch

/*
An interactive display of Maya Angelou's poem "Still I Rise"
Click and move the mouse to interact, and press the right key to continue reading

We were inspired in part by digital children's books and "choose-your-own-adventure", except in this piece of 
work, we wanted to create simple interactions in a consistent aesthetic that would complement the words in poem.  
We also really love typography, poetry and literature, so we wanted to take the opportunity to augment a person's experience in 
the reading of a poem. 
 
We chose "Still I Rise" by Maya Angelou because we felt that her words were especially pertinent in today's social and political climate. 

Alice: Stanzas 1, 4, 8, 9, title slide, combining into one file
Jaclyn: Stanzas 3, 5, 6, 7, uploading font ('Avara') into css/html files
Stanza 2 ended up being a combination of the two of us
*/

var state = 0
var instruct = "click and explore to interact, press right key to continue";

// stanza 1
var stanza1a = ["You", "may", "write", "me", "down", "in", "history"];
var stanza1b = ["With", "your", "bitter,", "twisted", "lies,"];
var stanza1c = ["You", "may", "trod", "me", "in", "the", "very", "dirt"];
var stanza1d = ["But", "still,", "like", "dust,", "I’ll", "rise."];
var clickcountStanzaOne = 0;

//stanza 2
var stanzaSassyA = "Does my sassiness upset you?";
var stanzaSassyB = "Why are you beset with gloom?";
var stanzaSassyC = "’Cause I walk like I've got oil wells";
var stanzaSassyD = "Pumping in my living room.";
var AccentStanzaSassyA = "sassiness";
var SassyX = 50;
var SassyY = 200;
var timeKeeping = 0;
var AnotherPositionX = 180;
var AnotherPositionY = 180;
var ROLL = 0.1;
var CircleFill = 40;

// stanza 3
var stanzaThreeA = "Just like moons and like suns,";
var stanzaThreeB = "With the certainty of tides,";
var stanzaThreeC = "Just like hopes springing high,";
var stanzaThreeD = "Still I'll rise.";
var AccentStanzaA = "shoot me";
var AccentStanzaB = "cut me";
var AccentStanzaC = "kill me";
var tX = 50;
var tY = 200;
var y = 200;
var y2 = 200
var speed = 0;
var speed2 = 0;
var acceleration = 0.1;
var acceleration2 = 0.1;
var clicksStanzaThree = 0;
var Ang = 0;

// stanza 4
var stanza4 = "Did you want to see me broken?\nBowed head and lowered eyes?\nShoulders falling down like teardrops,\nWeakened by my soulful cries?";
var teardrops = "teardrops";
var tears = [];

// stanza 5
var stanzaFiveA = "Does my haughtiness offend you?";
var stanzaFiveB = "Don't you take it awful hard";
var stanzaFiveC = "’Cause I laugh like I've got gold mines";
var stanzaFiveD = "Diggin’ in my own backyard.";
var AccentStanzaFiveA = "gold";
var AccentStanzaFiveB = "haughtiness";
var AccentStanzaFiveC = "awful";
var AccentStanzaFiveD = "own";
var AccentStanzaFiveE = "laugh";
var FiveX = 50;
var Fivey = 200;
var ExRan = 10;
var terrainDetailB = 0.01;
var terrainSpeedB = 0.0002;
var imG;
var imgG2;

// stanza 6
var stanzaSixA = "You may shoot me with your words,";
var stanzaSixB = "You may cut me with your eyes,";
var stanzaSixC = "You may kill me with your hatefulness,";
var stanzaSixD ="But still, like air, I’ll rise.";
var AccentStanzaSixA = "shoot me";
var AccentStanzaSixB = "cut me";
var AccentStanzaSixC = "kill me";
var AccentStanzaSixD = "I'll rise.";
var SixtX = 50;
var SixtY = 200;

// stanza 7
var stanzaSevenA = "Does my sexiness upset you?";
var stanzaSevenB = "Does it come as a surprise";
var stanzaSevenC = "That I dance like I've got diamonds";
var stanzaSevenD = "At the meeting of my thighs?";
var AccentStanzaSexy = "sexiness";
var Sevent = 0; 
var SevenX = 50;
var SevenY = 200;
var words = [stanzaSevenA.split(" ")];
var speed = 0;
var speed2 = 0;
var acceleration = 0.1;
var acceleration2 = 0.1;

// stanza 8
var array = ["Out", "of", "the", "huts", "of", "history's", "shame"];
var array2 = ["I", "rise"];
var array3 = ["Up", "from", "a", "past", "that's", "rooted", "in", "pain"];
var array4 = ["I", "rise"];
var array5 = ["I'm", "a", "black", "ocean,", "leaping", "and", "wide,"];
var array6 = ["Welling", "and", "swelling", "I", "bear", "in", "the", "tide."];
var waveSpeed = 0.00005;
var waveDetail = 0.005;
var TimeWaveOne = 0.001;
var TimeWaveTwo = 0.0006;

// stanza 9
var stanza9a = "Leaving behind nights of terror and fear\nI rise";
var stanza9b = "Into a daybreak that’s wondrously clear\nI rise";
var stanza9c = "Bringing the gifts that my ancestors gave,\nI am the dream and the hope of the slave.";
var stanza9d = "I rise\nI rise\nI rise.";
var rows;
var cols;
var radius;
var d; 
var stateStanzaNine = 0;

function preload() { // load images for stanza 5
    var imgURL = "https://i.imgur.com/ppkgvPp.png";
    var imgURL2 = "https://i.imgur.com/QDwN1E1.png"
    imG = loadImage(imgURL);
    imgG2 = loadImage(imgURL2)
}

function setup() {
    createCanvas(480, 480);
    textFont("avara");

    // for stanza 4, pushing teardrops into array
    for (var i = 0; i < 8; i++) {
        var rainX = random(width);
        var rainY = -10;
        tears[i] = makerain(rainX, rainY);
    }
    // for stanza 9, determining grid variables
    rows = 100;
    cols = rows;
    radius = (width / rows) / 2;
    d = radius * 2;
}

function draw() {
    // change stanza when state changes; state changes when right key is pressed
    if (state === 0) {
        StanzaZero();
    } else if (state === 1) {
        StanzaOne();
    } else if (state === 2) {
        StanzaTwo();
    } else if (state === 3) {
        StanzaThree();
    } else if (state === 4) {
        StanzaFour();
        broken();
        for (var j = 0; j < tears.length; j++) { // draw and move teardrop objects
            tears[j].draw();
            tears[j].move();
        }
    } else if (state === 5) {
        StanzaFive();
    } else if (state === 6) {
        StanzaSix();
    } else if (state === 7) {
        StanzaSeven();
    } else if (state === 8) {
        wavey();
        foam();
        StanzaEight();
    } else if (state === 9 || state > 9) {
        StanzaNine();
    }
}

// introduction slide
function StanzaZero() { 
    background(66, 77, 88);
    fill('AliceBlue');
    textSize(30);
    textAlign(LEFT);
    text("Still I Rise", 50, 120);
    textSize(18);
    text("Maya Angelou", 50, 150);
    fill(240, 248, 255, 150);
    text("an interactive poem", 50, 240, 60);
    textSize(12);
    textAlign(RIGHT);
    text(instruct, 430, 430);
}

// stanza 1
function StanzaOne() { // as mouse is clicked, words appear on screen
    background(66, 77, 88);
    textSize(18);
    textAlign(LEFT);
    fill('AliceBlue');
    var offset1 = 0;
    var offset2 = 0;
    var offset3 = 0;
    var offset4 = 0;
    var yPOS = 200; // original y position of first line
        for (var i = 0; i < clickcountStanzaOne; i++) { // with each line, stanza length increases by previous line
        var sLength;
        if (i < stanza1a.length) {
           var WORD1 = textWidth(stanza1a[i]);
           text(stanza1a[i], 5 * i + offset1 + 50, yPOS);
           offset1 += WORD1; //offset determines spacing between words based of width of previous word
        } else if (i - stanza1a.length < stanza1b.length) {
          sLength = i - stanza1a.length;
          var WORD2 = textWidth(stanza1b[sLength]);
          text(stanza1b[sLength], 5 * (sLength) + offset2 + 50, yPOS + 25);
          offset2 += WORD2;
        } else if (i - stanza1a.length - stanza1b.length < stanza1c.length) {
          sLength = i - stanza1a.length - stanza1b.length;
          var WORD3 = textWidth(stanza1c[sLength]);
          text(stanza1c[sLength], 5 * (sLength) + offset3 + 50, yPOS + 50);
          offset3 += WORD3;
        } else if (i - stanza1a.length - stanza1b.length - stanza1c.length < stanza1d.length) {
          sLength = i - stanza1a.length - stanza1b.length - stanza1c.length;
          var WORD4 = textWidth(stanza1d[sLength]);
          text(stanza1d[sLength], 5 * (sLength) + offset4 + 50, yPOS + 75);
          offset4 += WORD4;
        }  
    }   
}

// stanza 2
function StanzaTwo() {
    trial(); //function  for rotating oil blobs
    StanzaSassyWords(); //function for printing stanza 2 words
    Stretch();
}

function StanzaSassyWords() {
    push();
    noStroke();
    textSize(18);
    fill('AliceBlue');
    text(stanzaSassyB, SassyX, SassyY + 30); //manually setting leading for the type 
    text(stanzaSassyC, SassyX, SassyY + 60);
    text(stanzaSassyD, SassyX, SassyY + 90);
    pop();
}

function trial() {
    fill(66, 77, 88);
    rect(-3, -3, width + 3, height + 3); //background rectangle
    for (var i = 0; i < 360; i += 3) { //for loop for the angle that the blobs rotate and generate
        var x = 0; //variables for blob's position and width
        var y = 0; 
        var w = 0;
        floor(x = cos(radians(i)) * 100 + width / 3); //floor function for whole numbers 
        floor(y = sin(radians(i)) * 100 + height / 2); 
        floor(w = sin(radians(timeKeeping/4 + i)) * 400); //the timekeeping variable keeps the blobs constantly moving
        w = abs(w); //whole and positive numbers for w, since it defines width

        float(colOR = map(i, 0, 360, 0, 30)); //color mapped from a range of gray 
        circleFill = map(mouseX, 0, width, 0, 40); //opacity mapped so it changes with mouseX
        noStroke();
        fill(colOR, circleFill);
        ellipse(x, y, w, w);
        fill(255);
    }
    timeKeeping++;
}

function Stretch() {
    push(); //stretch text vertically and horizontally based on mouse
    textSize(18);
    fill('AliceBlue');
    var MouseScaleX = map(mouseX, 100, width, 1, 2); 
    var MouseScaleY = map(mouseY, 100, height, 1, 2);
    scale(MouseScaleX, MouseScaleY);
    text(stanzaSassyA, SassyX, SassyY);
    pop();
}

// stanza 3
function StanzaThree() {
    StanzaThreeWords();
    if (clicksStanzaThree > 0) {
        BounceOne(); //call bounce function when screen is clicked
    }
    if (clicksStanzaThree > 1) {
        clicksStanzaThree = 0; //resets click count specifically for this stanza
    }
    BounceClick() //keeps click count increasing when mouse is pressed
    Moon(); //function for rotating sun and moon 
}

function StanzaThreeWords() {
    push();
    fill(66, 77, 88);
    rect(-3, -3, width + 10, height + 10); //background rectangle
    noStroke();
    textSize(18);
    textFont("Avara");
    fill(255, 255, 255);
    text(stanzaThreeA, tX, tY);
    text(stanzaThreeB, tX, tY + 30);
    text(stanzaThreeC, tX, tY + 60);
    text(stanzaThreeD, tX, tY + 90);

    stroke(66, 77, 88);
    strokeWeight(2);
    fill(66, 77, 88);
    text("springing", 183, 260); //type to cover existing type in stanza,
    text("hopes", 127, 260); //makes room for bouncing type
    noStroke();
    pop();
}

function BounceOne() { 
    textSize(18);
    fill(255);
    textFont("Avara");
    text("springing", 183, y + 60);
    y += speed;
    speed += acceleration;
    
    if (y + 60 > height || y + 60 < 1) { //invert direction if the type hits the bottom of the page 
        speed = -speed;
    }
    text("hopes", 127, y2 + 60);
    y2 += speed2;
    speed2 += acceleration2; //acceleration makes type get progressively faster 
    
    if (y2 + 60 > height || y2 + 60 < 1) { //invert direction if the type hits the bottom of the page
        speed2 = -speed2;
    }
}

function BounceClick() {
    if (mouseIsPressed) {
        clicksStanzaThree++; //adds clicks to stanza click variable
    }
}

function Moon() {
    noStroke();
    push();
    translate(240, 240); //translates center to the center of the page 
    rotate(radians(Ang)); //rotates by Angle defined globally 
    Ang += 0.7; 
    var moonX = 200; //moon and sun inversely positioned to each other 
    var moonY = 90;
    var sunX = -200;
    var sunY = -90;
    fill(255);
    ellipse(moonX, moonY, 30, 30);
    fill(66, 77, 88, 220);
    ellipse(moonX - 10, moonY, 30, 30); //moon is made of 2 ellipses
    noStroke();
    fill(255);
    sunshine(sunX, sunY); //calls function for sun rays 
    pop();
}

function sunshine(sx, sy) { //seperate function for sun rays 
    strokeWeight(2);
    stroke(255);
    line(sx - 20, sy, sx + 20, sy);
    line(sx, sy - 20, sx, sy + 20);
    line(sx - 15, sy - 15, sx + 15, sy + 15);
    line(sx + 15, sy - 15, sx - 15, sy + 15);
    stroke(66, 77, 88);
    strokeWeight(3);
    ellipse(sx, sy, 30, 30);
}

// stanza 4
function StanzaFour() {
    background(66, 77, 88);
    fill('AliceBlue');
    textSize(18);
    noStroke();
    text(stanza4, 50, 180);
    text(teardrops, 299, 225);
}

function makerain() {
    var drop = { x: random(0, width),
                y: -10,
                speed: random(1, 2),
                draw: drawrain,
                move: moverain}
    return drop;
}

function drawrain() { // create falling teardrops
    textSize(12);
    var tearOpacity = map(this.y, 0, height, 200, 0);
    stroke(240, 248, 255, tearOpacity);
    noFill();
    var tearSize = random(4, 8);
    ellipse(this.x, this.y, tearSize, tearSize);
    noStroke();
}

function moverain() {
    this.y += this.speed;
    if (this.y > height) {
        this.y = -10;
    }
}

function broken() { // as mouseY changes, background darkens, highlighting last line of stanza
    var OP = map(mouseY, 0, height, 0, 255); // map opacity to mouseY
    background(30, 30, 40, OP);
    fill('AliceBlue');
    text("Weakened by my soulful cries?", 50, 248);
}

// stanza 5
function StanzaFive() {
    DarkHill(); //background and digging person function 
    StanzaFiveWords(); //stanza word placement 
    DrawTwoo(); //function for the cursor effect
    GoldenWords(); //word interaction function 
}

function StanzaFiveWords() { //simlpe placement of stanza
    push();
    noStroke();
    textSize(18);
    textFont("Avara");
    fill(255, 255, 255);
    text(stanzaFiveA, FiveX, Fivey);
    text(stanzaFiveB, FiveX, Fivey + 30);
    text(stanzaFiveC, FiveX, Fivey + 60);
    text(stanzaFiveD, FiveX, Fivey + 90);
    pop();
}

function DrawTwoo() { //cursor effect to illuminate the mouse
    stroke(255, 215, 80);
    strokeWeight(4);
    line(mouseX, mouseY, pmouseX, pmouseY);
}

function GoldenWords() { //function for the gold words when mouse hovers 
    push();
    stroke(255, 215, 80); //includes background type and also 
    strokeWeight(1);
    textSize(18);
    textFont("Avara");
    fill(255, 215, 80);
    ExRan = random(0, 2);

    if (dist(mouseX, mouseY, 285 + 5, 260) < 40) {
        text(AccentStanzaFiveA, 285, 260); // "gold"
        strokeWeight(3);
        textSize(27);
        textFont("Avara");
        fill(255, 215, 80, 20);
        stroke(255, 215, 80, 20);
        text(AccentStanzaFiveA, 285 + ExRan, 260 + ExRan); // "gold"
    } 
    if (dist(mouseX, mouseY, 135 + 10, 200) < 40) {
        text(AccentStanzaFiveB, 135, 200); // "haughty"
        strokeWeight(3);
        textSize(27);
        textFont("Avara");
        fill(255, 215, 80, 20);
        stroke(255, 215, 80, 20);
        text(AccentStanzaFiveB, 135 + ExRan, 200 + ExRan); // "haughty"
    }
    if (dist(mouseX, mouseY, 201 + 5, 230) < 40) {
        text(AccentStanzaFiveC, 201, 230); // "awful"
        strokeWeight(3);
        textSize(27);
        textFont("Avara");
        fill(255, 215, 80, 20);
        stroke(255, 215, 80, 20);
        text(AccentStanzaFiveC, 201 + ExRan, 230 + ExRan); // "awful"
    }
    if (dist(mouseX, mouseY, 182 + 5, 290) < 40) {
        text(AccentStanzaFiveD, 182, 290); // "own"
        strokeWeight(3);
        textSize(27);
        textFont("Avara");
        fill(255, 215, 80, 20);
        stroke(255, 215, 80, 20);
        text(AccentStanzaFiveD, 182 + ExRan, 290 + ExRan); // "own"
    }
    if (dist(mouseX, mouseY, 124 + 5, 260) < 40) {
        text(AccentStanzaFiveE, 124, 260); // "laugh"
        strokeWeight(3);
        textSize(27);
        textFont("Avara");
        fill(255, 215, 80, 20);
        stroke(255, 215, 80, 20);
        text(AccentStanzaFiveE, 124 + ExRan, 260 + ExRan); // "laugh"
    }
    noStroke();
    pop();
}

function DarkHill() { //function for background hill and digging
    push();
    background(66, 77, 88); //generative landscape based on milliseconds
    beginShape(); 
    stroke(0, 30);
    for (var x = 0; x < width; x++) { //
        var t = (x * terrainDetailB) + (millis() * terrainSpeedB);
        var y = map(noise(t), 0,1, 100, 200);
        line(x, y, x, height); 
    }
    endShape();
    pop();

    if (dist(mouseX, mouseY, 285 + 5, 260) < 80) { //mouse trigger for the digging person to animate 
        image(imgG2, 100, 100);
    } else {
        image(imG, 100, 100);
    }
}
// stanza 6
function StanzaSix() {
    background(66, 77, 88);
    Shootin(); //function for stanza six type 
    Blinds(); //function for closing blinds
    Accent(); //function for accent type above blinds  
}

function Shootin() { 
    push();
    fill(66, 77, 88);
    rect(0, 0, width, height);
    noStroke();
    textSize(18);
    textFont("Avara");
    fill(255, 255, 255);
    text(stanzaSixA,SixtX,SixtY);
    text(stanzaSixB,SixtX,SixtY + 30);
    text(stanzaSixC,SixtX,SixtY + 60);
    text(stanzaSixD,SixtX,SixtY + 90);
    pop();
}

function Blinds() {
    noStroke();
    var whyOne = max(0, min(mouseY, 479));
    var gentleOne = 0.5; //determines easing function for each "blind"
    var gentleTwo = 0.7;
    var y = 1; //y length for blind 1
    var y2 = 1; //y length for blind 2

    var disy = whyOne - y; //finds distance between mapped y vaiable and y position 
    var disy2 = whyOne - y2;
    y += disy * gentleOne; //applies distance to easing 
    y2 += disy2 * gentleTwo;

    //blinds 1
    fill(30, 30, 40, 220);//opacity 
    rect(480, 0, -y, 480); //negative y value so that the blinds open and close on both sides 
    rect(0, 0, y, 480)
    //blinds2
    fill(30, 30, 40, 220);
    rect(480, 0, -y2, 480);
    rect(0, 0, y2, 480)
}

function Accent() { //simple function for placing selected accent words on top of blinds
    fill(255);
    textSize(18);
    textFont("Avara");
    text(AccentStanzaSixA, SixtX + 83.5, SixtY);
    text(AccentStanzaSixB, SixtX + 83.5, SixtY + 30);
    text(AccentStanzaSixC, SixtX + 83.5, SixtY + 60);
    text(AccentStanzaSixD, SixtX + 152.5, SixtY + 90);
}

// stanza 7
function StanzaSeven(){
    mouseGlow(); //mouse background function to make type more readible when mouse hovers 
    wavee() //wave function for diamond pattern 
    Sexy1(); //basic stanza type 
    if (mouseIsPressed) {
        Sexy2(); //second type interaction 
    }
}
function Sexy1() { //basic stanza type 
    push();
    noStroke();
    buffer = dist(mouseX, mouseY, SevenX + 150, SevenY + 40);
    textSize(18);
    textFont("Avara");
    fill('AliceBlue');
    text(stanzaSevenA, SevenX, SevenY);
    text(stanzaSevenB, SevenX, SevenY + 30);
    text(stanzaSevenC, SevenX, SevenY + 60);
    text(stanzaSevenD, SevenX, SevenY + 90);
    pop();
  }

function mouseGlow() {
    noStroke();
    fill(66, 77, 88);
    ellipse(mouseX, mouseY, 120, 120); //ellipse follows mouse, function placed BEHIND 
    //all other functions so this becomes another background
}

function wavee() {
    background(66, 77, 88, 20); 
    noFill();
    stroke(255, 180);
    strokeWeight(1);
    for (var x = 0; x <= width; x = x + 90) { 
        for (var y = 0; y <= height; y = y + 60) { //nested for loop defines grid system for diamonds
            var xAngle = map(mouseX, 0, width, -4 * PI, 4 * PI, true); //maps angle around a circle so it's based on 
            var yAngle = map(mouseY, 0, height, -4 * PI, 4 * PI, true); //the mouse x and y positions 
            var angle = xAngle * (x / width) + yAngle * (y / height); //angle tells diamond to osccillate
            var myX = x + 20 * cos(2 * PI * Sevent + angle); //synthesize x and y angles to one data point for each diamond
            var myY = y + 20 * sin(2 * PI * Sevent + angle);//also makes the diamond change over time regardless of mouse
            Diamond(myX, myY);
        }
    }
    Sevent = Sevent + 0.01; //time function 
}
function Diamond(vx, vy) { //function to manually draw diamond shapes 
    noFill();
    beginShape();
    vertex(vx, vy);
    vertex(vx + 4, vy);
    vertex(vx + 8, vy + 4);
    vertex(vx, vy + 12);
    vertex(vx - 8, vy + 4);
    vertex(vx - 4, vy);
    endShape(CLOSE);

    beginShape();
    vertex(vx - 8, vy + 4);
    vertex(vx + 8, vy + 4);
    vertex(vx, vy + 12);
    vertex(vx - 4, vy + 4);
    vertex(vx, vy);
    vertex(vx + 4, vy + 4);
    vertex(vx, vy + 12);
    endShape(CLOSE);
}

function Sexy2() { //function for interaction with the words "sexinesss"
    textSize(18);
    textFont("Avara");
    fill(0);
    fill(255, 215, 80);
    noStroke();
    text(AccentStanzaSexy, 134.5, 200);
}

// stanza 8
function StanzaEight() { // "wavy" text, affected by mouseX and mouseY
    fill('AliceBlue');
    noStroke();
    var offset1 = 0;
    var yPOS = 85;
    for (var a = 0; a < array.length; a++) { //first line
        var WORD1 = textWidth(array[a]);
        text(array[a], 5 * a + offset1 + 50, yPOS);
        offset1 += WORD1; // offset determines spacing between words based on width of previous word

        var d = dist(mouseX, mouseY, 5 * a + offset1 + 10, yPOS);
        if (d > 0 & d < 20) { // based on distance, word will rise/ fall
            yPOS += 1;
        }
        if (d > 0 & d < 35) {
            yPOS += 5;
        }
        if (d > 0 & d < 50) {
            yPOS += 8;
        }
    }
    var offset2 = 0;
    var yPOS2 = 135;
    for (var b = 0; b < array2.length; b++) { //second line
        var WORD2 = textWidth(array2[b]);
        text(array2[b], 5 * b + offset2 + 50, yPOS2);
        offset2 += WORD2;

        var dd = dist(mouseX, mouseY, 5 * b + offset2 + 10, yPOS2);
        if (dd > 0 & dd < 20) { // based on distance, word will rise/ fall
            yPOS2 += 1;
        }
        if (dd > 0 & dd < 35) {
            yPOS2 += 5;
        }
        if (dd > 0 & dd < 50) {
            yPOS2 += 8;
        }
    }
    var offset3 = 0;
    var yPOS3 = 185;
    for (var c = 0; c < array3.length; c++) { //third line
        var WORD3 = textWidth(array3[c]);
        text(array3[c], 5 * c + offset3 + 50, yPOS3);
        offset3 += WORD3;

        var ddd = dist(mouseX, mouseY, 5 * c + offset3 + 10, yPOS3);
        if (ddd > 0 & ddd < 20) { // based on distance, word will rise/ fall
            yPOS3 += 1;
        }
        if (ddd > 0 & ddd < 35) {
            yPOS3 += 5;
        }
        if (ddd > 0 & ddd < 50) {
            yPOS3 += 8;
        }
    }
    var offset4 = 0;
    var yPOS4 = 235;
    for (var d = 0; d < array4.length; d++) { //fourth line
        var WORD4 = textWidth(array4[d]);
        text(array4[d], 5 * d + offset4 + 50, yPOS4);
        offset4 += WORD4;

        var dddd = dist(mouseX, mouseY, 5 * d + offset4 + 10, yPOS4);
        if (dddd > 0 & dddd < 20) { // based on distance, word will rise/ fall
            yPOS4 += 1;
        }
        if (dddd > 0 & dddd < 35) {
            yPOS4 += 5;
        }
        if (dddd > 0 & dddd < 50) {
            yPOS4 += 8;
        }
    }
    var offset5 = 0;
    var yPOS5 = 285;
    for (var e = 0; e < array5.length; e++) { //fifth line
        var WORD5 = textWidth(array5[e]);
        text(array5[e], 5 * e + offset5 + 50, yPOS5);
        offset5 += WORD5;

        var ddddd = dist(mouseX, mouseY, 5 * e + offset5 + 10, yPOS5);
        if (ddddd > 0 & ddddd < 20) { // based on distance, word will rise/ fall
            yPOS5 += 1;
        }
        if (ddddd > 0 & ddddd < 35) {
            yPOS5 += 5;
        }
        if (ddddd > 0 & ddddd < 50) {
            yPOS5 += 8;
        }
    }
    var offset6 = 0;
    var yPOS6 = 335;
    for (var f = 0; f < array6.length; f++) { //sixth line
        var WORD6 = textWidth(array6[f]);
        text(array6[f], 5 * f + offset6 + 50, yPOS6);
        offset6 += WORD6;

        var dddddd = dist(mouseX, mouseY, 5 * f + offset6 + 10, yPOS6);
        if (dddddd > 0 & dddddd < 20) { // based on distance, word will rise/ fall
            yPOS6 += 1;
        }
        if (dddddd > 0 & dddddd < 35) {
            yPOS6 += 5;
        }
        if (dddddd > 0 & dddddd < 50) {
            yPOS6 += 8;
        }
    }
}

// create wave and foam for stanza 8
function wavey() {
    background(66, 77, 88);
    beginShape();
    stroke(30, 30, 40);
    for (var w = 0; w < width; w++) {
        var t = (w * TimeWaveOne) + (millis() * TimeWaveTwo);
        var waveY = map(noise(t), 0, 0.5, 0, mouseY);
        line(w, waveY, w, width);
    } endShape();
}

function foam() {
    beginShape();
    stroke(255, 255, 255, 90);
    for (var waveF = 0; waveF < width; waveF++) {
        var t = (waveF * TimeWaveOne) + (millis() * TimeWaveTwo);
        var foamY = map(noise(t), 0, 0.5, 0, mouseY);
        vertex(waveF, foamY - 5);
        vertex(waveF, foamY - 5);
    } endShape();
}   

// stanza 9
function StanzaNine() {
    if (stateStanzaNine == 0) { //highlight only first two lines
        fill('AliceBlue');
        stroke(0);
        textSize(18);
        text(stanza9a, 50, 125);
        if (mouseX > 40 & mouseX < 140 && mouseY > 105 && mouseY < 165) {
            fill(66, 77, 88);
            text("rise", 62.25, 147.25) //change color when mouse hovers to indicate "click"
        }
    }
    if (stateStanzaNine == 1) { //highlight only third and fourth lines
        fill('AliceBlue');
        stroke(0);
        text(stanza9b, 50, 170);
        if (mouseX > 40 & mouseX < 140 && mouseY > 150 && mouseY < 215) {
            fill(66, 77, 88);
            text("rise", 62.25, 192.25) //change color when mouse hovers to indicate "click"
        }
    }
    if (stateStanzaNine == 2) { //highlight only fifth and sixth lines
        fill('AliceBlue');
        text(stanza9c, 50, 215);
        stroke(0);
        fill(255, 215, 80);
        text("hope", 268.25, 237.5); //change color when mouse hovers to indicate "click"
    }
    drawGrid(); // create spotlight
    
    if (stateStanzaNine == 3) { //reveal last stanza
        background(255, 250, 205);
        fill(110);
        text(stanza9a, 50, 125);
        text(stanza9b, 50, 170);
        text(stanza9c, 50, 215);
        fill(0);
        text(stanza9d, 50, 300);
    }
}

function drawGrid() { //create spotlight grid
    for (var gridY = 0; gridY < height; gridY += d) {
        for (var gridX = 0; gridX < width; gridX += d) {
            var modifiedX = gridX;
            if ((gridY % (d * 2)) === 0) {
                modifiedX = gridX + radius;
            }
            fill(0);
            var distance = dist(mouseX, mouseY, modifiedX, gridY);
            opacity = map(distance, 0, 160, 0, 255); //zero opacity at center of spotlight, fade as dist increases
            noStroke();
            fill(0, 0, 0, opacity);
            rectMode(CENTER);
            rect(gridX, gridY, d, d);
        }
    }
}

// for stanza nine
function mouseClicked() { //range for click-ability to switch lines
    if (state === 9 & mouseX > 40 && mouseX < 140 && mouseY > 105 && mouseY < 165) { //if clicked in this range, next two lines will be revealed
        stateStanzaNine = 1;
    } else if (state === 9 & mouseX > 40 && mouseX < 140 && mouseY > 150 && mouseY < 215) { //if clicked in this range, next two lines will be revealed
        stateStanzaNine = 2;
    } else if (state === 9 & mouseX > 268 && mouseX < 310 && mouseY > 223 && mouseY < 243) { //if clicked in this range, reveal all of stanza
        stateStanzaNine = 3;
    }
}

// for global state change
function keyPressed() {
    if (keyCode === RIGHT_ARROW) { //when right key is pressed, change stanzas
        state++;
    }
}

// for stanza one
function mousePressed() { //allow text to appear word by word in stanza one
    clickcountStanzaOne++;
}

Both of us are very interested in poetry and literature, so for this final project we wanted to take the opportunity to use animation and mouse interaction to augment the experience of reading a poem. We were inspired in part by digital children’s books and “choose-your-own-adventure”, except in this piece of work, we wanted to create simple interactions in a consistent aesthetic that would complement the words in poem.  We chose “Still I Rise” by Maya Angelou because we felt that her words were especially pertinent in today’s social and political climate.

This project was challenging to accomplish, and luckily we were able to work together well in order to tackle bigger problems. One of the most time-consuming struggles we encountered was the process of uploading, arranging, aligning, ordering and placing all of the type: we had to learn a couple new functions in order to manipulate the text in certain ways and treat some words as objects. We got sucked into the technicalities of typography, and spend more time than we should have finding and uploading the correct typeface. In the treatment of individual words, when placing words from a string in a for loop to appear one at a time, spacing became a tricky thing to deal with because our typeface wasn’t monospace. Another thing we struggled with was synthesizing all of our work. Because we would create sketches for each slide in a separate file, we had to establish a guide for how we labeled our variables so that they didn’t conflict. We also had to be careful about universal functions like mousePressed and keyPressed.

Inspiration credit:
p5.js official Wavemaker exampled
bounce function from p5.js official examples
Yasai openprocessing CircleShift Sketch

Even if we were working on different stanzas, we were able to talk problems out with each other and give advice. Although Alice focused on the opening slide, stanzas 1, 4, 8, and 9, and Jaclyn focused on stanzas 2, 3, 5, 6, and 7, the collaborative nature of our workflow allowed both of us to have a holistic influence to the project.
Overall, we are happy how this project turned out, and excited that we got to explore typography within p5.js.

Rachel Park – Final Project

Project Description & Instructions Below!!

finalproject

// Name: Rachel Park
// Andrew ID: rjpark
// 15-104 Section C

var i;
var currentkey = 'c';
var choreography = [];

var contemporary = [];
var groovy = [];
var footwork = [];
var isolation = [];
var pretty = [];
var swaggySassy = [];
var tutting = [];
var wavesWacking = [];

function preload() {
    // database of dance moves
    // loading the videos
    contemporary = ['C1.MOV', 'C2.MOV', 'C3.MOV', 'C4.MOV', 'C5.MOV',
        'C6.MOV', 'C7.MOV', 'C8.MOV', 'C9.MOV', 'C10.MOV', 'C11.MOV', 'C12.MOV',
        'C13.MOV', 'C14.MOV', 'C15.MOV', 'C16.MOV', 'C17.MOV', 'C18.MOV',
        'C19.MOV', 'C20.MOV', 'C21.MOV', 'C22.MOV', 'C23.MOV', 'C24.MOV',
        'C25.MOV', 'C26.MOV', 'C27.MOV', 'C28.MOV', 'C29.MOV', 'C30.MOV',
        'C31.MOV', 'C32.MOV', 'C33.MOV', 'C34.MOV', 'C35.MOV', 'C36.MOV',
        'C37.MOV', 'C38.MOV',];

    for (var j = 0; j < contemporary.length; j ++) {
        contemporary[j] = './Moves/C/' + contemporary[j];
    }

    groovy = ['G1.MOV', 'G2.MOV', 'G3.MOV', 'G4.MOV', 'G5.MOV', 'G6.MOV',
        'G7.MOV', 'G8.MOV', 'G9.MOV', 'G10.MOV', 'G11.MOV', 'G12.MOV',
        'G13.MOV', 'G14.MOV'];

    for (var j = 0; j < groovy.length; j ++) {
        groovy[j] = './Moves/G/' + groovy[j];
    }

    footwork = ['F1.MOV', 'F2.MOV', 'F3.MOV', 'F4.MOV', 'F5.MOV', 'F6.MOV',
        'F7.MOV', 'F8.MOV', 'F9.MOV', 'F10.MOV', 'F11.MOV', 'F12.MOV',
        'F13.MOV', 'F14.MOV', 'F15.MOV', 'F16.MOV', 'F17.MOV', 'F18.MOV',
        'F19.MOV', 'F20.MOV', 'F21.MOV'];

    for (var j = 0; j < footwork.length; j ++) {
        footwork[j] = './Moves/F/' + footwork[j];
    }

    isolation = ['I1.MOV', 'I2.MOV', 'I3.MOV','I4.MOV', 'I5.MOV', 'I6.MOV',
        'I7.MOV', 'I8.MOV', 'I9.MOV', 'I10.MOV', 'I11.MOV', 'I12.MOV',
        'I13.MOV', 'I14.MOV', 'I15.MOV', 'I16.MOV', 'I17.MOV', 'I18.MOV',
        'I19.MOV', 'I20.MOV', 'I21.MOV', 'I22.MOV', 'I23.MOV', 'I24.MOV',
        'I25.MOV', 'I26.MOV', 'I27.MOV', 'I28.MOV', 'I29.MOV', 'I30.MOV',
        'I31.MOV', 'I32.MOV', 'I33.MOV', 'I34.MOV', 'I35.MOV'];

    for (var j = 0; j < isolation.length; j ++) {
        isolation[j] = './Moves/I/' + isolation[j];
    }

    pretty = ['P1.MOV', 'P2.MOV', 'P3.MOV', 'P4.MOV', 'P5.MOV', 'P6.MOV',
        'P7.MOV', 'P8.MOV', 'P9.MOV', 'P10.MOV', 'P11.MOV', 'P12.MOV',
        'P13.MOV', 'P14.MOV', 'P15.MOV', 'P16.MOV', 'P17.MOV', 'P18.MOV',
        'P19.MOV', 'P20.MOV', 'P21.MOV', 'P22.MOV', 'P23.MOV', 'P24.MOV',
        'P25.MOV', 'P26.MOV', 'P27.MOV', 'P28.MOV', 'P29.MOV'];

    for (var j = 0; j < pretty.length; j ++) {
        pretty[j] = './Moves/P/' + pretty[j];
    }

    swaggySassy = ['S1.MOV', 'S2.MOV', 'S3.MOV', 'S4.MOV', 'S5.MOV', 'S6.MOV',
        'S7.MOV', 'S8.MOV', 'S9.MOV', 'S10.MOV', 'S11.MOV','S12.MOV', 'S13.MOV',
        'S14.MOV', 'S15.MOV', 'S16.MOV', 'S17.MOV', 'S18.MOV', 'S19.MOV',
        'S20.MOV', 'S21.MOV', 'S22.MOV', 'S23.MOV', 'S24.MOV', 'S25.MOV',
        'S26.MOV', 'S27.MOV', 'S28.MOV', 'S29.MOV', 'S30.MOV', 'S31.MOV',
        'S32.MOV'];

    for (var j = 0; j < swaggySassy.length; j ++) {
        swaggySassy[j] = './Moves/S/' + swaggySassy[j];
    }

    tutting = ['T1.MOV', 'T2.MOV', 'T3.MOV', 'T4.MOV', 'T5.MOV', 'T6.MOV',
        'T7.MOV', 'T8.MOV', 'T9.MOV', 'T10.MOV', 'T11.MOV', 'T12.MOV',
        'T13.MOV', 'T14.MOV', 'T15.MOV', 'T16.MOV', 'T17.MOV', 'T18.MOV',
        'T19.MOV', 'T20.MOV', 'T21.MOV', 'T22.MOV', 'T23.MOV', 'T24.MOV',
        'T25.MOV', 'T26.MOV', 'T27.MOV', 'T28.MOV', 'T29.MOV', 'T30.MOV',
        'T31.MOV', 'T32.MOV', 'T33.MOV', 'T34.MOV', 'T35.MOV', 'T36.MOV',
        'T37.MOV', 'T38.MOV'];

    for (var j = 0; j < tutting.length; j ++) {
        tutting[j] = './Moves/T/' + tutting[j];
    }

    wavesWacking = ['W1.MOV', 'W2.MOV', 'W3.MOV', 'W4.MOV', 'W5.MOV', 'W6.MOV',
        'W7.MOV', 'W8.MOV', 'W9.MOV', 'W10.MOV', 'W11.MOV', 'W12.MOV',
        'W13.MOV'];

    for (var j = 0; j < wavesWacking.length; j ++) {
        wavesWacking[j] = './Moves/W/' + wavesWacking[j];
    }
}

function setup() {
    createCanvas(700, 355);
    background(0);
    fill(255);
    noStroke();
    rect(600, 0, 100, 355);
}

function draw() {
    // title
    fill("pink");
    stroke(0);
    strokeWeight(3);
    textAlign(CENTER);
    textSize(30);
    text("Make Your Own Choreography!!", 300, 45);

    // box for reference
    fill(255);
    stroke("pink");
    strokeWeight(3);
    rectMode(CENTER);
    rect(85, 170, 120, 140);

    // text in reference box
    fill(0);
    noStroke();
    textAlign(CENTER);
    textSize(12);
    text("c = contemporary", 85, 117);
    text("f = footwork", 85, 133);
    text("g = groovy", 85, 149);
    text("i = isolation", 85, 165);
    text("p = pretty/feminine", 85, 181);
    text("s = swaggy/sassy", 85, 197);
    text("t = tutting", 85, 213);
    text("w = wavy/wacking", 85, 229);
    fill(255);
    stroke(0);
    strokeWeight(1);
    text("Letter Key Chart", 85, 90);

    // box for which letter key is pressed
    fill(255);
    stroke("pink");
    strokeWeight(3);
    rectMode(CENTER);
    rect(205, 127, 55, 55);

    // display the letter key that's pressed
    fill(0);
    noStroke();
    textAlign(CENTER);
    textSize(40);
    text(currentkey, 205, 140);
    fill(255);
    stroke(0);
    strokeWeight(1);
    textSize(12);
    text("Last Key Pressed", 205, 90);

    // box for play button
    fill(255);
    stroke("pink");
    strokeWeight(3);
    rectMode(CENTER);
    rect(205, 213, 55, 55);

    // display the play sign
    fill(0);
    noStroke();
    triangle(195, 200, 195, 230, 220, 215);
    fill(255);
    stroke(0);
    strokeWeight(1);
    textSize(12);
    text("Play Button", 205, 175);

    // box for instructions
    fill(255);
    stroke("pink");
    strokeWeight(3);
    rectMode(CENTER);
    rect(400, 170, 260, 140);

    // text in instructions box
    fill(0);
    noStroke();
    textAlign(LEFT);
    textSize(10);
    text("1. Look at the letter key chart & choose a letter to press", 
        280, 115);
    text("2. Press the letter key (displayed in the box to the right)", 
        280, 134);
    text("3. Keep pressing desired letter keys", 280, 153);
    text("4. When finished pressing letter keys, click the play", 280, 172);
    text("button to watch your choreography", 280, 191);
    text("5. Repeat steps 1-4 to keep adding to choreography", 280, 210);
    text("6. Refresh page to start over", 280, 229)
    fill(255);
    stroke(0);
    strokeWeight(1);
    textAlign(CENTER);
    textSize(12);
    text("Instructions", 400, 90);

    // side note & credit/shoutout to dancers
    fill(255);
    stroke(0);
    strokeWeight(1);
    textAlign(LEFT);
    textSize(10);
    text("** The objective of this project is to help you create a sequence" + 
        "of dance moves (choreography) that can be adjusted", 30, 280);
    text("and modified to match a song you choose when actually" +
        "choreographing.", 30, 295);
    text("** Also, shoutout to the featured dancers (by letter key chart);" +
        "Léann Bahi, Elizabeth Kuo, Jackie Jiang, Chris Shon,", 30, 320);
    text("Maggie Lyu, Emily Wu, Newton Xie, and Yuyan Sun.", 30, 335);
}

function getRandomInt(min, max) {
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min)) + min;
}

function keyTyped() {
    document.getElementById("danceVideo").muted = true;

    // assigning pressed keys to a dance move video
    // playing that dance move video
    // adding each dance move video into a new array called choreography
    if (key === 'c') {
        currentkey = key;
        i = getRandomInt(0, contemporary.length);
        choreography.push(contemporary[i]);
        document.getElementById("video").src = str(contemporary[i]);
        document.getElementById("danceVideo").load();
        document.getElementById("danceVideo").play();
    }

    if (key === 'g') {
        currentkey = key;
        i = getRandomInt(0, groovy.length);
        choreography.push(groovy[i]);
        document.getElementById("video").src = str(groovy[i]);
        document.getElementById("danceVideo").load();
        document.getElementById("danceVideo").play();
    }

    if (key === 'f') {
        currentkey = key;
        i = getRandomInt(0, footwork.length);
        choreography.push(footwork[i]);
        document.getElementById("video").src = str(footwork[i]);
        document.getElementById("danceVideo").load();
        document.getElementById("danceVideo").play();
    }

    if (key === 'i') {
        currentkey = key;
        i = getRandomInt(0, isolation.length);
        choreography.push(isolation[i]);
        document.getElementById("video").src = str(isolation[i]);
        document.getElementById("danceVideo").load();
        document.getElementById("danceVideo").play();
    }
    if (key === 'p') {
        currentkey = key;
        i = getRandomInt(0, pretty.length);
        choreography.push(pretty[i]);
        document.getElementById("video").src = str(pretty[i]);
        document.getElementById("danceVideo").load();
        document.getElementById("danceVideo").play();
    }

    if (key === 's') {
        currentkey = key;
        i = getRandomInt(0, swaggySassy.length);
        choreography.push(swaggySassy[i]);
        document.getElementById("video").src = str(swaggySassy[i]);
        document.getElementById("danceVideo").load();
        document.getElementById("danceVideo").play();
    }

    if (key === 't') {
        currentkey = key;
        i = getRandomInt(0, tutting.length);
        choreography.push(tutting[i]);
        document.getElementById("video").src = str(tutting[i]);
        document.getElementById("danceVideo").load();
        document.getElementById("danceVideo").play();
    }

    if (key === 'w') {
        currentkey = key;
        i = getRandomInt(0, wavesWacking.length);
        choreography.push(wavesWacking[i]);
        document.getElementById("video").src = str(wavesWacking[i]);
        document.getElementById("danceVideo").load();
        document.getElementById("danceVideo").play();
    }
}

function mouseClicked() {
    // plays through all videos in choreography array
    var nextVideos = function(i) {
       document.getElementById('video').src = choreography[i];

       var videoElement = document.getElementById('danceVideo');
       videoElement.load();
       videoElement.play();

       // checks to see if there are videos left to play (i < array length)
       // checks to see if current video has ended to play next video in array
       if (i < choreography.length) {
            videoElement.onended = function() {
                nextVideos(i + 1);
            }
        }
    }

    // clicking the play button will play back entire choreography
    if (mouseX > 205 - (55 / 2) & mouseX < 205 + (55 / 2)) {
        if (mouseY > 213 - (55 / 2) && mouseY < 213 + (55 / 2)) {
            nextVideos(0);
        }
    }
}

Statement

As stated in my final project proposal, I wanted to create something that was going to be personally interesting to me. So, I decided to create a visual and interactive computer keyboard dance generator. The objective of my project was to allow the user to compile a bunch of dance moves that are generated based off of the letter key that they pressed. This would help the user create their own choreography that can be adjusted and modified to fit a song they choose when they actually choreograph.

In order to create this, I had to collect a database of dance moves. So, I recorded various dancers on campus dancing in a specific dance style, and, from this, I gathered videos of 220 unique dance moves. Once I collected my videos, I loaded them into code and assigned specific dancers/dance style to different arrays. Next, I essentially paired certain letter keys to arrays so that, when the user presses a letter key, a random dance video (dance move) from the paired array would play. In addition, as the user presses a letter key, the corresponding dance videos are added to a “choreography” array which contains the entire choreography that the user makes and can be played by pressing the play button at the end.

My greatest struggle with this project was getting all the videos in the “choreography” array to play in succession. I had to do a lot of outside research to figure out how to see if one video has ended in the array for another one to start. I used document.getElementById().src, load, play, and onended to make this happen.

Below is a screenshot of what the project looks like as well as instructions on how to install and run it.

Screenshots

** can’t show the interactiveness of the project through screenshots; click the link below and follow instructions to use it!

Instructions

  1. Press the link below (you will only get access with CMU Andrew email).
  2. Download the file (it is safe to download & it will take a while).
  3. Open the folder Final Project.
  4. Open the index.html file.
  5. Read the instructions on the web page to play!

Choreography Generator Download Link

Jenny Hu — Final Project

For my final project, I created a simple step sequencer with 4 sounds and background music! You can add sounds at different points by toggling the squares and circles (the squares indicate a note switched off, and a circle indicates a note switched on), and change the speed the music player loops through the sequencer on the bpm toggle.

I really enjoyed making this because it became about the curation of the sounds and music,  letting anyone generate a soothing atmosphere.

Jenny’s sketch

//Jenny Hu
//Section E
//jjh1@andrew.cmu.edu
//Final Project

//This final project is a simple step sequencer.
//Sounds are loaded into each row,
//as the music player passes each note (switched on), it plays the sound
//squares indicate switched off notes, and circles indicate the ones switched on.

var playing = false;
var notesArray = []; //all notes are pushed into this array

//sizing variables
var totalC = 15; 
var canvasWidth = window.innerWidth; //sizing of everything should overall be parametric to this width.
var margin = 0.02 * canvasWidth; //space between notes and everything else
var noteW = (canvasWidth - (totalC + 1) * margin)/ totalC; //calculated width of each square/circle
var canvasHeight = 5 * margin + 4 * noteW + (noteW*4); 

//variables key to moving and playing components
var playButtonW = margin ; //big play/pause button
var baseLineX = margin; 
var baseLineY = (canvasHeight / 5) * 3.5;
var movingPlayBar = 20; 
var movingPlayBarW = 10; 
var playX; //moving x tracked against the array's x's. 

//variables to make bpm changeable
var bpmEdit = 2;
var bpm;
var bpmX;
var bpmToggleX;
var bpmToggleLength;

var backgroundMusicToggle = false;
var interval;
var volume = 1;

function preload() {
    // load sound files 
    bird = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/182507__swiftoid__bird-chirp.wav");
    bird.setVolume(volume);
    bell = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/173000__keykrusher__bicycle-bell-2.wav");
    bell.setVolume(volume / 3 * 2);
    cello = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/398494__anthousai__wind-chimes-single-01.wav");
    cello.setVolume(volume);
    shaker = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/368607__samsterbirdies__shake.wav");
    shaker.setVolume(volume);
    backgroundMusic = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/203099__lemoncreme__groove-music.wav");
    backgroundMusic.setVolume(volume);
}

function setup(){
	createCanvas(canvasWidth, canvasHeight);
	frameRate(1000);
	angleMode(DEGREES);

    bpmToggleLength = width / 8 * 5; 
	bpmX = bpmToggleLength + margin;
	//setting up rows with different sounds
    for (var c = 0; c < totalC; c++){
    	for (var r = 0; r < 4; r++){
    		var x = c * (noteW + margin) + margin;
    		var y = r * (noteW + margin) + margin; 
    		if ( r == 0){
    			snd = bird;
    		} else if (r == 1){
    			snd = bell;
    		} else if (r == 2){
    			snd = cello; 
    		} else {
    			snd = shaker;
    		}
    		var n = Note(x , y, snd, noteW);
            notesArray.push(n);
    	}
    }
}



function draw(){
	background(250, 230, 10);
	var wiggleRoom = 2;

	//intro text
	stroke(0, 0, 250);
	strokeWeight(1);
	fill(0, 0, 250);
	textSize(margin);
	textFont('Courier');
	text('press play to begin!', width / 2 - bpmToggleLength / 1.75, canvasHeight / 8 * 7); 

	//static line
	strokeWeight(5);
	line( baseLineX, baseLineY, canvasWidth - baseLineX, baseLineY);//playbar baseline

	//for loop rendering the array into notes
    for (var i = 0; i < notesArray.length; i++){
    	notesArray[i].render();

    	//play music when the play x and array of on notes collide
    	if (playX >= notesArray[i].x - wiggleRoom 
    		& playX <= notesArray[i].x + wiggleRoom 
    		&& notesArray[i].on == true){
    		notesArray[i].snd.play();
    	}
    }

	bpmToggle();
	playBackground();
	playButton();
    movePlayStream();
}

//function to move the bpm visual ui
function bpmToggle(){
	//shadow
	noStroke();
	fill(250, 250, 250, 150);
	rect( bpmToggleLength + margin, baseLineY + (margin * 2.5), width/5, noteW/5, 80); 
	//bpm text
	stroke(0, 0, 250, 250);
	fill(0, 0, 250);
	strokeWeight(1);
	text('bpm', bpmToggleLength + margin, canvasHeight / 8 * 7 + margin);
	//toggle
	fill(0, 0, 250);
	ellipse(bpmX, baseLineY + (margin * 2) + noteW / 3, noteW / 3, noteW / 3); 
}


//function to play background music
function playBackground(){
	if (backgroundMusicToggle == true){ //counter will be 1, or 2 (on rare occasion), immediate at play
		interval = 1250;
		if (counter  > 0 & counter < 3){
			backgroundMusic.play();
			playing = true;
		} else if ( counter % interval > 0 & counter % interval <= 2){ //interval is based on the timing of the background song
			playing = false;
			counter = 0; //reset counter if you've paused the music
		}
	 } 
}

//play/pause functionality + visuals
//playing is toggled in mousePressed
function playButton(){
	fill(0, 0, 250);

	if (playing == true) { 
		//pause button
		rect(width/2 - playButtonW, baseLineY + (margin * 2) , 
			playButtonW, playButtonW * 3);
		rect(width/2 + playButtonW, baseLineY + (margin * 2), 
			playButtonW, playButtonW * 3);
		//mapping of the bpm toggle to the speed of  playX
		bpmEdit = map(bpmX, 
				bpmToggleLength + noteW / 2, bpmToggleLength - noteW / 2 + width/5, 
				2, 4);
		bpm = bpmEdit; // the set BPM by the user
		backgroundMusicToggle = ! backgroundMusicToggle; //turning on the background music
		counter += 1;

	} else {
		//play button
		triangle ( width/2 - playButtonW, baseLineY + (margin * 2),
				  width/2 - playButtonW, baseLineY + (margin * 2) + noteW * .75 * 2,
				  width/2 - playButtonW + noteW * .75 * 2, baseLineY + (margin * 2) + noteW * .75 )
		bpm = 0; //stop the player if we've paused the switch
		counter = 0;
		backgroundMusic.stop();

	}
}

//draws the animated line
function movePlayStream(){
	playX = baseLineX + movingPlayBar - 20;
	stroke(0, 0, 250);
	fill(250);
	strokeWeight(4);
	//loops the line if the play button moves off the line
	if (playX > canvasWidth - baseLineX) {	
		movingPlayBar = baseLineX;
	} else {
		//the visual parts
		rect(playX, margin / 2, movingPlayBarW, canvasHeight / 5 * 3.5);
		ellipse( playX + movingPlayBarW / 2 , baseLineY, movingPlayBarW*2, movingPlayBarW*2);
	}
	//add whatever the bpm has been toggled to be
    movingPlayBar += bpm;
}



function mousePressed(){
	//turning on and off the individual notes by checking your mouse position
    for (var i = 0; i < notesArray.length; i++){
		if (mouseX > notesArray[i].x 
			& mouseX < notesArray[i].x + noteW 
			&& mouseY > notesArray[i].y
			&& mouseY < notesArray[i].y + noteW){
			var currentToggle = !notesArray[i].on;
			notesArray[i].on = currentToggle;
		}    	
    }
    //switch play/pause mode by clicking on the play/pause button
    if (mouseX > width / 2 - playButtonW 
    	& mouseX < width / 2 + margin + playButtonW 
    	&& mouseY > baseLineY + (margin * 2)
    	&& mouseY < baseLineY + (margin * 2) + playButtonW * 3){
    	playing = ! playing;
    }
}


function mouseDragged(){
	//dragging the bpm toggle
	if (mouseX > bpmToggleLength + margin
    	& mouseX < bpmToggleLength + width/5 + margin
    	&& mouseY > baseLineY + (margin * 2.5)
    	&& mouseY < baseLineY + (margin * 2.5) + noteW){
		bpmX = mouseX;
    }
}


//make the note real
function render(){
	fill(250);
	stroke(0, 0, 250);
	strokeWeight(5);
	//rendering cirlces if the note is on
	if (this.on){
		fill(250, 130, 180);
		noStroke();
		ellipse(this.x + noteW/2, this.y + noteW/2, this.w, this.w);
	} else { 
		//rendering squares if the notes is off
		rect(this.x, this.y, noteW, noteW); 
	}

	if(this.snd.isPlaying() & playX < this.x + noteW*1.5 && playX > this.x){
		var t = frameCount;
		this.w = noteW * sin(t*5);
	}else{
		this.w = noteW;
	}
}

//my note object maker function
function Note(x, y, snd, wide){
	var objNote = { //object literal format
		x : x,
		y : y,
		w: wide,
		snd : snd,
		on : false,
		playing: false
	}
	objNote.render = render;
	return objNote;
}


sketch

/*
Connor McGaffin
Section C
cmcgaffi@andrew.cmu.edu
Assignment 11-B
*/

var option = 3; //starting option
var w = 500; //width
var h = 300; //height
var w2 = w / 2; //width / 2
var h2 = h / 2; //height / 2
var x = []; //bubble starting pos
var y = [];
var dx = []; //bubble direction
var dy = [];
var col = []; //bubble color
var np = 50; // how many particles
var nb = 50; //how many bubbles
var title = ["Valerie","Hound Dog", "Step"];
var artist = ["Amy Winehouse", "Elvis Presley", "Vampire Weekend"];
var currentSpot = 200; //current spot and lengths are to be later configured with p5.js addons and Spotify connectivity
var songLength = 300; // placeholder
var tempo = [96,175,78,0]; //stored tempo of songs
var amp = new p5.Amplitude(); //get amplitude
var particles = [];

function preload() {
    img = loadImage("https://i.imgur.com/K3YQPRm.png"); //load spotify logo
    amy = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/amy.mp3"); //"Valerie" by Amy Winehouse
    elvis = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/elvis.mp3"); //"Hound Dog" by Elvis Presley
    vw = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/vw.mp3"); //"Step" by Vampire Weekend 
}

function particleStep() {
    this.x += this.dx;
    this.y += this.dy / 5;
}

function particleDraw() { //draw particle
    strokeWeight(this.s);
    point(this.x, this.y);
}

function makeParticle(px, py, pdx, pdy, ps) { //create particle with starting pos and velocity
    p = {x: px, y: py,
         dx: pdx, dy: pdy, s: ps,
         step: particleStep,
         draw: particleDraw
        }
    return p;
}

function explode() {
    for (var i = 0; i < np; i++) {// make a particle
        fill(255);
        stroke(255);
        var p = makeParticle(w2, h2, random(-30,50), random(-30,50), random(5,10)); //initial location x/y quadrant velocity x/y, size
        particles.push(p); // push the particle onto particles array 
    }
    for (var i = 0; i < np; i++) { // for each particle
        var p = particles[i];
        p.step();
        p.draw();
    }
}

function record () {
    noStroke();
    fill(0);
    ellipse (w2, h2 , 200); //vinyl
    for (i = 0; i < 8; i++){ 
        push();
        noFill();
        stroke(90);
        strokeWeight(1);
        ellipse(w2, h2, 190 - i * 30);
        pop();  //texture
    }
    fill(200); // blue
    ellipse(w2, h2, 75); //label
    push();
    translate(w2,h2);
    rotate(frameCount * 5);
    fill(150);
    ellipse(15, 0, 5); //label spin
    pop();
    fill(255);
    ellipse (w2, h2, 10); // peg
}

function tonearm () {
    angleMode(DEGREES);
    translate(w2 + 100, h2 - 100);
    fill(255);
    ellipse(0, 0, 30); //swivel
    var tonearmPosition = map(currentSpot, 0, songLength, 0, 28);
    push();
    rotate(tonearmPosition);
    push();
    rectMode(CENTER);
    rect(0,125, 12, 25); //cartridge
    pop();
    strokeWeight(3);
    stroke(255);
    line(0, 0, 0, 125); //bar
    pop();
}

function header () {
    image(img,10,10,20,20);
    fill(255);
    textSize(12);
    text(title[option], 35, 20); //display title 12pt
    textSize(9);
    text(artist[option], 35, 30); //display artist 9pt
}


function setup() {
    createCanvas(w, h);
    for (i = 0; i < nb; i++) {  //generate bubbles 
        x[i] = random(width); //randomly 
        y[i] = height; //at the bottom
        dx[i] = random(-.5,.5); //that float sideways randomly 
        dy[i] = random(-2,-1); // and float virtically faster
        col[i] = color(random(255)); //randomly filled in greyscale
    }
}

function draw() {
    background(10,0,0);
    stroke(0);
    strokeWeight(10);
    explode();
    var vol = amp.getLevel();
    var bdiam = map(vol, 0, 1 , 5, 18);
	var vdiam = map(vol, 0, 1, 200, 250); //vinyl amp diameter
    
    for (i = 0; i < nb; i++) {  
        fill(col[i]);
        noStroke();
        ellipse(x[i], y[i], bdiam);
        x[i] += dx[i];
        y[i] += dy[i];
        
    	if (x[i] > width) { //bounce off right wall
        	x[i] = width - (x[i] - width);
        	dx[i] = -dx[i];

    	} else if (x[i] < 0) { //bounce off left wall
        	x[i] = -x[i];
        	dx[i] = -dx[i];

    	}
    	if (y[i] > height) { //bounce off bottom
        	y[i] = height - (y[i] - height);
        	dy[i] = -dy[i];
       
    	} else if (y[i] < 0) { //float off top, appear at bottom
    		y[i] = height;
    	}
	}
	push();
    noStroke();
    fill(180);
    ellipse(w2,h2,vdiam,vdiam);
    pop();
    header();
    record();
    tonearm();         
}

function mouseClicked() {
	particles = []; //clear particles array
	explode(); //create particle explosion
    option ++; //next option when clicked
    if(option > 3){ 
        option = 0;
    }
    if(option == 0){
    	for(i = 0; i < nb; i++){
    		col[i] = color(0, 0, random(255)); //random blue
    		dx[i] = tempo[option] * random(-.1,.1) / 10; //horiz to tempo
    	}
        vw.stop(); //stop "step"
        amy.play(); // play "valerie"
    }
    if(option == 1){
    	for(i = 0; i < nb; i++){
    		col[i] = color(random(255),0,0); //random red
    		dx[i] = tempo[option] * random(-.1,.1) / 10; // horiz to tempo
    	}
        amy.stop(); //stop "valerie"
        elvis.play(); //play "hound dog"
    }
    if(option == 2){
    	for(i = 0; i < nb; i++){
    		col[i] = color(random(255)); //random greyscale
    		dx[i] = tempo[option] * random(-.1,.1) / 10; //horiz to tempo
    	}
        elvis.stop(); //stop "hound dog"
        vw.play(); //play "step"
    }
    if(option == 3){
        vw.stop(); //stop "step"
    	for(i = 0; i < nb; i++){
    		col[i] = color(255); //bubbles fill white
    		dx[i] = random(-.1,.1); //horiz moment unrelated to tempo
    	}
    }
}

Redesigning Spotify Chromecast

This projects is a brief redesign of Spotify’s current Chromecast interface. Pictured below is the current state of the Spotify Chromecast player, which displays the current song, some controls, its information, its album cover, and the album covers of tracks adjacent to it in the queue.

With Spotify Chromecast so frequently being used while hosting a social event, I would argue that the current interface is distracting from the event, or redundant at the least. The current layout would easily enable guests to continuously check which song is coming next, and which songs have already played, freely guiding listeners out of the enjoying the music in the moment. In addition to this, much of the information on the screen is only necessary for the DJ to know, and it is already being provided on their phone’s interface.

With this being said, I looked to create a new interface for Spotify when used on Chromecast that would allow listeners to stay in the moment of the music, while still providing an complimentary atmosphere to that the of the social event.

As the user plays songs, the generated bubbles behind the record and one large bubble behind the record itself jump to the amplitude of the music. The small floating bubbles move horizontally on their upward waltz in speed relation to their tempo.

This project functions as a mock-up of my Chromecast Spotify player as a living environment. Unfortunately, I was not able to configure the p5.js with the Spotify API, as it was out of the scope of 15-104, and thus only these three preloaded songs may be queued. These tracks were chosen with the intent of providing a brief range of visual possibilities this program can generate.

Controls

  • select anywhere on the canvas to play the next song, thus changing the color, size and flow of bubbles

 

Vicky Zhou – Final Project

sketch

/*Vicky Zhou
vzhou@andrew.cmu.edu
Project 12 - Final
Section E*/

var particles; //number of particles in sphere system
var spacing; //spacing of particles
var waveRipple; //manipulates ripple in sphere system
var points; //array for compiling all sphere points
var speed; // speed variable for ripple sphere wave effect
var amplitude; //amplitude of wave

//variables for new toruses
var myx = [];
var myy = [];
var myz = [];

//variables for existing, old toruses
var total = 5;
var locations = new Array(total);
var bool = new Array(total);
var oldpart = [];
var oldpartsize = 12;

//to change displays
var changescene = true;


function setup() {
    createCanvas(600, 600, WEBGL);
    particles = 2000; 
    spacing = 200;
    points = [];
    speed = 0.01;
    amplitude = 0.8;
    wavecol = 0;
    
    if (changescene) {
    	//array of random points for wave of spheres
    	for (var i = 0; i < particles; i++) {
    		points[i] = createVector(random(-5, 5),
    								 random(-5, 5),
    								 random(-5, 5));
    	}
    } else {
    	//array for generated toruses
    	for (var i = 0; i < myx.length; i++) {
    		myx[i];
    		myy[i];
    		myz[i];
    	}

    	//array for already existing toruses
    	for (var i = 0; i < locations.length; i++) {
    		locations[i] = new Array(total);
    		bool[i] = new Array(total);
    		for (var j = 0; j < locations[i].length; j++) {
    			locations[i][j] = new Array(total);
    			bool[i][j] = new Array(total);
    			for (var k = 0; k < locations[i][j].length; k++) {
    				locations[i][j][k] = createVector(i * oldpartsize,
    												j * oldpartsize,
    												k * oldpartsize);
    				bool[i][j][k] = false;
    			}
    		}
    	}

    	var thres = 2; //threshold for state of Torus 
    	for (var i = 0; i < total; i++) {
    		for (var j = 0; j < total; j++) {
    			for (var k = 0; k < total; k++) {
    				stateofTorus = random(15); //random number for state 
    				if (stateofTorus <= thres) {
    					st = 1;
    					bool[i][j][k] = true;
    				} else {
    					st = 0;
    				}
    				oldpart.push(new myTorus(i * oldpartsize,
    										j * oldpartsize,
    										k * oldpartsize,
    										st));
    			}
    		}
    	}
    }
}


//create and push new coordinates for toruses when any key is pressed
function keyPressed() {
	myx.push(random(-10, 10));
	myy.push(random(-300, 300));
	myz.push(random(-300, 300));
}


function draw() {
	background(30, 30, 50);
	wavecol += 1;
	//light that is emiited
	directionalLight(wavecol/2, 200, 200, 50);
	ambientLight(80, wavecol/5, 170);
	//manipulates wave of spheres
	waveRipple = mouseX * speed;

	//center rotating cube
	push();
	rotateX(frameCount/100);
	rotateY(frameCount/100);
	noStroke();
	box(150);
	pop();

	if (changescene) {
		//array of random points to plot each sphere
		for (var i = 0; i < particles; i++) {
			var dot = points[i];
			doty = sin(dot.x + waveRipple) * amplitude;
			push();
			translate(dot.x * spacing, doty * spacing, dot.z * spacing);
			noStroke();
			sphere(10);
			pop();
		}
	} else {
		//array of existing toruses
		push();
		for (var i = 0; i < oldpart.length; i++) {
			oldpart[i].display();
		}
		pop();

		//create new toruses
		for (var i = 0; i < myx.length; i++) {
			rotateY(frameCount/500);
			push();
			if (keyIsPressed === true) {
				updatez = myz[i];
			} else {
				updatez = 100;
			}
			thenewy = cos(waveRipple) * amplitude + myy[i];
			thenewz = 0;
			directionalLight(wavecol/2, 200, 200, 50);
			translate(myx[i], thenewy, thenewz + updatez);
			var gradred = map(mouseX, 0, width, 0, 255);
			var gradgreen = map(mouseX, 0, width, 0, 200);
			fill(gradred, gradgreen, 200);
			strokeWeight(0.2);
			stroke("blue");
			torus(12, 5);
			pop();
		}
	}
}

//function to create existing toruses
function myTorus(x, y, z, st) {
	this.x = x;
	this.y = y;
	this.z = z;
	this.st = st;

	this.display = function() {
		if (this.st == 1) {
			rotateY(frameCount/350);
			push();
			directionalLight(wavecol/2, 200, 200, 250);
			translate(this.x, this.y, this.z * 3.5);
			strokeWeight(0.2);
			stroke("blue");
			torus(oldpartsize, 5);
			pop();
		}
	}
}

//click mouse to toggle between scenes
function mousePressed() {
	changescene =! (changescene);
	setup();
}

Instructions

Mouse Click: toggle between screens

Mouse move: manipulate wave; manipulate color of toruses

Press any key: add a new torus

Hold down any key: explode toruses

Statement

“Re:current” is a project utilizing 3D graphics from WEBGL in order to manipulate arrays and variables to create interactive cyclical experiences — 1. a “current” like nature of manipulating a wave of spheres using curves to mimic ocean-like wave that changes color over time and 2. a looping interaction of adding toruses which rotate around a 3. perennially rotating cube. My greatest struggle was working and adapting to the 3D landscape and nuances of WEBGL, and being able to translate the 2D elements we learned thus far onto my canvas. For my beginning prototypes, the biggest challenge I faced with creating an interactive project was being able to manipulate factors utilizing the mouse press and mouse pressed feature, because of how a mouse inherently creates an output of 2D, whereas my project would call for 3D. I resolved this issue to the best of my ability in this short term by opting to toggle between screens and making basic interactions using the mouse, and then for more detailed engagement I utilized the key pressed feature to generate random x, y, and z coordinates. For further exploration, I would love to explore with more dynamic ways of altering the canvas, ideally in ways to can imitate biomimicry, seeing that WEBGL is able to produce interestingly rendered outcomes with its different textures, lighting, and other attributes.

Screenshots
Some screenshots of my work!

wave manipulation (color change over time)
wave manipulation
initial torus scene
adding more toruses!

Kade Stewart – Final Project

Please download and unzip kades_final. Then just open the HTML file in your browser to play my game. It has instructions in the game itself to play.

My game strays a little bit form my proposal, but is ultimately pretty similar. You just have to reach the right side of the screen and avoid the red planes. Each level has controls that are manipulated in some way, which you’ll learn after you complete the level before. Most of the levels are controlled via the arrow keys, but levels 6 and 7 have special controls you’ll learn when you play. It is not the hardest game, but hopefully you can have some fun (and unlock hard mode when you complete the game)!

Christine Chen-Final Project

Christine Chen-Final-Project

/*
Christine Chen
Section E
cyc1@andrew.cmu.edu
Final Project
*/

var heartArray = [];
var heartCount = 0;

var carrotArray = [];
var carrotCount = 0;

var bombArray = [];

//location where the things that are collected disappear
var collect = 340;
    
//-------------------------------------

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

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

function draw() {    
    background(255, 178, 220); //light pink background
    backgroundHeart();
    bunny();
    renderHeart();
    renderCarrot();
    renderBomb();
    pointDisplay();
    gameOver();
}

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

//***** HEART *****
function makeHeart(x, y) {
    return {
        x: x,
        y: y,
        draw: drawHeart,
    }
} 

function drawHeart() { //draw shape of heart 
    strokeWeight(0.3);
    noStroke();

    //color of heart changes to give glittering effect
    fill(255 - random(0, width), 116, 208); 
    ellipse(this.x - 5, this.y, 11, 11);
    ellipse(this.x + 5, this.y, 11, 11);
    triangle(this.x - 10.5, this.y + 2, 
        this.x + 10.5, this.y + 2, 
        this.x, this.y + 13);
}

function renderHeart() {
    //creates new heart every 30 frames
    if (frameCount % 30 == 0) {
        var newHeart = makeHeart(random(10, width - 10), 0);
        heartArray.push(newHeart);
    }

    //updates heart's y location(make them fall)
    for (var i = 0; i < heartArray.length; i ++) {
        var heart = heartArray[i];
        heart.y += 1.5;

        //remove hearts that are gone (off of canvas)
        //saves space to prevent codes from running slow
        if(heart.y > height) {
            heartArray.splice(i, 1);
        }

        //remove hearts that are collected
        if(dist(mouseX, collect, heart.x, heart.y) < 20) {
            heartArray.splice(i, 1);
            heartCount++; //keeps track of heart points
        }

        heart.draw();
    }
}

//***** CARROT *****
function makeCarrot(x, y) {
    return {
        x: x,
        y: y,
        draw: drawCarrot
    }
} 

//draws shapes of carrot
function drawCarrot() {
    //carrot stem
    fill(17, 147, 8); //green
    ellipse(this.x, this.y - 5, 5, 15);
    ellipse(this.x - 3, this.y - 5, 5, 15);
    ellipse(this.x - 7, this.y - 5, 5, 15);
    ellipse(this.x + 3, this.y - 5, 5, 15);
    ellipse(this.x + 7, this.y - 5, 5, 15);

    //carrot body 
    fill(255, 117, 25); //orange
    triangle(this.x - 10, this.y, 
        this.x + 10, this.y, 
        this.x, this.y + 30);
}

function renderCarrot() {
    //creates new carrot every 600 frames
    if (frameCount % 600 == 0) {
        var newCarrot = makeCarrot(random(10, width - 10), 0);
        carrotArray.push(newCarrot);
    }

    //updates carrot location
    for (var i = 0; i < carrotArray.length; i ++) {
        var carrot = carrotArray[i];
        carrot.y += 5; //carrot falls quite fast

        //removes carrots that are gone(off of canvas)
        if(carrot.y > height) {
            carrotArray.splice(i, 1);
        }

        //removes carrots that are eaten
        if(dist(mouseX, collect, carrot.x, carrot.y) < 20) {
            carrotArray.splice(i, 1);
            carrotCount++; //keeps track of number of carrots collected
        }

        carrot.draw();
    }
}

//***** BOMB *****
function makeBomb(x, y) {
    return {
        x: x,
        y: y,
        draw: drawBomb,
    }
} 

//draws shape of bomb
function drawBomb() {
    //spark
    fill(255, 0, 0); //red
    ellipse(this.x + 20, this.y - 23, 10, 10);

    //bomb line
    stroke(0); //black 
    strokeWeight(2);
    line(this.x, this.y, this.x + 18, this.y - 20);

    //bomb body
    noStroke();
    fill(48, 47, 47); //dark gray
    ellipse(this.x, this.y, 30, 30);
}


function renderBomb() {
    //creates bomb every 100 frames
    if (frameCount % 100 == 0) {
        var newBomb = makeBomb(random(10, width - 10), 0);
        bombArray.push(newBomb);
    }

    //updates bomb location
    for (var i = 0; i < bombArray.length; i ++) {
        var bomb = bombArray[i];
        bomb.y += 6;

        //removes bomb that is gone (off of canvas)
        if(bomb.y > height) {
            bombArray.splice(i, 1);
        }

        //removes bomb that is collected
        if(dist(mouseX, collect, bomb.x, bomb.y) < 30) {
            bombArray.splice(i, 1);
            //heart point deducted by 10 points if bomb collected
            heartCount -= 10; 
            //carrot point deducted by 1 if bomb collected 
            carrotCount -= 1;
        }

        bomb.draw();
    }
}

//***** BUNNY *****
function bunny() {
    //constrain bunny location to inside canvas
    var control = constrain (mouseX, 15, width - 15);

    noStroke();
    fill(255); //white

    //body
    fill(255); //white
    ellipse(control, 370, 55, 50);

    //feet
    ellipse(control - 15, height - 5, 20, 10); //left 
    ellipse(control + 15, height - 5, 20, 10); //right 

    //hands
    ellipse(control - 27, height - 45, 20, 10); //left 
    ellipse(control + 27, height - 45, 20, 10); //right 

    //ears
    ellipse(control - 8, 300, 10, 30); //left 
    ellipse(control + 8, 300, 10, 30); //right 

    //face
    ellipse(control, 330, 70, 50);

    //eyes
    fill(0);
    ellipse(control - 5, 325, 5, 5); //left 
    ellipse(control + 5, 325, 5, 5); //right 

    //mouth
    fill(255, 122, 169); //pink
    ellipse(control, 338, 12, 10);

    //nose
    fill(0);
    ellipse(control, 331, 5, 4);

    //blush
    fill(252, 204, 204); //light red
    ellipse(control - 10, 331, 10, 7); //left
    ellipse(control + 10, 331, 10, 7); //red
}

//***** GAMEOVER *****
function gameOver() {
    //if carrot point is less than 0,
    //or if heart point is or less than -100,
    //game ends
    if (carrotCount < 0 || heartCount <= -100) {
        noLoop();
    
    //gameover sign background
    fill(167, 0, 47); //red
    rectMode(CENTER);
    rect(width/2 - 10, height/2 - 10, 260, 70);

    //gameover sign text
    fill(255);
    textSize(22);
    textAlign(CENTER);
    text("   G A M E O V E R", width/2 , height/2);

    //skull
    fill(255); //white
    ellipse(width/2 - 100 , height/2 - 10, 30, 22);
    rect(width/2 - 100 , height/2, 16, 10);

    //skull eyes
    fill(50);//gray
    ellipse(width/2 - 105 , height/2 - 10, 7, 7);
    ellipse(width/2 - 95 , height/2 - 10, 7, 7);
    }
}

//***** POINT DISPLAY *****
function pointDisplay() {
    stroke(0); //black
    strokeWeight(0.2); //light outline
    fill(250, 214, 226); //pink
    rect(10, 10, 180, 60);

    //heart
    noStroke();
    fill(108, 136, 255);
    ellipse(35, 35, 11, 11);
    ellipse(45, 35, 11, 11);
    triangle(29.5, 37, 
        50.5 , 37, 
        40, 48);

    //heart count text
    fill(30);
    textSize(20);
    text("= " + heartCount, 55, 45);

    //carrot
    fill(17, 147, 8); //green
    ellipse(120, 25 , 5, 15);
    ellipse(117, 25, 5, 15);
    ellipse(113, 25, 5, 15);
    ellipse(123, 25, 5, 15);
    ellipse(127, 25, 5, 15);

    fill(255, 117, 25); //orange
    triangle(110, 30, 
        130, 30, 
        120, 60);
    
    //carrot count text
    fill(30);
    textSize(20);
    text("= " + carrotCount, 140, 45);
}

//***** BACKGROUND HEART *****
function backgroundHeart() {
    noStroke();
    fill(253, 158, 209); //pink
    ellipse(180, 140, 260, 270);
    ellipse(420, 140, 260, 270);
    triangle(73, 220, 
        width - 73 , 220, 
        width/2, height - 5);
    ellipse(width/2, 220, 80, 80);
}

Heart
Carrot
Bomb

 

 

 

How to play:
– Move bunny left and right with mouse to control bunny’s position
– All things have to touch bunny’s mouth to be effective
– Collect hearts to gain points
– Collect carrots to store as lives
– Carrots save the bunny when bomb hits the bunny
– Bomb takes away bunny’s lives and deduct 10 heart points
– Bunny dies when heart point is -100 or when bomb hits bunny and there is no carrot to save bunny

Statement:
For my project, I decided to create a game that involves a bunny and heart rain. I wanted to create a simple, cute game that people can play with. Hearts are points and the carrots are lives. The bomb is what kills the bunny. The player should aim to gain as many heart points as possible. The stored carrots save the bunny from dying in the case that the bomb hits the bunny. I made the bomb fall fast on purpose so that it will be harder to escape the bomb, adding some excitement to the game. I also made the carrots, which rarely appears (to make the carrots more precious), fall fast. 10 heart points are deducted if the bunny hits the bomb. This is so that the player can’t keep using carrots to save the bunny while not collecting hearts. I made the background simple for this game as because of the “glittering effect” of the heart and the movement of the things falling, having more complicated background will confuse the player’s eyes too much. Initially, I made the hearts simply pink, but then I thought that was too boring and so I changed my codes so that they change colors randomly to create a glittering effect. The things have to touch the bunny’s mouth to be effective as my idea is that the bunny “eats” the things to collect them. For my codes, I used a lot of nested functions to make my codes clearer and easier to manipulate. I had a lot of fun creating this project as I have always wanted to create a cute game with movements of hearts that people can play with that cheers them up on bad days.

 

Collect hearts to gain points
Bomb kills the bunny

Justin Yook- Final Project

stageMaster

// Justin Yook
// jyook@andrew.cmu.edu
// Section C
// Final Project: Stage Master

var dancerArr = []; // Array of dancer objects 
var siz = 20; // Diameter of dancers (fixed)
var cFrame = 0; // Current frame = current count (index)
var tFrames = 8; // Total number of frames = eight count
var c; //Current canvas

function setup() {
    c = createCanvas(480, 360);
}

function draw() {
    background(255);
    // Create Grid 
    // Horizontal lines
    for (var i = 0; i < height; i+=30) {
        stroke(200);
        strokeWeight(1);
        line(0, i, width, i);
    }
    // Vertical lines
    for (var i = 0; i < width; i+=30) {
        stroke(200);
        strokeWeight(1);
        line(i, 0, i, height);
    }

    // Create bottom rectangle
    fill(65, 105, 225);
    rect(0, 300, width, height);

    // Create count circles 
    for (var i = 0; i < 8; i++) {  
        fill(255);
        ellipse(i * 50 + 66, 330, 2 * siz, 2 * siz);

        fill(0);
        textSize(14);
        text(i + 1, i * 50 + 62, 335);
    }

    // Create upper rectangle
    fill(65, 105, 225);
    rect(0, 0, width, height - 300);

    // Check and display dancer objects
    for (var i = 0; i < dancerArr.length; i++) {
        dancerArr[i].draw();
    }

    // Display title
    fill(255);
    textSize(20);
    text("STAGE MASTER", width / 3, height / 8);

    // Indicate which eight count you are currently on with green ellipse
    if (cFrame == 0) {
        fill(0, 255, 0);
        ellipse(66, 330, 40, 40);

        fill(0);
        textSize(14);
        text("1", 62, 335);
    }

    if (cFrame == 1) {
        fill(0, 255, 0);
        ellipse(116, 330, 40, 40);

        fill(0);
        textSize(14);
        text("2", 112, 335);
    }

    if (cFrame == 2) {
        fill(0, 255, 0);
        ellipse(166, 330, 40, 40);

        fill(0);
        textSize(14);
        text("3", 162, 335);
    }

    if (cFrame == 3) {
        fill(0, 255, 0);
        ellipse(216, 330, 40, 40);

        fill(0);
        textSize(14);
        text("4", 212, 335);
    }

    if (cFrame == 4) {
        fill(0, 255, 0);
        ellipse(266, 330, 40, 40);

        fill(0);
        textSize(14);
        text("5", 262, 335);
    }

    if (cFrame == 5) {
        fill(0, 255, 0);
        ellipse(316, 330, 40, 40);

        fill(0);
        textSize(14);
        text("6", 312, 335);
    }

    if (cFrame == 6) {
        fill(0, 255, 0);
        ellipse(366, 330, 40, 40);

        fill(0);
        textSize(14);
        text("7", 362, 335);
    }

    if (cFrame == 7) {
        fill(0, 255, 0);
        ellipse(416, 330, 40, 40);

        fill(0);
        textSize(14);
        text("8", 412, 335);
    }
}

function keyPressed() {
    // Add new dancer at (mouseX, mouseY) when pressing spacebar
    if (key == ' ') { 
        dancerArr.push(makeDancer(mouseX, mouseY, siz));
    }

    // Move to next eight count when pressing 'd'
    if (key == 'd') {
        cFrame += 1;
        cFrame = cFrame % tFrames;
        for (var i = 0; i < dancerArr.length; i++) {
            if (dancerArr[i].posArr.length < cFrame) {
                //x
                dancerArr[i].posArr[cFrame][0] = dancerArr[i].posArr[cFrame - 1][0];

                //y
                dancerArr[i].posArr[cFrame][1] = dancerArr[i].posArr[cFrame - 1][1];
            }
        }
    }

    // Move to previous eight count when pressing 'a'
    if (key == 'a') {
        cFrame -= 1;
        if (cFrame < 0) {
            cFrame += 8;
        }
    }

    // Download screenshot of current formation
    if (key == "s") {
        saveCanvas(c, 'formation', 'jpg');
    }
}

// Click and drag dancer object
function mousePressed() {
    for (var i = 0; i < dancerArr.length; i++) {
        if (dist(mouseX, mouseY, dancerArr[i].posArr[cFrame][0], dancerArr[i].posArr[cFrame][1]) <= (dancerArr[i].ps / 2)) {
            dancerArr[i].drag = true;
        }
    }
}

function mouseDragged() {
    for (var i = 0; i < dancerArr.length; i++) {
        if (dancerArr[i].drag == true) {
            for (var j = cFrame; j < tFrames; j++) {
                dancerArr[i].posArr[j][0] = mouseX; 
                dancerArr[i].posArr[j][1] = mouseY; 
            }
        }
    }
}

function mouseReleased() {
    for (var i = 0; i < dancerArr.length; i++) {
        dancerArr[i].drag = false;
    }
}

//----------------------------------------------------------------------
// DANCER OBJECT
function makeDancer(x, y, s) { 
    var dancer = {"px": x, "py": y, "ps": s, "drag": false};
    var posArr = [];
    for (var i = 0; i < tFrames; i++) {
        posArr.push([x, y]);
    }
    dancer.posArr = posArr;
    dancer.draw = dancerDisplay;
    return dancer;
}

function dancerDisplay() {
    fill(0);
    var cpos = this.posArr[cFrame];
    ellipse(cpos[0], cpos[1], this.ps, this.ps);
}

Instructions:

‘space’ key: Add a new dancer at (mouseX, mouseY)

‘d’ key: Move to the next count

‘a’ key: Move to the previous count

’s’ key: Save a screenshot of the current formation

Mouse: Click, hold, and drag inside dancer to move the dancer

First add a dancer on the canvas, at your mouse position; click and drag the dancer to move them around. If you are satisfied with the placement of your dancers, then move to the next count by pressing ‘d’. If you want to change any previous formations, press ‘a’ to move to the previous count. You can also save a screenshot of the current formation by pressing ’s’.

Statement:

“Stage Master” is a tool for choreographers and dancers to visualize dance formations. I really enjoyed creating this project, because it is something that can help me make formations easily, and in an organized manner. The most challenging part of the project was developing how dancer objects were updated for each count because I had to understand nested arrays and objects very well. The current version of the program only works for a single set of eight counts, but in the future, I would want to add a feature that allows the user to navigate through sets of eight counts. In addition, it would be better if I were to include a feature where the user can play and pause music. Overall, the visuals are not that great, and the amount of code is not a lot, but I believe that the function of the program is useful.

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.

Shirley Chen – Final Project – Little Red Riding Hood

For the final project I create a game based on the story of “Little Red Riding Hood”. In this game, the user use mouse to control the position of Little Red to avoid a group of wolves running randomly. The Little Red should try to get more apples, and for each apple she gets the user can earn 10 points. There is another snake which wants to eat Little Red’s apple, so Little Red should get the apple before the snake reaches it. If the snake eats the apple before Little Red reaches it, the user will lose 10 points. The game would be over if the wolves hit Little Red. Little Red’s grandma is very worried about her granddaughter at the bottom right corner. This project is very fun and I enjoy the process of solving different problems that I met.
Have fun playing it! 🙂

sketch

// Shirley Chen
// Section B
// junfanc@andrew.cmu.edu
// FINAL PROJECT

var xWolf = [];
var yWolf = [];
var dxW = [];
var dyW = [];
var bushesX = [];
var bushesY = [];
var appleX = 300;
var appleY = 300;
var girl;
var wolf;
var bushes;
var apple;
var grandma;
var count = 0;
var snakeX = 590;
var snakeY = 0;
var textAppear = false;


function preload(){
  girl = loadImage("https://i.imgur.com/X6Tfuu7.png");
  wolf = loadImage("https://i.imgur.com/80Jv2F4.png");
  grass = loadImage("https://i.imgur.com/zTPmG8i.png");
  apple = loadImage("https://i.imgur.com/dgBmn61.png");
  snake = loadImage("https://i.imgur.com/QpLwiaZ.png");
  snakeflip = loadImage("https://i.imgur.com/aGK7Aa3.png");
  bushes = loadImage("https://i.imgur.com/mfdC2Ny.png");
  grandma = loadImage("https://i.imgur.com/3dTTlzG.png");
}


function setup(){
  createCanvas(600, 600);
  imageMode(CENTER);
// Randomly select bushes' location
  for (var i = 0; i < 10; i++){
    bushesX.push(random(20, 580));
    bushesY.push(random(20, 580));
  }
// Randomly select wolf's starting location and speed
  for (var i = 1; i < 15; i++) { 
    xWolf[i] = random(0, width);
    yWolf[i] = random(0, height);
    dxW[i] = random(-4, 4);
    dyW[i] = random(-4, 4);
  }
}


function draw(){
  background(229, 255, 229);
// Draw the bushes as background
  for (var i = 0; i < 10; i ++){
    image(bushes, bushesX[i], bushesY[i]);
  }
// Make the wolves move randomly in the canvas
  for (var i = 1; i < 15; i ++){
    image(wolf, xWolf[i], yWolf[i]);
    xWolf[i] += dxW[i];
    yWolf[i] += dyW[i];
// Make the wolves bounce back if they hit the boundary of the canvas 
    if (xWolf[i] + 10 > width || xWolf[i] - 10 < 0){
      dxW[i] =- dxW[i];
      }
    if (yWolf[i] + 15 > height || yWolf[i] - 15 < 0){
      dyW[i] =- dyW[i];
      }
// Create a girl controled by user's mouse
    image(girl, mouseX, mouseY);
// When the girl hits a wolf the game is over
    if (xWolf[i] > mouseX - 10  & xWolf[i] < mouseX + 10){
      if (yWolf[i] > mouseY - 10 && yWolf[i] < mouseY + 10){
        gameOver();
      }      
    }
  }
// Draw an apple with starting location (300, 300)
  image(apple, appleX, appleY);
// Create a snake that will move to eat apple
  var dx = appleX - snakeX;
  var dy = appleY - snakeY;
  snakeX += dx / 70;
  snakeY += dy / 70;
  image(snake, snakeX, snakeY);

// If the girl gets apple, adds 10 point to the score
// If the snake eats apple, minus 10 point to the score
  fill(255, 153, 175);
  textSize(15);
  text("SCORE: " + str(count), 20, 30);
  if (girlGetsApple()){
    pickAppleLocation(); 
    count +=10;
  }
  if (snakeGetsApple()){
    pickAppleLocation();
    count -=10;
  }
// Grandma is very worried about her granddaughter
  image(grandma, 540, 500);
}

// Randomly pick apple's location
function pickAppleLocation(){
  appleX = random(10, 290);
  appleY = random(10, 290);
}

// If the girl or snake gets apple,
// the apple will change to another location
function girlGetsApple(){
  var d = dist(mouseX, mouseY, appleX, appleY);
  if (d < 13){
    return true;
  }
  else{
    return false;
  }
}
function snakeGetsApple(){
  var s = dist(appleX, appleY, snakeX, snakeY);
  if (s < 13){
    return true;
  }
  else{
    return false;
  }
}

// When the gram is over, screen will appear "GAME OVER!"
// and the game will stop
function gameOver(){
  textAppear = true;
  fill(255, 153, 175);
  textSize(30);
  textStyle(BOLD);
  textFont('Arial');
  text("GAME OVER!", 200, 300);
  noLoop();
}