For my final project, I wanted to show that climate change is not just a singular concept, but rather a buildup of many different factors and situations, as well as how they are related to one another. First, trees are cut down to clear space for more buildings, people in these buildings require electricity which requires the burning of fossil fuels, and the release of carbon dioxide into the air exacerbates global warming and melts glaciers that increase ocean levels.
The user goes through these 3 scenes and interacts with them, worsening the situation. At the end of each scene, they are presented with statistics of each action and they realize those consequences. Perhaps the polar bear is a metaphor for humans and at the end when the polar bear “drowns”, the user is asked “Are you sorry now?” telling them to rethink their actions before it is too late.
If I had more time, I would have liked to make my graphics more detailed. I would have also liked to include sound but I was having too many problems with looping the sounds so I decided not to include them.
//Catherine Liu
//jianingl@andrew.cmu.edu
//Section D
//Final Project
//An interactive narrative consisting of 3 scenes
var scene = 1;
var frameNum = 0;
var trees = []; //array to hold objects for trees
var treeCount = 0; //keeping track of framecount specifically for trees
var buildings = []; //array to hold objects for buildings
var lightSwitch = false; //tracking if switch is on or off
var smokeTrail1 = []; //array for holding smoke trail
var smokeTrail2 = []; //array for holding smoke trail
var seaHeight; //tracking height of sea
var gameFail = 0; //tracking if polar bear has drowned
var finalFrame = false; //tracking framecount to show final frame
var finalCount = 0; //tracks count of final frame
//making sine wave for ocean (code refrenced from p5js.org)
var xspacing = 5; // Distance between each horizontal location
var w; // Width of entire wave
var theta = 0.0; // Start angle at 0
var amplitude = 25.0; // Height of wave
var period = 500.0; // How many pixels before the wave repeats
var dx; // Value for incrementing x
var yvalues; // Using an array to store height values for the wave
function setup() {
createCanvas(600,400);
//adds trees to array
for (i = 0; i < 5; i++) {
var rx = random(width);
trees[i] = makeTree(rx);
}
// create an initial collection of buildings
for (var i = 0; i < 10; i++){
var rx = random(width);
buildings[i] = makeBuilding(rx);
}
//sets up smoke object
for (i = 170; i > 50 ; i-=15) {
smoke1 = { x: 400 + random(-10,10),
y: i,
size: random(30,50)
}
smokeTrail1.push(smoke1)
smoke2 = {x: 500 + random(-10,10),
y: i,
size: random(30,50)
}
smokeTrail2.push(smoke2)
}
//setting up sine wave
w = width + 16;
dx = (TWO_PI / period) * xspacing;
yvalues = new Array(floor(w / xspacing));
}
function draw() {
//tracking framecount for scenes to show up
frameNum += 1
if (frameNum == 200){
scene = 1.5
}
if (frameNum == 250){
scene = 2;
}
if (frameNum == 450){
scene = 2.5
}
if (frameNum == 500) {
scene = 3;
}
if (finalFrame) {
finalCount += 1;
}
//Scene 1: Cutting down trees
if (scene == 1) {
background(230,230,250);
noStroke();
fill(0,100,0);
rect(0,height-50,width,50)
//show trees until a certain point and switch to buildings
if (treeCount <= 120) {
textSize(20);
textAlign(CENTER);
text("Try cutting down trees with the saw", width/2, 30);
updateAndDisplayTrees();
removeTrees();
addNewTree();
treeCount += 1
}
if (treeCount > 120) {
fill(50);
rect(0,height-50,width,50)
text("There's no more trees to cut...", width/2, 30);
updateAndDisplayBuildings();
removeBuildings();
addNewBuildings();
}
//function for making saw
drawSaw();
}
if (scene == 1.5) {
background(0);
textSize(20);
textAlign(CENTER);
fill(255);
text("More than 3.5 billion trees are cut down annually",width/2,height/2);
text("for human needs and urban development", width/2, height/2+30)
}
//Scene 2: Factory producing smoke
if (scene == 2) {
//function for creating smoke
factorySmoke();
fill(255)
text("Click the light switch...", 120,90);
text("watch the window",120,120);
}
if (scene == 2.5) {
background(0);
textSize(20);
textAlign(CENTER);
fill(255);
text("62% of our electricity comes from fossil fuels",width/2,height/2);
text("1.5 million metric tons of C02 are released annually",width/2,height/2+30)
}
//Scene 3: rising ocean levels and melting glaciers
if (scene == 3) {
noStroke();
frameRate(10);
background(70,130,180);
textSize(20);
textAlign(CENTER);
fill(255);
text("Keep the polar bear above the water, or else...",width/2,30)
//drawing mountains
fill(100,146,198);
triangle(0,height,150,50,300,height);
triangle(450,height,550,100,650,height);
fill(135,206,235);
triangle(100,height,300,150,500,height);
//draw polar bear that follows mouse
polarBear();
//drawing wave
seaHeight = map(frameNum,500,700,height,-10);
fill(193,223,255);
calcWave();
renderWave(seaHeight);
// if polar bear drowns too many times or ocean rises canvas top, scene ends
if (gameFail>=10 || seaHeight <= 0) {
background(0);
fill(255);
text("The ocean is expected to rise 15-25cm by 2050",width/2,height/2);
finalFrame = true;
}
}
if (finalCount>20) {
background(0);
text("Are you sorry now?", width/2, height/2);
}
}
function treeDisplay() {
//draw each tree
//checking for saw intersection with tree
treeUpdate();
frameRate(10);
//tree spawns initially but if saw intersects with tree, only draw trunk
if (this.intersect == false) {
noStroke();
fill(194, 178, 128);
push();
translate(this.x,height-40);
rect(0,-this.vert,this.hor,this.vert);
fill(0,this.color,100);
ellipse(this.hor,-this.vert,this.crown1,this.crown2);
pop();
} else if (this.intersect == true) {
fill(101,67,33);
push();
translate(this.x,height-40);
rect(0,-this.vert/2,this.hor,this.vert/2);
pop();
}
}
function treeMove() {
this.x += this.speed;
}
function makeTree(treeX) {
var tree= {x:treeX,
hor: random(10,20),
vert: random(150,250),
speed: -3,
color: random(255),
crown1: random(80,100),
crown2: random(50,80),
intersect: false,
move: treeMove,
display: treeDisplay,
update: treeUpdate,
}
return tree
}
function updateAndDisplayTrees() {
for (var i = 0; i < trees.length; i++){
trees[i].update();
trees[i].display();
trees[i].move();
}
}
function removeTrees() {
//if tree goes off canvas, remove it
var treesToKeep = [];
for (var i = 0; i < trees.length; i++){
if (trees[i].x + trees[i].crown2 > 0) {
treesToKeep.push(trees[i]);
}
}
trees = treesToKeep;
}
function addNewTree() {
//add new trees after trees move off canvas
var newTreeLikelihood = 0.06;
if (random(0,1) < newTreeLikelihood) {
trees.push(makeTree(width));
}
}
function drawSaw() {
fill(100);
rect(mouseX, mouseY,80,20);
for (i = mouseX+5; i < mouseX + 85; i += 10) {
circle(i, mouseY+20, 10);
}
fill("red")
rect(mouseX-30,mouseY,40,20,20);
}
function updateAndDisplayBuildings(){
// Update the building's positions, and display them.
for (var i = 0; i < buildings.length; i++){
buildings[i].move();
buildings[i].display();
}
}
function removeBuildings(){
//remove buildings as they go off screen
var buildingsToKeep = [];
for (var i = 0; i < buildings.length; i++){
if (buildings[i].x + buildings[i].breadth > 0) {
buildingsToKeep.push(buildings[i]);
}
}
buildings = buildingsToKeep; // remember the surviving buildings
}
function addNewBuildings() {
// With a very tiny probability, add a new building to the end.
var newBuildingLikelihood = 0.007;
if (random(0,1) < newBuildingLikelihood) {
buildings.push(makeBuilding(width));
}
}
function buildingMove() {
this.x += this.speed;
}
function buildingDisplay() {
// draw the building and some windows
var floorHeight = 40;
var bHeight = this.nFloors * floorHeight;
fill(this.wallCol);
noStroke()
push();
translate(this.x, height - 40);
rect(0, -bHeight, this.breadth, bHeight);
fill(this.windowCol);
for (var i = 0; i < this.nFloors; i++) {
rect(5, -15 - (i * floorHeight), this.breadth - 10, 10);
}
pop();
}
function makeBuilding(birthLocationX) {
var bldg = {x: birthLocationX,
breadth: 50,
speed: -3,
nFloors: round(random(2,8)),
windowCol: random(200,255),
wallCol: random(100,150),
move: buildingMove,
display: buildingDisplay}
return bldg;
}
function factorySmoke() {
//tracking height of smoke so it moves down every frame
var smokeLevel = map(frameNum,250,450,0,150);
if (lightSwitch == false) { //if light is off
frameRate(20);
noStroke();
background(176,196,222);
fill(100);
//moving smoke up the canvas by adding random dx and dy
for (i = 0; i < smokeTrail1.length; i++) {
var randomDx = (random(5));
var randomDy = (random(-5,0));
smokeTrail1[i].x += randomDx;
smokeTrail1[i].y += randomDy;
//reset x and y position if smoke leaves canvas
if (smokeTrail1[i].y <= 10) {
smokeTrail1[i].x = 400 + random(-10,10);
smokeTrail1[i].y = 150;
}
circle(smokeTrail1[i].x, smokeTrail1[i].y, 50)
}
for (i = 0; i < smokeTrail2.length; i++) {
var randomDx = (random(5));
var randomDy = (random(-5,0));
smokeTrail2[i].x += randomDx;
smokeTrail2[i].y += randomDy;
//reset x and y position if smoke leaves canvas
if (smokeTrail2[i].y <= 10) {
smokeTrail2[i].x = 500 + random(-10,10);
smokeTrail2[i].y = 200;
}
circle(smokeTrail2[i].x, smokeTrail2[i].y, 50)
}
//smoke funnels
fill(10);
rect(370,160,50,150);
rect(470,210,50,120);
//drawing smoke accumulating at top of window
calcWave();
fill(100);
beginShape();
vertex(width,0);
vertex(0,0);
for (let x = 0; x <= yvalues.length; x++) {
vertex(x * xspacing, smokeLevel+ yvalues[x])
}
endShape(CLOSE);
//walls
fill(178,157,105);
rect(0,0,width,30);
rect(0,0,width-370,height);
rect(0,width,-10,height);
rect(0,height,width,-(height-310));
//light switch
fill(255);
rect(70,140,70,100);
fill(210);
rect(75,190,60,45);
}else if (lightSwitch == true) { //if light is turned on
frameRate(20);
noStroke();
background(176,196,222);
fill(50);
//moving smoke up the canvas by adding random dx and dy
for (i = 0; i < smokeTrail1.length; i++) {
var randomDx = (random(5));
var randomDy = (random(-5,0));
smokeTrail1[i].x += randomDx;
smokeTrail1[i].y += randomDy;
//reset x and y position if smoke leaves canvas
if (smokeTrail1[i].y <= 10) {
smokeTrail1[i].x = 400 + random(-10,10);
smokeTrail1[i].y = 150;
}
circle(smokeTrail1[i].x, smokeTrail1[i].y, 100)
}
for (i = 0; i < smokeTrail2.length; i++) {
var randomDx = (random(5));
var randomDy = (random(-5,0));
smokeTrail2[i].x += randomDx;
smokeTrail2[i].y += randomDy;
//reset x and y position if smoke leaves canvas
if (smokeTrail2[i].y <= 10) {
smokeTrail2[i].x = 500 + random(-10,10);
smokeTrail2[i].y = 200;
}
circle(smokeTrail2[i].x, smokeTrail2[i].y, 100)
}
//smoke funnels
fill(10);
rect(370,160,50,150);
rect(470,210,50,120);
//drawing smoke accumulating at top of window
calcWave();
fill(50);
beginShape();
vertex(width,0);
vertex(0,0);
for (let x = 0; x <= yvalues.length; x++) {
vertex(x * xspacing, smokeLevel+ yvalues[x])
}
endShape(CLOSE);
//walls
fill(247,224,169);
rect(0,0,width,30);
rect(0,0,width-370,height);
rect(0,width,-10,height);
rect(0,height,width,-(height-310));
//lightswitch
fill(255);
rect(70,140,70,100);
fill(210);
rect(75,145,60,45);
}
}
function treeUpdate() {
//if saw intersects with tree, change the state of variable to true
if (mouseX+40 > this.x & mouseX+40< this.x + this.hor) {
this.intersect = true;
}
}
function mousePressed() {
//turn light switch on and off if mouse is pressed on lightswitch
if (mouseX>70 & mouseX<140 && mouseY>190 && mouseY<240) {
lightSwitch = true;
skySmoke = 1;
} else if (mouseX>70 & mouseX<140 && mouseY>140 && mouseY<190) {
lightSwitch = false;
skySmoke = 0;
}
}
function polarBear() {
//draws polar bear
fill(255);
ellipse(mouseX,mouseY,100,70);
circle(mouseX-50,mouseY-30,50);
circle(mouseX-70,mouseY-50,20);
circle(mouseX-35,mouseY-50,20);
rect(mouseX-40,mouseY+20,10,20,10);
rect(mouseX+30,mouseY+20,10,20,10);
fill(70,130,180);
circle(mouseX-60,mouseY-30,5);
circle(mouseX-40,mouseY-32,5);
ellipse(mouseX-50,mouseY-25,10,5)
//if polar bear moves below ocean surface, increase count of fails
if (mouseY-70 >= seaHeight) {
gameFail +=1;
}
}
function calcWave() {
// Increment theta (try different values for
// 'angular velocity' here)
theta += 0.2;
// For every x value, calculate a y value with sine function
let x = theta;
for (let i = 0; i < yvalues.length; i++) {
yvalues[i] = sin(x) * amplitude;
x += dx;
}
}
function renderWave(yPos) {
noStroke();
beginShape();
vertex(0,height);
// A simple way to draw the wave with an ellipse at each location
for (let x = 0; x <= yvalues.length; x++) {
vertex(x * xspacing, yPos + yvalues[x])
}
vertex(width,height);
endShape(CLOSE);
}
]]>For this project, I made 2 fish who are lovers that swim randomly around an aquarium looking for each other. When they meet, they fall in love again and the supportive crab in the corner claps for them and a love song plays.
//Catherine Liu
//jianingl@andrew.cmu.edu
//Section D
//project 10
//two fishes swimming around tank (bump sound when they hit the sides)
//when they bump into each other they fall in love (song plays)
//crab clapping for them (clapping sound)
//house bubbles turn into hearts (bubbling sound)
// use http://localhost:8000/ to run html
//variables for pink fish
var x1 = 50;
var y1 = 50;
var dx1 = 50;
var dy1 = 20;
//variables for green fish
var x2 = 300;
var y2 = 300;
var dx2 = 40;
var dy2 = 30;
var meet = false; //checks to see if fish meet
var crabClaw = 1; //keeps track of frame for crab claw clapping
var songCount = 0; //counter for song played
var bubbleArrayX = []; //x value for each bubble
var bubbleArrayY = []; //y value for each bubble
var heartArrayX = []; //x value for each heart
var heartArrayY = []; //y value for each heart
//sound variables
var bump;
var bubbles;
var heart;
var clap;
function preload() {
bump = loadSound("https://courses.ideate.cmu.edu/15-104/f2021/wp-content/uploads/2021/11/bump.mp3");
bubbles = loadSound("https://courses.ideate.cmu.edu/15-104/f2021/wp-content/uploads/2021/11/bubble-4.wav");
heart = loadSound("https://courses.ideate.cmu.edu/15-104/f2021/wp-content/uploads/2021/11/lovesong.mp3");
clap = loadSound("https://courses.ideate.cmu.edu/15-104/f2021/wp-content/uploads/2021/11/clap.wav");
}
function setup() {
noStroke();
createCanvas(600, 400);
useSound();
frameRate(10);
//sets up bubble and heart arrays
for (i = 170; i > 50 ; i-=15) {
bubbleArrayX.push(400 + random(-10,10));
bubbleArrayY.push(i);
heartArrayX.push(400 + random(-10,10));
heartArrayY.push(i);
}
}
function soundSetup() {
bump.setVolume(0.2);
bubbles.setVolume(0.2);
clap.setVolume(0.1);
heart.setVolume(0.3)
}
function draw() {
background(65,105,225);
fill(255,250,205);
rect(0,height-50,width,height);
drawCrab();
drawHouse();
//if fish meet, play song and stop bump sounds
if (meet == true) {
bump.stop();
playSong();
}
//if fish have not met, keep moving them
if (meet == false) {
x1 += dx1;
y1 += dy1;
}
fill(255,182,193) //pink fish
drawFish(x1,y1,dx1);
dx1 += random(-5,5);
dy1 += random(-5,5);
if (x1 < 40) {
bump.play();
dx1 = -dx1;
}
if (x1 > width - 40) {
bump.play();
dx1 = -dx1;
}
if (y1 < 40) {
bump.play();
dy1 = -dy1;
}
if (y1 > height - 75) {
bump.play();
dy1 = -dy1;
}
if (meet == false) {
x2 += dx2;
y2 += dy2;
}
fill(173,255,47) //green fish
drawFish(x2,y2,dx2);
dx2 += random(-5,5);
dy2 += random(-5,5);
if (x2 < 40) {
bump.play();
dx2 = -dx2;
}
if (x2 > width - 40) {
bump.play();
dx2 = -dx2;
}
if (y2 < 40) {
bump.play();
dy2 = -dy2;
}
if (y2 > height - 75) {
bump.play();
dy2 = -dy2;
}
meetUp(25); //checks if fish have met
}
function drawFish(x, y, dx) {
ellipse(x, y, 60, 40); //fish body
//checks which direction fish is facing to draw tail
if (dx > 0) {
if (meet == true) { //if fish met, draw smile
push();
fill(0);
circle(x+20,y,10);
pop();
push();
noFill();
stroke(2);
arc(x+10, y+7, 10, 10, 0, PI);
pop();
} else { //no smile
push();
fill(0);
circle(x+20,y,10);
pop();
}
triangle(x-30, y, x-50, y-10, x-50, y+10); //fish tail
} if (dx < 0) {
if (meet == true) { //if fish met, draw smile
push();
fill(0);
circle(x-20,y,10);
pop();
push();
noFill();
stroke(2);
arc(x-10, y+7, 10, 10, 0, -PI);
pop();
} else { //no smile
push();
fill(0);
circle(x-20,y,10);
pop();
}
triangle(x+30, y, x+50, y-10, x+50, y+10); //fish tail
}
}
//checks if fish have "met" by checking distance between them <= 0 and draws heart
function meetUp(size) {
if (dist(x1, y1, x2, y2) <= 40) {
//draws heart
fill("red")
var xPos = x1;
if ((y1-70) < 10) {
var yPos = y1 + 70;
} else {
var yPos = y1 - 70;
}
beginShape();
vertex(xPos, yPos);
bezierVertex(xPos - size / 2, yPos - size / 2, xPos - size, yPos + size / 3, xPos, yPos + size);
bezierVertex(xPos + size, yPos + size / 3, xPos + size / 2, yPos - size / 2, xPos, yPos);
endShape();
meet = true; //change variable to true to show fish have met
}
}
function drawCrab() {
fill("orange");
ellipse(100, height-80,80,50);
circle(60,height-100,20);
circle(140,height-100,20);
arc(60,height-130, 50, 50, -(radians(30)), -HALF_PI);
arc(150,height-130, 50, 50, -(radians(30)), -HALF_PI);
push();
noFill();
strokeWeight(10);
stroke("orange");
arc(120, height-35, 80, 80, PI + QUARTER_PI, TWO_PI);
arc(80, height-35, 80, 80, PI, PI + HALF_PI);
pop();
fill(0);
circle(120,height-90, 8);
circle(100,height-90, 8);
//if fish have met, crab claps by drawing circles on top of arcs
if (meet == true) {
arc(100, height-80, 20, 20, 0, -PI);
//alternating variable so crab claws open and close with every frame
if (crabClaw == 1) {
clap.play();
fill("orange");
circle(60, height-130,50,50);
circle(150,height-130,50,50);
crabClaw += 1;
} else if (crabClaw == 2) {
crabClaw = 1;
}
}
}
function drawHouse() {
fill(160,82,45); //brown
rect(width-200, height-170, 120,120);
triangle(width-220,height-170,width-140,height-250,width-60,height-170);
rect(width-200,height-230,20,80);
fill(173,216,230); //light blue
rect(width-160, height-110,40,60);
fill(0); //black
circle(width-155,height-70,5);
fill(173,216,230); //light blue
rect(width-180,height-150,20,20);
rect(width-120,height-150,20,20);
fill(224,255,255); //light cyan
bubbles.play();
//moving bubbles up the canvas by adding random dx and dy
for (i = 0; i < bubbleArrayX.length; i++) {
var randomDx = (random(-5,5));
var randomDy = (random(-10,0));
if (meet == false) { //if fish have not met, house bubbles
bubbleArrayX[i] += randomDx;
bubbleArrayY[i] += randomDy;
//reset x and y position if bubble leaves canvas
if (bubbleArrayY[i] <= 0) {
bubbleArrayX[i] = 400 + random(-10,10);
bubbleArrayY[i] = 150;
}
circle(bubbleArrayX[i], bubbleArrayY[i], 5)
} else if (meet == true) { //if fish have met, house bubbles hearts
heartArrayX[i] += randomDx;
heartArrayY[i] += randomDy;
var size = 10;
fill("pink")
beginShape();
vertex(heartArrayX[i], heartArrayY[i]);
bezierVertex(heartArrayX[i] - size / 2, heartArrayY[i] - size / 2, heartArrayX[i] - size, heartArrayY[i] + size / 3, heartArrayX[i], heartArrayY[i] + size);
bezierVertex(heartArrayX[i] + size, heartArrayY[i] + size / 3, heartArrayX[i] + size / 2, heartArrayY[i] - size / 2, heartArrayX[i], heartArrayY[i]);
endShape();
//reset x and y position if heart leaves canvas
if (heartArrayY[i] <= 0) {
heartArrayX[i] = 400 + random(-10,10);
heartArrayY[i] = 150;
}
}
}
}
//plays love song after fish meet
function playSong() {
//only want song to play once so use counter
if (songCount == 0) {
heart.play();
songCount += 1;
} else {
//do nothing
}
}
]]>For this project, I wanted to create an interactive portrait. I was inspired by Ryan Alexander’s work and wanted to have the portrait show up based on where you clicked your mouse. I wasn’t able to get that working fully, but I still think the final product is pretty cool as the way the portrait shows up changes based on where you click the mouse. I also added randomness to each pixel width and height to make the portrait more animated.
//Catherine Liu
//jianingl_andrew.cmu.edu
//Section D
//project_09
//image shows up in pixels and spreads out on canvas
var img;
var xStart; //stores mouseX
var yStart; //stores mouseY
var click = true; //checks for mousePressed and removes text
var radianSize = 0; //increases area of expansion
var clickCol = false; //checks color of background
function preload() {
img = loadImage("https://i.imgur.com/mzVFTDQ.jpg");
}
function setup() {
img.resize(img.width/2, img.height/2);
createCanvas(img.width, img.height);
frameRate(50);
print (img.width/2);
print(img.height/2)
}
function draw() {
//redraws background every mousePressed
if (clickCol) {
fill(203, 195, 227);
rect(0,0,width,height);
clickCol == false;
}
//sets up text that only shows up at beginning
if(click) {
background(203, 195, 227)
push();
fill(255);
textAlign(CENTER);
textSize(20)
text("Click anywhere to draw image",width/2, height/2)
pop();
}
pixelExpand();
}
function pixelExpand() {
for (var vert = yStart; vert < radianSize; vert += 3) {
for (var hor = xStart; hor < radianSize; hor += 3) {
noStroke();
//expands right downwards
var c = img.get(hor, vert);
fill(c);
ellipse(hor, vert, random(5), random(5));
//expands right upwards
var c2 = img.get(hor, height-vert);
fill(c2);
ellipse(hor, height-vert, random(5), random(5));
//expands left upwards
var c3 = img.get(width-hor, height-vert);
fill(c3);
ellipse(width-hor, height-vert, random(5), random(5));
//expands left downwards
var c4 = img.get(width-hor, vert);
fill(c4);
ellipse(width-hor, vert, random(5), random(5));
}
}
radianSize += 5; //increase expansion size
}
//returns starting point for pixels to show
function mousePressed() {
radianSize = 0;
clickCol = true;
click = false; //text disappears
xStart = constrain(mouseX, 0, width/4);
yStart = constrain(mouseY, 0, height/4);
}
]]>For this week’s Looking Outwards, I wanted to talk about Vera Molnar, who is regarded as a pioneer of computer generative art and is one of the first women artists to use computers in her practice. Particularly, I was drawn to her piece “(Dés-)Ordres”. What caught my eye initially were the contrasting colors used in different parts of the image, pulling my eye around. This contrast between order and disorder amongst the different layered squares also creates the impression of movement as if the squares are vibrating against one another. Surprisingly, this image was generated with a computer, where Molnar changed the parameters of her algorithm to randomly disrupt the regularity of the concentric squares.
Molnar was born in Hungary in 1924 and studied art history and aesthetics at the Budapest College of Fine Arts. From as early as 1959, she began experimenting with the concept of algorithms or “machine imaginaire” where an image can be created by following a set of pre-ordained compositional rules. In 1968, she begin using computers and plotters to make her paintings and drawings, creating a variety of algorithms iterating simple geometric shapes and geometrical themes.
Mike Tucker is an interactive director, designer, and developer who has been working at Magic Leap for the past 5 years. I was inspired by his spatial computing projects and even more so after his lecture at EYEO. I am personally very interested in the fields of AR and VR and how they can change the human experience. While he started his career with graphic design, he seems to be more interested in combining music with interactive digital environments. The project he presented at EYEO 2019: Tonandi, was especially interesting to me because of all the different senses incorporated into it. Including sight, sound, and motion, it is amazing how immersive digital environments can be designed nowadays. The lecture itself was effective in showing the actual experience of using the headset Magic Leap designed. Tucker presented videos showing live demos of the VR projects and how the user can interact with the virtual environment. The videos are also shot from a first-person perspective so the viewer can see the project as if they are using the VR headset themselves.
For this project, I was excited to explore different types of curves and how you can create new ones through simple interactions. I started off my process by looking at and trying different curves in Sublime to see which ones were the most interesting to change. I also spent some time plugging mouseX and mouseY into different parts of the equation to see how they changed. I found it interesting that curves can look completely different based on which variables were changed. For instance, I used a Cayley’s Sextic curve for one of my curves, which looks like a rounded heart. However, when you control the ratio multiplied by cosine, it changes the number of curves and their rotation so it looks like a flower.
Astroid curve in different phases:
Cayley’s Sextic in different phases:
//Catherine Liu
//jianingl@andrew.cmu.edu
//Section D
//jianingl_07_Project
var type = 1 //keeps track of current type of curve
//draws two different types of curves that changes with mousePressed
function setup() {
createCanvas(480, 480);
}
function draw() {
//checks for current curve
if (type == 1) {
drawSextic();
} else if (type == 2) {
drawAstroid();
}
}
function drawAstroid() {
//Astroid
//https://mathworld.wolfram.com/Astroid.html
//creates a circular form with curves around the circumference
var red = min(mouseX, 255);
var green = min(mouseY, 255);
fill(red, green, 0);
noStroke();
translate(width/2,height/2);
background(0);
beginShape();
var x ;
var y ;
var b = map(mouseY, 0, 480, 10, 20); //controls number of curves around circumference
var a = constrain(mouseX, 0, width/2); //controls size of circle curve
rotate(radians(mouseY/3));
for (i = 0; i < 480; i++) {
var theta = map(i,0,480, 0, TWO_PI);
x = (a-b)*cos(theta) + b*cos((a-b)/b*(theta));
y = (a-b)*sin(theta) - b*cos((a-b)/b*(theta));
vertex(x,y);
}
endShape();
}
function drawSextic() {
//Cayley's Sextic
//https://mathworld.wolfram.com/CayleysSextic.html
//creates a flower form with different numbers of petals
push();
var blue= min(mouseX, 255);
var red = min(mouseY, 255);
translate(width/2,height/2);
fill(red,0,blue);
noStroke();
background(0);
beginShape();
var x ;
var y ;
var b = map(mouseY, 0, 480, 0, 1); //controls rotation and number of petals
var a = map(mouseX, 0, width, 0, 150); //controls size of form
rotate(radians(mouseY/5));
for (i = 0; i < 480; i++) {
var theta = map(i, 0, 480, 0, PI * 3);
x = (3*a*pow(cos((1/b)*theta),3)*cos(theta));
y = (3*a*pow(cos((1/b)*theta),3)*sin(theta));
vertex(x,y);
}
endShape();
pop();
}
function mousePressed() {
//switches current curve when mouse is pressed
if (type == 1) {
type += 1
} else if (type == 2) {
type = 1
}
}
]]>A student in CMU Design called Bryce Li created a website in 2021 that helps visualize songs by a specific artist. If you like a song by that artist, the website can show you other songs by the same artist that are similar. This information is presented in clustered data points that are visualized as circles. Each circle represents a song and the closer 2 circles are together, the more similar those songs are. When you click on a circle, it also plays a 30-second clip of the song so you can hear it for yourself. This website was coded using libraries from React and Three js. And the coding languages are HTML, CSS, and JS. To cluster different points by shared similarities, Spotify has a quantifying API that Bryce used along with a k-means clustering algorithm. I thought this was interesting because I often come across a similar situation where I like an artist’s song but I don’t know how to find similar songs. By simplifying the amount of data a user sees and taking away unnecessary information, this website, therefore, communicates clearly what the user wants to know: similar types of songs by the same artist.
//Catherine Liu
//Section D
//jianingl@andrew.cmu.edu
//Assignment-06-Project
//an abstract clock where the objects move according to seconds, minutes, and hours
var xPos = []; //array for x position of clouds
var yPos = []; //array for y position of clouds
function setup() {
createCanvas(480, 400);
//creates random positions for 60 clouds
for (i = 0; i < 60; i++) {
xPos[i] = random(0,480);
yPos[i] = random(0,150)
}
}
function draw() {
background(155,212,255);
fill(0,0,102);
rect(0,height/2, width, height/2);
//draws a number of clouds according to the current second
for (i = 0; i <= second(); i++) {
clouds(xPos[i], yPos[i]);
}
push();
sunMoon(-150, 0); //calls function that draws the rotating sun and moon
pop();
//aliens move closer to each other according to minutes
print(minute().toString())
rightAlien(480 - 8 * minute(), height/2); //calls function that draws right alien
leftAlien(0 + 8 * minute(), height/2); //calls function that draws left alien
}
function rightAlien (x, y) { //draws right alien
stroke(0,102,51);
strokeWeight(5);
//arm waves up and down according to seconds using mod
if (second() % 2 == 0) {
line(x-40, y-60, x-13,y-30); //left arm
} else if (second() % 2 != 0) {
line(x-45, y-50, x-13,y-30); //left arm
}
line(x+13, y-30, x+30, y-15); //right arm
line(x, y-50, x, y-30); //neck
stroke(204, 255, 153);
line(x-10, y, x-10, height); //left leg
line(x+10, y, x+10, height); // right leg
stroke(0,102,51);
line(x-17, y-70, x-7, y-50); //antennae
line(x+17, y-70, x+7, y-50); //antennae
noStroke();
fill(204, 255, 153);
ellipse(x, y-50, 30,20); //head
rect(x-15, y-35, 30,120,10); //body
fill(0,102,51)
//eyes look back when the aliens pass each other halfway
if (minute() < 31) {
circle(x-10, y-51, 5); // left eye
circle(x-3, y-51, 5); // right eye
} else if (minute() >= 31) {
circle(x+10, y-51, 5); // left eye
circle(x+3, y-51, 5); // right eye
}
}
function leftAlien(x, y) { //draws left alien
stroke(0,102,51);
strokeWeight(5);
//arm waves up and down according to seconds using mod
if (second() % 2 != 0) {
line(x+40, y-60, x+13,y-30); //left arm
} else if (second() % 2 == 0) {
line(x+45, y-50, x+13,y-30); //left arm
}
line(x-13, y-30, x-30, y-15); //left arm
line(x, y-50, x, y-30); //neck
stroke(204, 255, 153);
line(x-10, y, x-10, height); //left leg
line(x+10, y, x+10, height); //right leg
stroke(0,102,51);
line(x-17, y-70, x-7, y-50); //left antennae
line(x+17, y-70, x+7, y-50); //right antennae
noStroke();
fill(204, 255, 153);
ellipse(x, y-50, 30,20); //left man's head
rect(x-15, y-35, 30,120,10); //left man's body
fill(0,102,51)
//eyes look back when the aliens pass each other halfway
if (minute() < 31) {
circle(x+10, y-51, 5); // left eye
circle(x+3, y-51, 5); // right eye
} else if (minute() >= 31) {
circle(x-10, y-51, 5); // left eye
circle(x-3, y-51, 5); // right eye
}
}
function sunMoon (x, y) { //sun and moon rotates according to hour, from 0-12 it is sun, from 12-23 it is moon
translate(width/2, height/2);
var dx = 30 * hour();
rotate (radians(dx));
if (hour() > 12 & hour() <=23) {
fill(255, 243, 176);
arc(x, y, 100, 100, 0, PI + QUARTER_PI, PIE); //moon
ellipse(x + 30, y - 30, 5, 10);
ellipse(x - 30, y + 70, 5, 10);
} else if (hour() >= 0 & hour() <= 12) {
fill(255, 128, 0);
ellipse(x, y, 100, 100); //sun
}
}
function clouds(x, y) { //draws a cloud at position x, y
fill(233, 254, 255);
rect(x, y, 20, 10,30); //cloud
}
I started off this project by sketching a rough idea of what I wanted to make, which was two people playing badminton, with the shuttlecock moving from one end to the next in minutes. The sun and moon would then rotate according to the hour.
However, I came across some coding problems so I changed my idea slightly. Now, they are aliens that can only move with every minute. As they move closer to each other, they are waving and their eyes follow each other. While I kept the sun and moon concept the same, I changed the background clouds to increase numbers according to seconds.
]]>For this week’s LO, I looked back on a game I used to play a lot as a kid: Minecraft created in 2011 by Mojang. It is interesting how despite its simple graphics, Minecraft encourages creativity and worldbuilding. This shows that games do not need the most sophisticated or detailed assets to still be fun and addicting. One of the main defining aspects of Minecraft is its terrains that are regenerated with every game and are virtually infinite, allowing the player to explore and build however they wish.
I was curious about how the terrains were generated, as they seemed to spawn randomly, sometimes even creating floating islands. Apparently, Minecraft terrains are generated based on Perline noise functions. When the game starts, it generates a Seed (a random 64-bit number) which is used to generate noise functions. So the game starts on a broad level by creating simple topographical maps, then goes into smaller random details like bushes, animals, lakes, etc. However, these random environments and objects still have a consistent logic and constraints they have to follow to make them believable terrains. This random generation allows for a different world each time, giving the player new ideas and possibilities to define their own gameplay.
//Catherine Liu
//jianingl
//Section D
var catList = [1, 2, 3]; //keeps track of cat for genration of wallpaper
function setup() {
createCanvas(600, 600);
background(230,230,250);
}
function draw() {
for (var y = 30; y <= 600; y += 100) {
for (var x = 50; x <= 550; x += 150) {
var cat = random(catList); //randomly generates a cat at that position
if (cat == 1) {
CatStand(x,y);
} else if (cat == 2) {
CatFront(x,y);
} else if (cat == 3) {
CatSit(x,y);
}
}
}
noLoop()
}
function CatStand (x, y) {
strokeWeight(2);
push();
noFill();
strokeWeight(5);
stroke(233,150,122);
arc(x + 60, y-20, 50, 50, 0, HALF_PI); //tail
pop();
stroke(139,69,19)
fill(233,150,122);
ellipse(x+60, y + 40, 5, 20); // back left leg
ellipse(x+10, y + 40, 5, 20); // left front leg
ellipse(x + 40, y + 20, 80, 50); //body
triangle(x + 15, y - 15, x + 30, y - 25, x + 25, y - 5); // right ear
triangle(x - 15, y - 15, x - 30, y - 25, x - 25, y - 5); // left ear
ellipse(x, y, 50, 40); //head
ellipse(x+70, y + 42, 5, 20); //back right leg
ellipse(x+18, y + 43, 5, 20); // right front leg
fill(100,107,47);
ellipse(x-15, y, 10, 5); //left eye
ellipse(x+5, y, 10, 5); //right eye
fill(205,92,92);
ellipse(x-7, y+5, 5, 3); //nose
stroke(0,100,0);
fill(50,205,50);
circle(x-20, y+50, 10);
push();
noFill();
arc(x -65, y+45, 50, 50, 0, HALF_PI);
pop();
circle(x-35, y +50, 15);
push();
noFill();
arc(x + 83, y-20, 50, 50, 0, HALF_PI);
pop();
circle(x+ 85, y+3, 8);
}
function CatFront (x, y) {
strokeWeight(2);
push();
noFill();
strokeWeight(5);
stroke(139,69,19);
arc(x + 45, y-20, 50, 50, 0, HALF_PI); //tail
pop();
stroke(139,69,19)
fill(255,218,185);
ellipse(x+10, y+10, 90, 70); //body
triangle(x + 15, y - 15, x + 30, y - 25, x + 25, y - 5); // right ear
triangle(x - 15, y - 15, x - 30, y - 25, x - 25, y - 5); // left ear
ellipse(x, y, 50, 40); //head
fill(119,136,153);
ellipse(x-15, y, 10, 5); //left eye
ellipse(x+5, y, 10, 5); //right eye
fill(244,164,96);
ellipse(x-7, y+5, 5, 3); //nose
fill(255,218,185);
ellipse(x + 35, y + 40, 20, 10); // right leg
ellipse(x - 10, y + 40, 20, 10); // left leg
stroke(70,130,180);
push();
noFill();
arc(x -60, y+45, 50, 50, 0, HALF_PI);
arc(x + 80, y+15, 50, 50, 0, HALF_PI)
pop();
fill(135,206,235);
circle(x-30, y+45, 10);
circle(x+80, y+45, 15);
}
function CatSit (x, y) {
strokeWeight(2);
push();
noFill();
strokeWeight(5);
stroke(119,136,153);
arc(x + 60, y-10, 50, 50, 0, HALF_PI); //tail
pop();
stroke(139,69,19);
fill(119,136,153);
ellipse(x+5, y+50, 10, 20); //front right leg
ellipse(x+30, y+30, 70, 60); //body
ellipse(x+45, y+50, 30, 20); //back leg
ellipse(x+15, y+50, 10, 20); //front left leg
triangle(x + 15, y - 15, x + 30, y - 25, x + 25, y - 5); // right ear
triangle(x - 15, y - 15, x - 30, y - 25, x - 25, y - 5); // left ear
ellipse(x, y, 50, 40); //head
fill(0,100,0)
ellipse(x, y, 10, 5); //left eye
ellipse(x-15, y, 10, 5); //right eye
fill(40,26,13)
ellipse(x-7, y+5, 5, 3); //nose
stroke(75,0,130);
fill(147,112,219);
circle(x+60, y-15, 15);
circle(x+75, y-15, 15);
ellipse(x+75, y-5, 10, 5);
ellipse(x+60, y-5, 10, 5);
push();
strokeWeight(3)
line(x+67, y-15,x+67, y-5);
pop();
push();
noFill();
arc(x -65, y+45, 50, 50, 0, HALF_PI);
pop();
circle(x-35, y +50, 15);
}
Before creating this wallpaper, one of my friends from home called me and showed me pictures of her cats. In the spur of the moment, I thought that it’d be cute to create a wallpaper full of cats. I also thought it would be cool if the wallpaper could generate different versions of itself. For this, I created 3 different cats and had the draw function choose a random cat to draw each time it ran through the 2 for loops. For each cat, I drew up a quick sketch of what I wanted each cat to look like and added some decorative yarn and butterflies to make them look more like wallpaper: