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
/*
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.
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
]]>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.
/*
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.
Click on the canvas to make marks, and press the ENTER key to switch between the lines and the jagged swirlies!
]]>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
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;
}
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.
]]>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!
I was looking through Looking Outwards and Jason’s Looking Outwards 07 caught my eye because of the simple little grid.
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.