Final Project

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.

sketch

//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);
}










Project 10: Sonic Story

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.

sketchDownload

//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
    }
}





Project 09: Computational Portrait

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.

Final portrait
beginning screen
image showing up

sketchDownload

//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);
}

LO: 09

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.

Vera Molnar explaining her work

LO 08

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.

Tonandi project

Project: Composition with Curves

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:

sketch

//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
    }
}

LO: Computational Data Visualization

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.

Bryce’s Vimeo video describing his project

Project-06: Abstract Clock

sketchDownload

//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.

original sketch

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.

LO-06: Randomness

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.

floating islands in Minecraft

Project 05: Wallpaper

sketch

//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:

quick sketches of different cat positions