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.

Alice Fang – Looking Outwards – 12

Andrea Gysin’s website is an inspiration for my and Jaclyn’s proposed project. Her website is full of examples of interactive type, which flow across the screen and shift with the position of the mouse. Other than the changes with mouseX and mouseY in the background, the body of text left aligned on the page flickers through and ‘rotates’ when the mouse hovers, creating a really cool loading characters effect. Andrea Gysin’s other work beyond just the construction of her website also include a program created for graphic designers to build simple, animated alphabets, and other tools to create visuals and installations. A lot her work is inspiring and along the lines of typographic interaction that Jaclyn and I are trying to build.


A project along similar but slightly different lines is Amnon Owed’s CAN Generative Typography. Using processing, he created an alphabet of generated letters, with different graphic characteristics. While it does not deal with bodies of text, as Andrea Gysin’s website does, the generative part of this video is what I find interesting; it would be really cool if we could apply a generative aspect to how the lines or stanzas of the poem for our project appear onto the canvas. Owed’s alphabet is not interactive, but a hybrid with the interactions seen in Gysin’s website could produce results that we want.

CAN Generative Typography from Amnon Owed on Vimeo.

A demo video of his generative typography

Alice Fang – Project 12 – Proposal

I am planning to work with my classmate Jaclyn Saik to create an interactive poem. We plan to use one of our favorite poems, “Still I Rise” by Maya Angelou, not only because it’s an excellent piece of writing but also because her message feels especially pertinent in today’s political and social climate. The poem is 43 lines and 9 stanzas long, and we plan to figure out a way to break it up and display it on separate slides, which the user can move through as they continue to read and interact. We want to create interactions specific to the different lines (or couplets, or stanzas). For example, the line “I’m a black ocean, leaping and wide,/Welling and swelling I bear in the tide”, we plan to animate the text based on the mouse position to imitate waves.

We were inspired by the work of programmer and poet Allison Parish, who creates a lot of work involving interactive text and generative poetry.

Some sketches and storyboards for the ways users can interact with the lines of text

Alice Fang – Project 11

sketch

/*
Alice Fang
Section E
acfang@andrew.cmu.edu
Project-11-Turtle Graphics
*/

var ttl;
var r;  
var g; 
var b; 

var state = 0;

function setup() {
    createCanvas(400, 400);
   	background(180, 40, 80);
    ttl = makeTurtle(width / 2, height / 2);
}

function mousePressed() {
	if (state === 0) { // make line strokes based on mouse location
		r = random(0, 256); // randomized colors
		g = random(0, 256);
		b = random(0, 256);

		ttl.goto(mouseX, mouseY);
		ttl.setWeight(random(1, 10)); // randomized stroke weight, length, angle
		ttl.setColor(color(r, g, b));
		ttl.penDown();
		ttl.forward(random(10, 100));
		ttl.right(random(30, 355));

	} else if (state === 1) { // make swirlies
		ttl.penUp();
		ttl.goto(mouseX, mouseY);
		ttl.setWeight(random(1, 4));
		ttl.penDown();
		swirly();
		ttl.penUp(); 
	}
}

function swirly() { // function to create swirly
	for (var i = 0; i < 30; i++) {
		ttl.setColor(color(240, 240, 80));
		ttl.forward((30 - i) / random(2, 4));
		ttl.right(random(20, 40));
	}
}

function keyPressed() { // press ENTER key to change between. lines and swirlies
	if (keyCode === ENTER) {
		state = state + 1;
	} 
	if (state > 1) {
		state = 0;
	}
}

function turtleLeft(d){this.angle-=d;}function turtleRight(d){this.angle+=d;}
function turtleForward(p){var rad=radians(this.angle);var newx=this.x+cos(rad)*p;
var newy=this.y+sin(rad)*p;this.goto(newx,newy);}function turtleBack(p){
this.forward(-p);}function turtlePenDown(){this.penIsDown=true;}
function turtlePenUp(){this.penIsDown = false;}function turtleGoTo(x,y){
if(this.penIsDown){stroke(this.color);strokeWeight(this.weight);
line(this.x,this.y,x,y);}this.x = x;this.y = y;}function turtleDistTo(x,y){
return sqrt(sq(this.x-x)+sq(this.y-y));}function turtleAngleTo(x,y){
var absAngle=degrees(atan2(y-this.y,x-this.x));
var angle=((absAngle-this.angle)+360)%360.0;return angle;}
function turtleTurnToward(x,y,d){var angle = this.angleTo(x,y);if(angle< 180){
this.angle+=d;}else{this.angle-=d;}}function turtleSetColor(c){this.color=c;}
function turtleSetWeight(w){this.weight=w;}function turtleFace(angle){
this.angle = angle;}function makeTurtle(tx,ty){var turtle={x:tx,y:ty,
angle:0.0,penIsDown:true,color:color(128),weight:1,left:turtleLeft,
right:turtleRight,forward:turtleForward, back:turtleBack,penDown:turtlePenDown,
penUp:turtlePenUp,goto:turtleGoTo, angleto:turtleAngleTo,
turnToward:turtleTurnToward,distanceTo:turtleDistTo, angleTo:turtleAngleTo,
setColor:turtleSetColor, setWeight:turtleSetWeight,face:turtleFace};
return turtle;}

For this project, I wanted to create something simple and colorful. I was inspired by my current phone wallpaper, and I couldn’t choose between which version I liked better so I included both as potential interactions someone could have with the program.

My current phone wallpaper!

Click on the canvas to make marks, and press the ENTER key to switch between the lines and the jagged swirlies!

Alice Fang – Looking Outwards – 11

A demonstration of Weather Thingy

Weather Thingy is a climate sound controller which can affect how a musician performs music in real time based on current weather conditions. Created by Adrien Kaeser, using Arduino, weather sensors and C++, Weather Thingy allows “listerners to feel the impact of the climate on the composition.” Consisting of two parts, a weather station and a controller, the sound that is produced varies based on wind speed, rain and precipitation levels, wind direction, and UV level. The four weather variables in turn affect the pan, chorus, LFO (flow frequency oscillation), and delay. The controller converts data from the climate into midi data that can be interpreted by the instruments.

I think this project is interesting because it encapsulates some of the forces in nature to effect how music is performed. Instead of having purely electronic synthesizers, there’s this weird, beautiful combination of utilizing technology to have something beyond our control to create music. I also think the real-time capabilities of this is super cool; imagine if this project was scaled up and a whole orchestra was affected.

Alice Fang – Project 10

sketch

/*
Alice Fang
Section E
acfang@andrew.cmu.edu
Project-10-Generative Lanscape
*/

var boats = [];
var clouds = [];
var terrainDetail = 0.001; // small waves
var terrainSpeed = 0.0005;


function setup() {
    createCanvas(400, 200);
    noStroke();
    
    for (var i = 0; i < 5; i++) {
      // populate arrays
      var boatX = random(width);
      var boatY = random(height / 2, height - 20);
      boats[i] = makeBoat(boatX, boatY);

      var cloudX = random(width);
      var cloudY = random(0, height / 3);
      clouds[i] = makeCloud(cloudX, cloudY);
    }
}

function draw() {
    background(210, 235, 250);

    // sun
    fill('Gold');
    ellipse(350, 20, 20, 20);

    // ocean
    fill(180, 215, 230);
    rect(0, height / 2, width, height);

    // top current
    stroke(170, 205, 220);
    beginShape();
    for (var w = 0; w < width; w++) {
        var t = (w * terrainDetail) + (millis() * terrainSpeed);
        var Y = map(noise(t), 0, 1, height / 2, height);
        vertex(w, Y);
    }
    endShape();
    // bottom current
    stroke(170, 205, 220);
    beginShape();
    for (var w = 0; w < width; w++) {
        var t = (w * terrainDetail) + (millis() * terrainSpeed);
        var Y = map(noise(t), 0, 1, height / 2 + 20, height);
        vertex(w, Y);
        vertex(w, Y+10);
    }
    endShape();

    // draw boats
    for (var j = 0; j < boats.length; j++) {
        boats[j].draw();
        boats[j].move();
    }

    // draw clouds
    for (var k = 0; k < clouds.length; k++) {
        clouds[k].draw();
        clouds[k].move();
    }

}

// boats
function drawBoat() {
    // mast
    var mastX = this.xPos + (this.rWidth * 2 / 3);
    stroke(10, 30, 20);
    strokeWeight(3);
    line(mastX, this.yPos - 3 * this.rHeight, mastX, this.yPos);

    // boat
    noStroke();
    fill('LightSalmon');
    quad(this.xPos, this.yPos, this.xPos + this.rWidth, this.yPos, 
        this.xPos + this.rWidth - this.xOff, this.yPos + this.rHeight, this.xPos + this.xOff, this.yPos + this.rHeight);

    // sail
    noStroke();
    fill(255, 245, 238);
    triangle(mastX, this.yPos - 5, mastX - this.rSail, this.yPos - 5, mastX, this.yPos - 4 * this.rHeight);
    triangle(mastX, this.yPos - 5, mastX + this.rSail / 2, this.yPos - 5, mastX, this.yPos - 4 * this.rHeight);

}

function moveBoat() {
    this.xPos += this.speed;
     //if boat reaches right side, restart at left; randomize speed, width, height, offset
    if (this.xPos > width) { 
        this.xPos = 0;
        this.xOff = random(5, 15); 
        this.rWidth = random(40, 60);
        this.rHeight = random(8, 12);
        this.speed = random(0.1, 0.5);
    }

}

function makeBoat(x, y) {
    var boat = { xPos: x,
                yPos: y,
                xOff: random(5, 10), // offset of trapezoid
                rSail: random (10, 20),
                rWidth: random(30, 50),
                rHeight: random(8, 12),
                speed: random(0.1, 0.5),
                draw: drawBoat,
                move: moveBoat}
    return boat;
}

// clouds
function drawCloud(x, y) {
    fill(240, 245, 250, 120);
    ellipse(this.xPos, this.yPos, this.rWidth, this.rWidth);
    ellipse(this.xPos + this.rWidth/2, this.yPos, this.rWidth * 2, this.rWidth * 2);
    ellipse(this.xPos + this.rWidth, this.yPos, this.rWidth, this.rWidth);

}

function moveCloud() {
    this.xPos += this.speed;
    //if cloud reaches right side, restart at left; randomize speed
    if (this.xPos > width) {
        this.xPos = 0;
        this.speed = random(0.1, 0.4);
    }
}

function makeCloud(x, y) {
    var cloud = { xPos: x,
                yPos: y,
                rWidth: random(10, 20),
                speed: random(0.05, 0.3),
                draw: drawCloud,
                move: moveCloud}
    return cloud;
}

Sketch of boats!

I wanted to create a calm, cute landscape that wasn’t exactly too urban, so when I asked my roommate and she offered the suggestion of beaches, I thought of an ocean populated by little boats. I struggled with implementing objects, because this was the first time I had to code it personally instead of just editing certain elements, but doing this really helped me understand objects better.

Alice Fang – Looking Outwards – 10

An example from the Human Race Machine
Nancy Burson is an artist and photographer who uses morphing technology and digital manipulation in her works, which are often politically and/or socially charged. One project of hers that I am particularly interested in is Human Race Machine, which I think is extremely relevant in the current political climate as well. Originally, she created a software that would ‘age’ an image in order to track missing children for law enforcement; using similar software in facial recognition and facial alteration, this project is an interactive display which allows the user to see what they could look like if they were of a different race. As described on its website, “the concept of race is not genetic, but social”– her project is an extremely powerful tool for social reflection, and discussing diversity and issues of race of ethnicity. Although this was originally created in 2000 for the London Millennium Dome, the elegant software used to shift and change a human face and place oneself in the literal ‘face’ of another is still pertinent today, as human compassion and understanding is challenged by polarization and radical idealism.

Alice Fang – Project 9

after one image is formed, click for another!

/*
Alice Fang
Section E
acfang@andrew.cmu.edu
Project-09
*/

var friend = []; // picture of friend!
var state = 0; // indicates which picture

function preload() {
	var imgURL = [];
	imgURL[0] = "https://i.imgur.com/62FXNHg.jpg"; // full body
  	imgURL[1] = "https://i.imgur.com/ieqrCP3.jpg"; // torso
  	imgURL[2] = "https://i.imgur.com/e4nON6h.jpg"; // portrait

  	for (var i = 0; i < imgURL.length; i++) {
  		friend[i] = loadImage(imgURL[i]);
  	}
}

function setup() {
  createCanvas(270, 400);
  background('lightyellow');
  imageMode(CENTER);
  noStroke();
  friend[state].loadPixels();
}

function draw() {
	// select pixels from center of canvas outwards
	var pX = randomGaussian(width / 2, 75);
	var pY = randomGaussian(height / 2, 75);
	
	// constrain to canvas
	var cX = constrain(floor(pX), 0, width - 1);
	var cY = constrain(floor(pY), 0, height - 1);

	// get color from pixel
	var col = friend[state].get(cX, cY);

	fill(col); 
	textSize(16 - (state * 4));
	text("美", cX, cY);
}

function mousePressed() { // change image
	background('lightyellow'); // 'refresh' canvas
	state = state + 1;
	if (state > 2) state = 0;
}

This is a picture of my friend Meijie that I took for a photo class last semester! Originally, the shape I used for the color was an ellipse, and I spent a lot of time playing around with image ‘resolution’ compared to the underlying image. When I changed it to the character 美, which is ‘beauty’ in Chinese, and also part of her name, I didn’t like how large the character was and the lack of detail it created in the image. Because of that, I explored with changing the image from a full body shot, to a torso shot, to a closer crop of her face; I couldn’t decide which one I liked better so I decided to use all three! As the image zooms in, the resolution becomes sharper!

my friend!

Alice Fang – Looking Outwards – 09

I was looking through Looking Outwards and Jason’s Looking Outwards 07 caught my eye because of the simple little grid.

The grid that caught my eye
Screenshot of possible simulations of my life

This project by Nathan Yau simulates causes of death and related mortality rates. I agree with the assessment that the simplicity of conveying complex data is very successful in this project, and that interaction is balanced nicely with customization or individualization. It’s a little strange to think about though, because it compares the individualization to a general population of other simulated individualizations. My first reaction to seeing this chart was how low the mortality rate is for majority of a person’s lifespan, compared to what I assumed it would be; at 70 years old, the Asian female population has only a 9% mortality rate, which is shocking considering all of the negative stats and information that we retain from media. Similarly, Nathan Yau’s other visualizations on life and death are equally as intriguing, although I think this project is the easiest to understand.

Alice Fang – Looking Outwards – 08

Eyeo 2014 – Mike Bostock from Eyeo Festival on Vimeo.

Mike Bostock is a data-visualization specialist, and was an interactive graphics designer for The New You Times. He is also a developer of D3.js, which is a javascript library that is used for visualizing data. He graduated in 2000 with a BSE in computer science from Princeton University.

Many of the projects on his portfolio website are visualizations of data for The New York Times, with topics ranging from “Mapping every Path to the N.F.L Playoffs” to “Drought and Deluge in the Lower 48.” He combines his interest and passion for interactive visualization and animation with his knowledge of coding, to create very visually compelling images that aim to make learning and teaching abstract concepts more intuitive. I admire the simplicity in a lot of its work, and the consideration he has for human factors, like how people intake and react to the information, something which translates in his presentation as well.

Visualization of QuickSort, from his Eyeo Talk in 2014
His presentation on “Visualizing Algorithms” helped me understand different types of sorting algorithms through quick animation, in a way that just looking at code would have made difficult. He also addresses some tricky questions: how much visualization is too much, to the point where it overwhelms the viewer? I think his work is very applicable to design, where similar considerations have to be made about the best way to communicate ideas.