Instructions: Please expand your browser window as much as possible since my canvas is at the width of 600! Thank you
To interact with my speculative comic panel, here are a couple of interactions you can try.
To speed up the subway car, press the “f” key.
To slow down the subway car, press the “s” key. (Note: you cannot make the subway go backwards. If you press this key too many times, it will reset to its original speed.)
To reset the subway car speed, press the “r” key.
In frames 1 & 2, click your mouse around to see some falling stars.
/*
* Angela Lee
* Section E
* ahl2@andrew.cmu.edu
* Final Project
*/
// FRAME 1 VARIABLES
var f1height = 270; // height of first frame
var f1width = 275; // width of first frame
var f1x = 22.5; // upper left x-pos of first frame
var f1y = 12.5; // upper left y-pos of first frame
var f1spacing = 9; // spacing to the right of the first frame
var accelerate = 0; // how much the subway car speeds up/slows down
var skyBlue; // top of the sky color
var skyYellow; // bottom of the sky color
var waterYellow; // top of the water color
var waterBlue; // bottom of the water color
var shootingStarArray = []; // array for shooting stars
var ripples = []; // array for the ripples
// FRAME 2 VARIABLES
var f2width = 270; // width of second frame
var f2height = 130; // height of second frame
var f2spacing = 11.5; // spacing below the second frame
var planetx = 400; // x position of the big planet
var subwayStarArray = []; // array for subway stars
function setup(){
createCanvas (600, 300);
background(27, 34, 45);
// gradient for the sky in frame 1
skyBlue = color(2, 76, 98); // top of the sky color
skyYellow = color(233, 255, 191); // bottom of the sky color
// gradient for the water in frame 1
waterYellow = color(252, 239, 194); // top of the water color
waterBlue = color(59, 98, 115); // bottom of the water color
sub = makeSubway(); // creates a subway object in frame 1
// RIPPLES IN FRAME 1
// boundaries for ripples in frame 1
var top = 212; // top boundary
var bottom = height; // bottom boundary
// first ripples that come into frame 1
for (var i = 0; i < 10; i++) {
var rippleX = random(width);
var rippleY = random(top, bottom);
ripples[i] = makeRipples(rippleX, rippleY);
}
}
function draw(){
// FRAME 1
f1scene(); // the sky
// moving the subway
sub.move();
sub.draw();
// moving the ripples
for (var i = 0; i < ripples.length; i++) {
ripples[i].move();
ripples[i].draw();
}
removeRipple();
addRipple();
// FRAME 2
f2scene(); // background in frame 2
// moving the shooting stars
for (var i = 0; i < shootingStarArray.length; i++) {
shootingStarArray[i].move();
shootingStarArray[i].draw();
}
removeShootingStar(); // keeps shooting stars that are in frame
addShootingStar(); // adds shooting stars when mouse is clicked
f2car(); // subway car features in frame 2
astroboy(); // astronaut boy in frame 2
// FRAME 3
f3scene(); // background in frame 3
// TEXT
// words in frame 1
var f1text = "every night i take the subway home..."
// words in frame 2
var f2text = "i wonder..."
// words in frame 3
var f3text = "if our worlds will ever collide again."
noStroke();
fill("white");
textSize(14);
textStyle(ITALIC);
text(f1text, f1x + 10, 279); // frame 1 text
text(f2text, 490, 87); // frame 2 text
text(f3text, f1x + f1width + 25, 279); // frame 3 text
// BORDERS AROUND THE FRAMES
noStroke();
fill(27, 34, 45);
rect(0, 0, width, f1y); // top border
rect(0, 0, f1x, height); // border left of frame 1
rect(f1x + f1width, 0, 17, height); // border right of frame 1
rect(0, 0, f1x + f1width, f1y); // border above frame 1
// border below frame 1
rect(0, f1y + f1height, width, width - f1y - f1height);
// border below frame 2
rect(f1x + f1width, f1y + f2height, width - f1x - f1width, f2spacing);
// border right of frame 2
rect(f1x + f1width + f1spacing + f2width, 0,
width - f1x + f1width + f1spacing + f2width, height);
}
// ---------------- FUNCTIONS & OBJECTS FOR FRAME 1 ------------------
// FRAME 1 BACKGROUND
function f1scene(){
// GRADIENT SKY
noFill();
for (var s = f1y; s < f1y + f1height; s++) {
var skyInter = map(s, f1y, f1y + f1height, 0, 1);
var skyStroke = lerpColor(skyBlue, skyYellow, skyInter);
stroke(skyStroke);
line(f1x, s, f1x + f1width, s);
}
planets(); // planets in the sky
surface(); // surface of the moon
clouds(); // clouds in the sky
constellations(); // constellations in the sky
// TWINKLING STARS
fill(242, 242, 158);
for (var st = 0; st < 8; st++) {
// x positions for the twinkling stars
starX = [32, 77, 129, 177, 190, 205, 242, 276];
// y positions for the twinkling stars
starY = [91, 66, 63, 27, 79, 53, 25, 90];
// sizes for the twinkling stars
starSize = [5, 4, 4, 5, 4, 5, 4, 5];
star(starX[st], starY[st],
starSize[st] - random(1, 3), starSize[st], 5);
}
// SUBWAY TRACK
stroke(79, 94, 94);
fill(130, 142, 134);
// pillars holding the track
for (var i = 0; i < 6; i++) {
rect(34 + i * 48, 148, 14, 67);
}
rect(0, 148, f1x + f1width, 13); // bridge above the water
// GRADIENT WATER
noFill();
for (var w = 212; w < f1y + f1height; w++) {
var waterInter = map(w, 212, f1y + f1height, 0, 1);
var waterStroke = lerpColor(waterYellow, waterBlue, waterInter);
stroke(waterStroke);
line(f1x - 1, w, f1x + f1width, w);
}
}
// CONSTELLATIONS iN FRAME 1
function constellations() {
stroke(131, 178, 177);
strokeWeight(1);
// LIBRA CONSTELLATION
// the following lines join together to create libra
line(98.5, 90.5, 98.5, 84.5);
line(98.5, 84.5, 89.5, 70.5);
line(89.5, 70.5, 111.5, 60.5);
line(111.5, 60.5, 112.5, 74.5);
line(89.5, 70.5, 96.5, 56.5);
line(96.5, 56.5, 111.5, 60.5);
// LEO CONSTELLATION
// the following lines join together to create leo
line(146, 84, 142, 75);
line(142, 75, 154, 79);
line(154, 79, 146, 84);
line(146, 84, 148, 97);
line(148, 97, 156, 95);
line(156, 95, 156, 89);
line(156, 89, 161, 88);
line(161, 88, 172, 95);
line(172, 95, 169, 100);
// AQUARIUS CONSTELLATION
// the following lines join together to create aquarius
line(258, 49, 241, 63);
line(241, 63, 246, 65);
line(246, 65, 254, 63);
line(241, 63, 241, 67);
line(241, 67, 239, 69);
line(239, 69, 247, 83);
line(247, 83, 249.5, 77.5);
line(249.5, 77.5, 258, 76);
line(258, 76, 268, 82);
}
// CLOUDS IN FRAME 1
function clouds() {
noStroke();
// YELLOW CLOUDS
fill(210, 216, 130, 70);
var ycloudx = [92, 67, 103, 117, 122, 159]; // x coordinates
var ycloudy = [92, 88, 53, 56, 52, 70]; // y coordinates
var ycloudw = [56, 48, 27, 35, 19, 25]; // cloud width
var ycloudh = [9, 8, 5, 4, 3.5, 3]; // cloud height
// for loop generates 6 yellow clouds
for (var y = 0; y < 6; y++) {
ellipse(ycloudx[y], ycloudy[y], ycloudw[y], ycloudh[y]);
}
// PINK CLOUDS
fill(242, 136, 158, 150);
var pcloudx = [251, 65, 48, 183, 196, 212]; // x coordinates
var pcloudy = [109, 58, 55, 43, 46, 41]; // y coordinates
var pcloudw = [80, 30, 25, 35, 31, 29]; // cloud width
var pcloudh = [12, 7, 3, 4, 7, 9]; // cloud height
// for loop generates 6 pink clouds
for (var p = 0; p < 6; p++) {
ellipse(pcloudx[p], pcloudy[p], pcloudw[p], pcloudh[p]);
}
}
// SURFACE OF THE MOON IN FRAME 1
function surface() {
strokeWeight(1);
stroke(31, 73, 72);
fill(38, 104, 119);
// x positions of the moon surface's shape
var moonx = [f1x, 61, 107, 152, 202, 248, f1x + f1width,
f1x + f1width, f1x];
// y positions of the moon surface's shape
var moony = [187, 185, 191, 180, 188, 190, 195, 237, 237];
// SURFACE OF THE MOON
beginShape();
vertex(moonx[0], moony[0]);
// for loop for curved parts of the shape
for (var m = 0; m < 7; m++) {
curveVertex(moonx[m], moony[m]);
}
// straight parts of the moon surface shape
// these points can't be seen because they are
// blocked by the water
vertex(moonx[7], moony[7]);
vertex(moonx[8], moony[8]);
vertex(moonx[0], moony[0]);
endShape();
// MOON CRATERS
noStroke();
fill(22, 81, 91);
ellipse(58, 192, 29, 6);
ellipse(96, 202, 19, 3);
ellipse(146, 188, 19, 4);
ellipse(201, 201, 20, 3);
ellipse(241, 200, 34, 5);
}
// PLANETS IN THE SKY OF FRAME 1
function planets () {
// LARGEST PLANET
strokeWeight(2);
stroke(30, 76, 114);
fill(32, 111, 142);
ellipse(217, 164, 142, 142);
// SECOND LARGEST PLANET
strokeWeight(1);
stroke(24, 76, 119);
fill(18, 114, 130, 95);
ellipse(95, 119, 75, 75);
// RINGED PLANET
noStroke();
fill(234, 106, 124);
ellipse(63.5, 45, 51, 12); // rings
strokeWeight(0.5);
stroke(140, 65, 83);
fill(242, 136, 158);
ellipse(64, 44, 25, 25); // planet itself
// CRESCENT MOON
strokeWeight(1);
stroke(234, 96, 119);
fill(242, 242, 158);
ellipse(144.8, 40, 30, 30); // crescent part
noStroke();
fill(31, 94, 104);
ellipse(143, 37, 24, 24); // shadow part
}
// MAKING THE STARS IN FRAME 1 + 3
// called in f1scene() and f3scene()
function star(x, y, radius1, radius2, npoints) {
var angle = TWO_PI / npoints;
var halfAngle = angle / 2.0;
beginShape();
for (let a = 0; a < TWO_PI; a += angle) {
var sx = x + cos(a) * radius2;
var sy = y + sin(a) * radius2;
vertex(sx, sy);
sx = x + cos(a + halfAngle) * radius1;
sy = y + sin(a + halfAngle) * radius1;
vertex(sx, sy);
}
endShape(CLOSE);
}
// ------------- FUNCTIONS & OBJECTS FOR FRAME 2 -------------------
// FRAME 2 BACKGROUND
function f2scene() {
// WINDOW BEHIND THE RIDER
strokeWeight(4);
stroke(255);
fill(9, 74, 89);
rect(302, 29, 291, 85);
// NIGHTTIME SCENERY
// stars that come into frame when the subway is "moving"
noStroke();
fill(242, 242, 158);
for (var i = 0; i < subwayStarArray.length; i++) {
subwayStarArray[i].move();
subwayStarArray[i].draw();
}
removeSubwayStar(); // keeps stars in the keepSubwayStar array
addSubwayStar(); // based on a small probability, adds new stars
// big planet
strokeWeight(2);
stroke(30, 76, 114);
fill(32, 111, 142);
ellipse(planetx, 100, 130, 130);
planetx += 0.05 // planet slowly moves right as time passes
// MORE WINDOW FEATURES
// glass dividers on the subway windows
strokeWeight(3);
stroke(255);
var divideh = 24; // height of vertical dividers
var dividex = 335; // initial x position of vertical dividers
var dividey = 32; // y position of all vertical dividers
var dividespace = 70; // horizontal spacing in between
// 4 vertical dividers are created through lines
for (var d = 0; d < 4; d++) {
line(dividex + dividespace * d, dividey,
dividex + dividespace * d, dividey + divideh);
}
// horizontal line beneath the vertical dividers
line(302, 32 + divideh, 302 + f2width, 32 + divideh);
// window reflections
noStroke();
fill(253, 255, 255, 40);
// first reflection shape
beginShape();
vertex(330, 112);
vertex(326, 95);
vertex(381, 31);
vertex(399, 31);
vertex(330, 112);
endShape();
// second reflection shape
beginShape();
vertex(355, 95);
vertex(409, 31);
vertex(418, 31);
vertex(364, 95);
vertex(355, 95);
endShape();
// third reflection shape
beginShape();
vertex(423, 98);
vertex(472, 30);
vertex(509, 30);
vertex(465, 98);
vertex(423, 98);
endShape();
// fourth reflection shpae
beginShape();
vertex(512, 102);
vertex(555, 31);
vertex(572, 31);
vertex(531, 102);
vertex(512, 102);
endShape();
}
// FEATURES OF THE SUBWAY CAR IN FRONT OF THE WINDOW
function f2car() {
// subway car wall above the window
noStroke();
fill(143, 169, 183);
rect(f1x + f1width + 10, 0, f2width, 29);
// subway car wall below the window
rect(f1x + f1width + 10, 114, f2width, 40);
// subway chairs
// back of the chair that the rider leans against
stroke(73, 122, 121);
strokeWeight(1);
fill(174, 199, 206);
rect(320, 92, 247, 77, 15);
// indents in the chair
noStroke();
fill(153, 181, 188);
var indentx = 333; // initial x positions of indents
var indenty = 103; // y positions of indents
var indentw = 66; // width of indents
var indenth = 65; // height of indents
var indentround = 15; // roundness of indents
var indentspacing = 79; // spacing between each indent
// for loop creates 3 evenly spaced indents in the chair
for (var c = 0; c < 3; c++) {
rect(indentx + indentspacing * c, indenty, indentw,
indenth, indentround);
}
// handles above the window
noFill();
strokeWeight(3);
stroke(200, 214, 219);
var handlespacing = 82; // horizontal spacing between handles
// for loop creates 3 evenly spaced handles
for (var h = 0; h < 3; h++) {
ellipse(360 + handlespacing * h, 20, 10, 40);
}
}
// ASTRONAUT BOY SITTING IN THE SUBWAY
function astroboy() {
// arms
noStroke();
fill(160, 116, 87);
rect(397, 137, 13, 15); // right arm
// shirt
fill(194, 160, 224);
ellipse(379, 118.5, 66, 19); // shoulders
rect(356, 117, 47, 35); // body
rect(346, 119, 15, 19); // left sleeve
ellipse(354, 137.5, 16, 5); // bottom of left sleeve
rect(400, 119, 12, 19); // right sleeve
ellipse(404, 137.5, 16, 5); // bottom of right sleeve
// pocket on shirt
fill(234, 132, 132);
rect(385, 130, 11, 13);
ellipse(390, 143, 11, 3);
// right arm
noStroke();
fill(160, 116, 87);
quad(346, 138, 360, 138, 366, 150, 352, 150); // left arm
// astronaut helmet
// neck piece
noStroke();
fill(100);
ellipse(377, 111, 27, 6);
rect(364, 100, 27, 11);
// helmet
strokeWeight(1);
stroke(184, 209, 208);
fill("white");
ellipse(377, 83, 48, 48);
// glass piece
noStroke();
fill(33, 51, 68);
ellipse(376, 95.5, 34, 11);
quad(357, 85, 395, 85, 393, 96, 359, 96);
ellipse(376, 85, 38, 11);
// highlights on glass piece
fill(100, 107, 109);
ellipse(371, 84, 22, 2); // top highlight
ellipse(376, 98, 18, 1); // bottom highlight
}
// ------------------ FUNCTIONS/OBJECTS IN FRAME 3 ---------------------
// FRAME 3 BACKGROUND
function f3scene() {
// the dark night sky
noStroke();
fill(18, 38, 76);
rect(307, 12 + f2height + f2spacing, f2width, f2height);
// clouds
// darkest clouds
fill(39, 49, 117, 170);
ellipse(382, 193, 69, 48);
ellipse(534, 201, 95, 47);
// purple clouds
fill(118, 86, 153, 120);
ellipse(348, 230, 107, 71);
ellipse(445, 220, 142, 77);
ellipse(531, 225, 98, 52);
// darker pink clouds
fill(219, 127, 154, 130);
ellipse(374, 247, 76, 50);
ellipse(520, 251, 135, 50);
// glow behind the star in figure's hand
noStroke();
var glowx = 491; // x position of the glow center
var glowy = 216; // y position of the glow center
// controls how the glowing portion of the star
// grows and shrinks, so it looks like it's flickering
var osc = 10 + sin(millis()) * 1;
// outer glow
fill(239, 125, 172, 100);
ellipse(glowx, glowy, 20 + osc, 20 + osc);
// middle glow
fill(249, 155, 189, 120);
ellipse(glowx, glowy, 10 + osc, 10 + osc);
// inner glow
fill(237, 102, 138);
ellipse(glowx, glowy, 4 + osc, 4 + osc);
starfigure(); // mysterious figure in the clouds
// light pink clouds
noStroke();
fill(242, 165, 165, 200);
ellipse(337, 273, 76, 50);
ellipse(535, 276, 94, 50);
fill(242, 165, 165); // cloud covering the figure is opaque
ellipse(430, 278, 135, 50);
// twinkling stars
fill(242, 242, 158);
for (var st = 0; st < 5; st++) {
// x positions for the twinkling stars
starX = [366, 413, 491, 493, 514];
// y positions for the twinkling stars
starY = [240, 213, 216, 245, 178];
// sizes for the twinkling stars
starSize = [8, 9, 6, 8, 6];
star(starX[st], starY[st],
starSize[st] - random(4, 5), starSize[st], 5);
}
}
function starfigure() {
// body
fill(224, 184, 140);
ellipse(466, 195.5, 2, 5); // right ear
ellipse(443, 194.5, 2, 5); // left ear
rect(450, 203, 8, 9); // neck
quad(470, 235, 485, 219, 490, 220, 475, 240); // arm
ellipse(488.5, 219.5, 7, 3); // palm
//forefinger
beginShape();
vertex(491, 220);
vertex(491, 218);
vertex(494.2, 217.6);
vertex(494.5, 218.5);
vertex(492, 220);
vertex(491, 220);
endShape();
ellipse(494.5, 216.5, 1, 4);// upper finger
//hair
fill(248, 252, 195);
ellipse(454.5, 194, 23, 24);
hairx = [443, 466, 464, 463, 462, 452, 450, 449, 445, 445,
444, 441, 443, 443]; // x positions of hair coordinates
hairy = [194, 194, 208, 205, 208, 208, 206, 208, 207, 205,
207, 207, 199, 194]; // y positions of hair coordinates
beginShape();
for (var h = 0; h < 14; h++) {
vertex(hairx[h], hairy[h]);
}
endShape();
// cape
fill(30, 30, 124);
ellipse(454, 217, 35, 13);
quad(426, 266, 437, 218, 472, 218, 482, 266);
// folds in the cape
fill(16, 16, 94);
triangle(438, 259, 443, 221, 443, 259);
triangle(467, 260, 465, 222, 472, 260);
// constellations of the cape
strokeWeight(1);
stroke(113, 108, 183);
// first constellation
line(443, 216, 448, 224);
line(448, 224, 452, 225);
line(452, 225, 445, 235);
// second constellation
line(455, 236, 453, 244);
line(453, 244, 459, 246);
line(459, 246, 461, 242);
line(461, 242, 465, 250);
// constellation on the edge of the cape
line(432, 243, 434, 245);
line(434, 245, 429, 251);
}
// ------------- FUNCTIONS & OBJECTS FOR THE MOVING SUBWAY IN FRAME 1 -------------
// PRESSING KEYS TO CONTROL SUBWAY CAR SPEED
function keyPressed() {
// everytime the "f" key is pressed, the subway car's
// speed will increase by 1
if (key == "f") {
accelerate += 1;
}
// likewise, everytime "s" is pressed, the subway car's
// speed will reduce by 1
if (key == "s") {
accelerate -= 1;
}
// upon pressing the r key, it resets the accelerate to 0
// helpful in case you make the train way too fast and don't
// want to click s multiple times to get back to the original speed
if (key === "r") {
accelerate = 0;
}
}
// SUBWAY OBJECT
function makeSubway() {
var subwaycar = {x: -200,
y: 119,
speed: 4,
move: moveSubway,
draw: drawSubway}
return subwaycar;
}
function drawSubway() {
// subway cars
var sublength = 75; // length of subway car
var subheight = 28; // height of subway car
var subx = -200; // x position of subway car
var suby = 119; // y position of subway car
var round = 7.5; // rounded edges of rect
// subway car windows
var windowY = 125; // y position of subway window
var windowW = sublength / 4 // length of subway window
var windowH = 10; // height of subway window
var wspace = 4.5; // spacing between subway windows
// creating the subway car
for (var s = 0; s < 5; s++) {
stroke(125, 121, 132);
fill(230, 230, 252);
rect(this.x + sublength * s, suby, sublength, subheight, round);
for (var i = 0; i < 3; i++) {
fill(21, 68, 76);
rect(this.x + wspace * (i + 1) + windowW * i + sublength * s,
windowY, windowW, windowH);
}
}
}
function moveSubway() {
this.x += (this.speed + accelerate);
// to prevent the subway car from moving backwards,
// if accelerate becomes too big that its absolute value
// is greater than the speed, it resets to 0, which means
// the subway train will pause right before and upon pressing
// the s key again, it will reset to the original speed
if (accelerate < this.speed * -1) {
accelerate = 0;
}
// reset the x position of the last subway car to -500
// if the last subway car exits the frame
if (this.x > f1x + f1width) {
this.x = -500;
}
}
// ----------- FUNCTIONS & OBJECTS FOR FALLING STARS IN FRAME 1 & 2 ----------
// FALLING STARS IN FRAME 1 & 2 WHEN MOUSE IS PRESSED
function mousePressed() {
// when mouse is pressed, the function will
// to add a shooting star will be called with
// the x and y inputs of the mouse position
if (mouseX < width && mouseX > f1x &&
mouseY < f1y + f1height && mouseY > f1y) {
addShootingStar(mouseX, mouseY);
}
}
// SHOOTING STAR OBJECT
function makeShootingStar(xPos, yPos) {
var shootingStar = {x: xPos,
y: 0,
size: random(7, 20),
speed: random(3, 8),
move: moveShootingStar,
draw: drawShootingStar}
return shootingStar;
}
// DRAWING THE SHOOTING STAR
function drawShootingStar() {
// controls how the glowing portion of the star
// grows and shrinks, so it looks like it's flickering
var osc = 10 + sin(millis()) * 1;
noStroke();
// glowing part of the star
fill(244, 242, 168, 75);
ellipse(this.x, this.y, this.size + osc, this.size + osc);
// inner part of the star
fill(239, 235, 91);
ellipse(this.x, this.y, this.size, this.size);
}
// MOVING THE SHOOTING STAR
function moveShootingStar() {
this.y += this.speed;
}
// ADDING SHOOTING STARS
function addShootingStar(xPos, yPos) {
shootingStarArray.push(makeShootingStar(xPos, yPos));
}
// REMOVING SHOOTING STARS
function removeShootingStar() {
// array for keeping the stars
var keepStar = [];
// as long as the shooting stars are in bound of frame 1
// they will be pushed into the array for keeping the stars
for (var i = 0; i < shootingStarArray.length; i++) {
if (shootingStarArray[i].y < f1y + f1height +
shootingStarArray[i].size) {
keepStar.push(shootingStarArray[i]);
}
}
shootingStarArray = keepStar;
}
// ------------- FUNCTIONS & OBJECTS FOR THE RIPPLES IN FRAME 1 --------------
// RIPPLE OBJECT
function makeRipples(xPos, yPos) {
var makeRipple = {x: xPos,
y: yPos,
// longer ripples are in the front, shorter ones in the back
length: map(yPos, 212, height, 5, 75),
// thinner ripples in the back, thicker ones in the front
weight: map(yPos, 212, height, 1, 4),
// faster ripples in the front, slower ripples in the back
speed: map(yPos, 212, height, 0.5, 1),
move: moveRipple,
draw: drawRipple}
return makeRipple;
}
// MOVING THE RIPPLE
function moveRipple() {
// x position changes by speed
this.x += this.speed;
// if the ripple leaves the frame, reset x position
// to the left side of the frame
if (this.x > width + this.length) {
this.x === -this.length;
}
}
// ADDING RIPPLES
// using a tiny probability, add ripples
function addRipple() {
if (random(0, 1) < 0.025) {
ripples.push(makeRipples(-75, random(212, height)));
}
}
// REMOVING RIPPLES
function removeRipple() {
// an array for ripples to keep
var keepRipples = [];
// as long as ripples are within the bounds of frame 1,
// keep them in the keepRipples array
for (var i = 0; i < ripples.length; i++) {
if (ripples[i].x < f1x + f1width) {
keepRipples.push(ripples[i]);
}
}
ripples = keepRipples;
}
// DRAWING THE RIPPLE
function drawRipple() {
strokeWeight(this.weight);
stroke(255, 255, 255, 75);
line(this.x, this.y, this.x + this.length, this.y);
}
// ------------- FUNCTIONS & OBJECTS FOR THE STARS IN FRAME 2 -------------
// SUBWAY STAR OBJECT
function makeStar(xPos, yPos) {
var subwayStar = {x: xPos,
y: yPos,
radius1: random(5, 10),
npoints: 5,
speed: random(0.5, 2),
move: moveSubwayStar,
draw: drawSubwayStar}
return subwayStar;
}
// MOVING THE SUBWAY STARS
function moveSubwayStar() {
// x position changes by speed
this.x += this.speed;
// if the star leaves the frame, reset x position
// to the left side of the frame
if (this.x > width) {
this.x === f1x + f1width + 10;
}
}
// ADDING SUBWAY STARS
// using a tiny probability, add subway stars
function addSubwayStar() {
if (random(0, 1) < 0.02) {
subwayStarArray.push(makeStar(f1x + f1width + 10,
random(30, 100)));
}
}
// REMOVING SUBWAY STARS
function removeSubwayStar() {
// an array for subway stars to keep
var keepSubwayStar = [];
// as long as the stars are within the width,
// keep them in a separate array
for (var i = 0; i < subwayStarArray.length; i++) {
if (subwayStarArray[i].x < width) {
keepSubwayStar.push(subwayStarArray[i]);
}
}
subwayStar = keepSubwayStar;
}
// DRAWING THE SUBWAY STARS
function drawSubwayStar() {
fill(242, 242, 158);
var angle = TWO_PI / this.npoints;
var halfAngle = angle / 2.0;
beginShape();
for (let a = 0; a < TWO_PI; a += angle) {
var sx = this.x + cos(a) * (this.radius1 - 5);
var sy = this.y + sin(a) * (this.radius1 - 5);
vertex(sx, sy);
sx = this.x + cos(a + halfAngle) * this.radius1;
sy = this.y + sin(a + halfAngle) * this.radius1;
vertex(sx, sy);
}
endShape(CLOSE);
}
For my final project, I created a speculative cartoon panel. It’s speculative in the sense that the it forces the viewer to use their imagination to weave a story of what might have happened–the possibilities are endless. In this project, I enjoyed exploring my interests in illustration, concept art, and storytelling. To convey this dreamy, mysterious, magical atmosphere, I worked within a range of cool/teal tones, with pink and yellow highlights.
To begin, I created a moodboard of images/animations on Pinterest. Then, I sketched out/digitized my idea using simple shapes on Illustrator. Not only did I enjoy creating the static images, I think programming the interactions made the comic scene much more fun and engaging.
I am using 1 of my grace days for this late submission.
For my final project proposal, I’m planning to make a short animation illustrating rising water levels of a personified earth. One project that inspired my idea is 🔥👶🔥 by Joona Leppanen. This gif shows a still figure being partially masked by some smoke. I was particularly inspired by the different textures that Leppanen used in her illustration, but also the naturalness of that smoke. Using simple stroke and fill, she created a convincing air-like substance that communicates her message.
The second piece I’d like to talk about is a gif by Yukai Du. Using similar colors as Leppanen (which is coincidental), she’s able to capture water ripples in a simple yet elegant way. I am particularly inspired by how she represents depth through decreasing sizes of the eyes and the ripples. Her work has also made me consider putting material objects in the rising sea levels for my animation and how they can contribute to the story.
(Using a grace day :))
For my final project, I plan on creating a short animation personifying the Earth (as Mother Earth) and, through her, showing an aspect of the climate crisis. For instance, I found the flood map for 2050 particularly sad, so I might show her losing land mass and being surrounded by rising levels of sea water. I want to personify the earth because I think that seeing the emotions on their face will inspire more empathy, and because I personally love playing with character illustrations. I am fascinated by different illustration styles, but for this project, I would love to explore a simple illustration style with an interesting color palette through the aesthetics of my project. I anticipate animating the water level to be a challenge to get it to look natural and convincing.
/*
* Angela Lee
* Section E
* ahl2@andrew.cmu.edu
* Project 11 Generative Landscape
*/
// tallest mountains
var tallMtnDetail = 0.005; // detail in mountains
var tallMtnSpeed = 0.0001; // speed of mountains
// medium mountains
var medMtnDetail = 0.0075;
var medMtnSpeed = 0.0002;
// beach
var beachDetail = 0.003;
var beachSpeed = 0.0004;
// array for ripples
var ripples = [];
var yellow, pink;
function setup() {
createCanvas(480, 300);
frameRate(10);
// boundaries for ripples
var top = height * 5/8 + 10;
var bottom = height - 10;
// first ripples
for (var i = 0; i < 10; i++) {
var rippleX = random(width);
var rippleY = random(top, bottom);
ripples[i] = makeRipples(rippleX, rippleY);
}
// gradient for the water
yellow = color(255, 219, 140);
pink = color(247, 132, 124);
}
function draw() {
noStroke();
background(255, 156, 161);
makeSun(); // setting sun
makeTallMtn(); // tallest mountains
makeMedMtn(); // middle mountains
//makeWater();
gradientWater();
// ripple functions
updateRipple();
removeRipple();
addRipple();
makeBeach();
}
//----------------- FUNCTIONS BELOW THIS LINE -----------------------------
// SETTING SUN
function makeSun() {
// sun rays
noStroke();
fill(255, 161, 135);
ellipse(width/2, height * 3/8, 275, 275);
// sun
stroke(255);
strokeWeight(1);
fill(247, 217, 82);
ellipse(width/2, height * 3/8, 175, 175);
}
// TALLEST MOUNTAINS
function makeTallMtn() {
fill(252, 119, 165);
strokeWeight(1);
beginShape();
vertex(0, height);
for (var x = 0; x < width; x++) {
var t = (x * tallMtnDetail) + (millis() * tallMtnSpeed);
var y = map(noise(t), 0, 1, height / 8 * 2, height / 8 * 4);
vertex(x, y);
}
vertex(width, height);
endShape();
}
// MEDIUM MOUTAINS
function makeMedMtn() {
fill(230, 99, 160);
strokeWeight(1);
beginShape();
vertex(0, height);
for (var x = 0; x < width; x++) {
var t = (x * medMtnDetail) + (millis() * medMtnSpeed);
var y = map(noise(t), 0, 1, height / 8 * 3, height / 8 * 5);
vertex(x, y);
}
vertex(width, height);
endShape();
}
// OCEAN
function gradientWater() {
noFill();
for (var y = height * 5/8; y < height; y++) {
var inter = map(y, height * 5/8, height, 0, 1);
var c = lerpColor(yellow, pink, inter);
stroke(c);
line(0, y, width, y);
}
}
// BEACH
function makeBeach() {
fill(252, 195, 182);
strokeWeight(1);
beginShape();
vertex(0, height);
for (var x = 0; x < width; x++) {
var t = (x * beachDetail) + (millis() * beachSpeed);
var y = map(noise(t), 0, 1, height * 7/8, height * 19/20);
vertex(x, y);
}
vertex(width, height);
endShape();
}
//----------------- RIPPLE OBJ & FUNCTIONS BELOW THIS LINE -----------------------------
// RIPPLE OBJECT
function makeRipples(xPos, yPos) {
var makeRipple = {x: xPos,
y: yPos,
// longer ripples are in the front, shorter ones in the back
length: map(yPos, height * 5/8, height, 5, 75),
// thinner ripples in the back, thicker ones in the front
weight: map(yPos, height * 5/8, height, 1, 4),
// faster ripples in the front, slower ripples in the back
speed: map(yPos, height * 5/8, height, 1, 4),
move: moveRipple,
draw: drawRipple}
return makeRipple;
}
// MOVING THE RIPPLE
function moveRipple() {
// x position changes by speed
this.x += this.speed;
// if the ripple leaves the frame, reset x position
// to the left side of the frame
if (this.x > width + this.length) {
this.x === -this.length;
}
}
// ADDING RIPPLES
// using a tiny probability, add ripples
function addRipple() {
if (random(0, 1) < 0.075) {
ripples.push(makeRipples(-75, random(height * 5/8, height)));
}
}
// REMOVING RIPPLES
function removeRipple() {
// an array for ripples to keep
var keepRipples = [];
for (var i = 0; i < ripples.length; i++) {
if (ripples[i].x < width) {
keepRipples.push(ripples[i]);
}
}
ripples = keepRipples;
}
// UPDATE AND DISPLAY RIPPLE
function updateRipple() {
for (var i = 0; i < ripples.length; i++) {
ripples[i].move();
ripples[i].draw();
}
}
// DRAWING THE RIPPLE
function drawRipple() {
strokeWeight(this.weight);
stroke(255);
line(this.x, this.y, this.x + this.length, this.y);
}
For my landscape, I was inspired the 80s vaporwave illustration style and color. I created a sunset scene and had fun with showing depth through details. For instance, the sizes and speeds of the mountains and ripples are all dependent on the “distance” they would be away from the viewer (the farther something is, the slower and smaller it is).
I’m using a grace day for this project
I’d like to focus on the media artist Eunsu Kang, the artist behind the installation Shin’m. Kang studied at Ewha Woman’s University, where she received her BFA and MFA. For her postgraduate studies, Kang received an MA from UCSC and a PhD from the University of Washington.
Shin’m is a hybrid of a performance and installation which explores the relationships between the body and space through spatial drawing of sound. Kang created this piece in collaboration with Diana Garcia-Snyder (a choreographer), Donald Craig, and Bo Choi (costume designer). I loved seeing the environment respond visually and sonically to the participants’ movements. I think it inspired the participant to try different movements to see what kind of environment they could generate. Moreover, though the piece is very technical, the sounds of nature add a different dimension to the environment and create a soothing, beautiful atmosphere.
]]>Long ago, the four nations lived together in harmony. Then, everything changed when the Fire Nation attacked…
/*
* Angela Lee
* Section E
* ahl2@andrew.cmu.edu
* Project 10 Sonic Sketch
*/
// illustrations of the avatar characters
// illustrations created by me :)
var imageLinks = [
"https://i.imgur.com/b8bcUw6.png",
"https://i.imgur.com/CNlT1Ed.png",
"https://i.imgur.com/vmDdgoD.png",
"https://i.imgur.com/yOvvKhm.png"];
// variables in which images will be loaded into
var katara;
var aang;
var toph;
var zuko;
// variables for sound
var waterSound;
var airSound;
var earthSound;
var fireSound;
// loading the images and sounds
function preload() {
// call loadImage() and loadSound() for all media files here
aang = loadImage("https://i.imgur.com/b8bcUw6.png");
katara = loadImage("https://i.imgur.com/CNlT1Ed.png");
toph = loadImage("https://i.imgur.com/vmDdgoD.png");
zuko = loadImage("https://i.imgur.com/yOvvKhm.png");
waterSound = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/11/water-3.wav");
airSound = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/11/wind.wav");
earthSound = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/11/earth.wav");
fireSound = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/11/fire.wav");
}
function setup() {
createCanvas(480, 480);
//======== call the following to use sound =========
useSound();
}
function soundSetup() { // setup for audio generation
waterSound.setVolume(1);
airSound.setVolume(1.25);
earthSound.setVolume(1);
fireSound.setVolume(1);
}
function draw() {
background(200);
// images of the characters
image(aang, 0, 0, width/2, height/2);
image(katara, width/2, 0, width/2, height/2);
image(toph, 0, height/2, width/2, height/2);
image(zuko, width/2, height/2, width/2, height/2);
}
function mousePressed() {
// airbending sounds play if user clicks in aang's space
if (mouseX >= 0 & mouseX < width/2 &&
mouseY >= 0 && mouseY < height/2) {
airSound.play();
} else { // if user clicks away, sound stops
airSound.pause();
}
// waterbending sounds play if user clicks in katara's space
/*
note: the water sound has some silence in it, so if you
hear silence even after pressing on katara, it may be
the silent part of the file. click again to start from
the beginning of that sound file.
*/
if (mouseX >= width/2 & mouseX < width &&
mouseY >= 0 && mouseY < height/2) {
waterSound.play();
} else { // if user clicks away, sound stops
waterSound.pause();
}
// earthbending sounds play if user clicks in toph's space
if (mouseX >= 0 & mouseX < width/2 &&
mouseY >= height/2 && mouseY < height) {
earthSound.play();
} else { // if user clicks away, sound stops
earthSound.pause();
}
// firebending sounds play if user clicks in zuko's space
if (mouseX >= width/2 & mouseX < width &&
mouseY >= height/2 && mouseY < height) {
fireSound.play();
} else { // if user clicks away, sound stops
fireSound.pause();
}
}
Because one of the requirements was to feature at least 4 sounds, I thought of things that came in four and could be represented. There are 4 nations in the world of Avatar, which is a show I love. To introduce people who haven’t watched the show before, I used sound to communicate which nation each major character is from. The sounds of earth, air, water, and fire are all pretty distinct and recognizable, so I think it was successful in communicating those nations. Plus, I had a lot of fun creating the illustrations for the characters.
]]>“Apparatum” is a sound installation with sound inspired by Bogusław Schaeffer and the aesthetics inspired by Oskar Hansen. The installation in general also draws inspiration from the heritage of the Polish Radio Experimental Studio. The project consists of analog sound generators which are controlled through a digitized sheet music touch pad. I admire the speculative nature of the piece. Because it’s not commercial music that has to appeal to a wide audience, it feels much more thoughtful and edgy, and I am drawn to the process of creating it. I think that the artistic sensibilities manifested in the visual design. The aesthetics complement the sound art without overpowering it, since it has minimal grayscale colors, limited use of textures, and consistent forms. The textures of the sound are also quite interesting, challenging you to think of new ways to weave sounds and tones together.
]]>For this week’s Looking Outwards post, I’ll be discussing Gretchen Kupferschmid’s Looking Outwards 07 about the project “Halo.” Like Gretchen, I also appreciate that the project adds an artistic element to data visualization since data, especially in the medical field, is often displayed traditionally through graphs or just listed out as numbers. Being aware of your health data is very important so that you can make informed decisions that will affect your body positively, and I see this visualization as a way of 1. summarizing dense quantitative content into an impression (that you can take in within a glimpse) and 2. engaging users so that they will want to learn more about their own health. While a creative visualization like this by itself may not provide all the necessary details for a holistic report, integrating the two creates an experience where checking your own health can become a visual delight. I also agree with Gretchen that showing data in this visual way allows users to more intuitively and efficiently compare large sets of data without having to process all the numbers in their head.
]]>/*
* Angela Lee
* Section E
* ahl2@andrew.cmu.edu
* Project 9 Computational Portrait
*/
var underlyingImage;
// loading the image
function preload() {
var url = "https://i.imgur.com/GsniIVQ.jpg";
underlyingImage = loadImage(url);
}
function setup() {
createCanvas(300, 400);
background(145, 1, 10);
underlyingImage.loadPixels();
frameRate(60); // how fast the pixels come up
}
function draw() {
var px = random(width);
var py = random(height);
var ix = constrain(floor(px), 0, width);
var iy = constrain(floor(py), 0, height);
var theColorAtLocationXY = underlyingImage.get(ix, iy);
noStroke();
fill(theColorAtLocationXY);
ellipseW = random(1, 8); // original ellipse width
ellipseH = random(1, 8); // original ellipse height
keyPressed(); // key pressed determines ellipse width and height
// drawing the ellipse
ellipse(px, py, ellipseW, ellipseH);
}
// what happens if the key is pressed
function keyPressed() {
// ellipse will grow if g is pressed
if (key == "g") {
ellipseW = random (5, 15);
ellipseH = random(5, 15);
} else if (key == "s") {
// ellipse will shrink if s is pressed
ellipseW = random(0.5, 3);
ellipseH = random(0.5, 3);
} else if (key == "n") {
// ellipses will return to original size
ellipseW = random(1, 8);
ellipseH = random(1, 8);
}
return ellipseW;
return ellipseH;
}
For this portrait, I started out with ellipses being randomized for a width and height ranging from 1 to 5. However, I got impatient while waiting for the portrait to finish, so I decided to make the ellipse grow when you pressed the key “g.” But since I didn’t like how blurry the portrait got, I gave the user the option of returning the ellipses back to its original size or an even smaller size.
For this Looking Outwards, I decided to focus on the designer Alexander Chen (http://chenalexander.com/), who is a creative director at Google Creative Lab. He studied Engineering and Digital Media Design at the University of Pennsylvania and is currently working in Cambridge, Massachusetts. He also is a musician, and much of his work integrates his passion for visual design and music. One of his key projects was MTA.ME, in which he translated the New York City subway map into a string instrument. He has also created visualizations of the first Prelude of Bach’s Cello Suites. I particularly enjoy his Bach’s cello suite visualizations because how sophisticated yet natural looking the motions are. I enjoy how his works are very explorative and experimental in nature. While they may not act as solutions to a defined problem, they suggest new and thoughtful ways of visualizing sound that may influence other people’s work. What I enjoyed about his presentation was his conversational tone–it made him as a designer and his work seem very friendly and approachable. Also, he constantly referred to the work and specific parts of a piece he was talking about, so I didn’t have to speculate but could see it for myself. I also admired the straightforwardness and simplicity of his website. I found it easy to navigate and it allowed the work to shine rather than my focus being on the website.
]]>