## Final Project: Iceberg

sketch
``````
// ICE VAR
var terraine = [];          // array for ice terraine y coordinates
var noiseParam = 0;         // x value of noise func
var noiseStep = .02;        // how much x increases by
var icesize = noiseStep*2500;

// OBJECT VAR
var washer;
var handwasher;
var bubble;
var bubs = [];
var waterbottle;
var reusebottle;
var car;
var bus;
var shoponline;
var shoplocal;

var allobj = [];            // holds all objects once they are created
var len;                    // length of allobj array
var n = 0;                  // initial spot in allobj array
var q;
var isMouseClicked = -1; // boolean for click

// AUDIO VAR
var waterdrop;
var watersplash;

// OBJECT FUNCTIONS
function makeWasher(cx, cy) {
washer = { x: cx,
y: cy,
show: drawWasher,
melt: 4,
txt: "Use electric washer" }
return washer;
}

function makeHandwasher(cx, cy) {
for (var i=0; i<30; i++) {
bubx = random(-35, 35);
buby = -random(-.5, 3)*i;
var bub = makeBubble(bubx, buby);
bubs.push(bub);
}
handwasher = { x: cx,
y: cy,
show: drawHandwasher,
melt: 1,
txt: "Hand-wash clothing"  }
return handwasher;
}

function makeBubble(bubx, buby) {;
bubble = {x: bubx,
y: buby,
sz: random(2, 6),
show: drawBubble }
return bubble;
}

function makeWaterbottle(cx, cy) {
waterbottle = {x: cx,
y: cy,
show: drawWaterbottle,
melt: 3,
txt: "Plastic water bottle"}
return waterbottle;
}

function makeReusebottle(cx, cy) {
reusebottle = {x: cx,
y: cy,
show: drawReusebottle,
melt: 1,
txt: "Reusable water bottle"}
return reusebottle;
}

function makeCar(cx, cy) {
car = {x: cx,
y: cy,
show: drawCar,
melt: 5,
txt: "Drive a car"}
return car;
}

function makeBus(cx, cy) {
bus = {x: cx,
y: cy,
show: drawBus,
melt: 1,
txt: "Take public transportation"}
return bus;
}

function makeOnline(cx, cy) {
shoponline = {x: cx,
y: cy,
show: drawOnline,
melt: 10,
txt: "Shop online"}
return shoponline;
}

function makeLocal(cx, cy) {
shoplocal = {x: cx,
y: cy,
show: drawLocal,
melt: 1,
txt: "Shop local"}
return shoplocal;
}

}

// SETUP
function setup() {
createCanvas(400, 400);
strokeWeight(2);
rectMode(CENTER);
textAlign(CENTER);
textFont('Georgia');
textSize(13);
useSound();

// fill terraine array w coordinates for ice:
for (var i=0; i<icesize; i++) {
var n = noise(noiseParam);      // n is between 0 and 1
var value = map(n, 0, 1, height/3, height);     // draws onto canvas nicely
terraine.push(value);           // value is the y coordinate at x=i
noiseParam += noiseStep;
}

// create objects:
washer = makeWasher(125, 100);
handwasher = makeHandwasher(275, 100);
waterbottle = makeWaterbottle(265, 100);
reusebottle = makeReusebottle(135, 100);
car = makeCar(125, 100);
bus = makeBus(275, 100);
shoponline = makeOnline(290, 100);
shoplocal = makeLocal(115, 100);

allobj = [washer, handwasher,
reusebottle, waterbottle,
car, bus,
shoplocal, shoponline];
len = allobj.length;
}

// SOUND SETUP
function soundSetup() { // setup for audio file
waterdrop.setVolume(0.5);
watersplash.setVolume(0.5);
}

// DRAW
function draw() {

// blue sky:
background(100, 175, 240);

// ice:
drawIce();

// it melts:
meltIce();

// draw objects:
if (terraine.length > 1) {
q = n%(len);        // loops through array by 2s
allobj[q].show();
allobj[q+1].show();
}
}

// WHEN USER CLICKS
function mousePressed() {
let onObj = checkClick();
if (onObj == true) {
isMouseClicked = -isMouseClicked;
n += 2;                 // loops through array by 2s
isMouseClicked = -isMouseClicked;
}

}

function checkClick() {
if (mouseX>75 & mouseX<175 && mouseY>50 && mouseY<150) {
meltMore(allobj[q]);
return true;
}
if (mouseX>225 & mouseX<325 && mouseY>50 && mouseY<150) {
meltMore(allobj[q+1]);
return true;
}
}

// DRAW ICE
function drawIce() {
fill(255);
stroke(0);
strokeWeight(1);
beginShape();
vertex(-5, height);
vertex(-5, terraine[0]);
for (var j=0; j<terraine.length-1; j++) {
vertex(j*5, terraine[j]);
}
vertex((terraine.length-1)*icesize, terraine[terraine.length]);
vertex((terraine.length-1)*5, height);
endShape();

// check if completely melted:
checkIce();
}

// MELT ICE constantly
function meltIce() {
// when ice is melted, 'game' over
if (terraine.length == 1) { return }
// as ice melts, water drop audio is played
if (frameCount%250==0) {
terraine.pop();
waterdrop.play();
}
}

// MELT MORE! when environmentally more harmful choice is picked
function meltMore(obj) {
for (var k=0; k<obj.melt; k++) {
terraine.pop();
}
if (obj.melt == 1) { waterdrop.play() }
else { watersplash.play() }
}

// CHECK IF COMPLETELY MELTED
function checkIce() {
if (terraine.length <= 1) {
rect(width/2, height/2, 350, 350);
textAlign(CENTER);
textSize(35);
fill(0);
text("Oh No!", width/2, 150);
textSize(16);
fill(150, 0, 0);
text("Looks like your choices caused global warming.", width/2, 175);
textSize(20);
fill(0);
text("Next time, try to make better choices.", width/2, 215);
textSize(13);
text("You were already trying to make good choices?", width/2, 300);
text("You picked the best options that were avaiable to you?", width/2, 325);
textSize(18);
fill(150, 0, 0);
text("Have better options next time I guess.", width/2, 350);
textSize(11);
noStroke();
fill(255);
noLoop();
}
}

// DRAW OBJECTS:
// washer
function drawWasher() {
push();
translate(this.x, this.y);

fill(220);
square(0, 0, 100, 5);

fill(150);
circle(0, 0, 75);

// water in washer:
noStroke();
fill(75, 125, 225);
beginShape();
vertex(-30, 0);
vertex(-30, 0);
curveVertex(-24, 2);
curveVertex(-18, 0);
curveVertex(-12, 2);
curveVertex(-6, 0);
curveVertex(0, 2);
curveVertex(6, 0);
curveVertex(12, 2);
curveVertex(18, 0);
curveVertex(24, 2);
vertex(30, 0);
curveVertex(26, 16);
curveVertex(15, 26);
curveVertex(0, 30);
curveVertex(-15, 26);
curveVertex(-26, 16);
vertex(-30, 0);
vertex(-30, 0);
endShape();

// door partially see-through:
stroke(0);
fill(175, 200, 255, 175);
circle(0, 0, 60);

// glare on glass door
noStroke();
push();
fill(200, 215, 255);
ellipse(0, -15, 30, 5);
ellipse(0, -20, 15, 3);
pop();

// bottons + stuff

fill(200, 0, 0);
circle(40, -40, 3);
fill(75);
circle(35, -40, 3);
circle(30, -40, 3);
fill(150);
circle(35, -33, 7);

// text
fill(0);
text(this.txt, 0, 62);

pop();
}

// handwash basin
function drawHandwasher() {
push();
translate(this.x, this.y);

// basin
fill(200);
quad(-50, 0, 50, 0, 35, 40, -35, 40);
arc(0, 0, 100, 25, 0, PI);
arc(0, 0, 100, 15, PI, 2*PI);

// clothes
noStroke();
fill(58, 101, 74);
quad(-30, -7, -15, -8, -15, 10, -32, 8);
fill(200, 100, 150);
quad(-20, -8, -7, -9, -5, 10, -25, 10);

// water
fill(100, 140, 240);
beginShape();
vertex(-45, 6);
vertex(-45, 5);
curveVertex(-36, 5);
curveVertex(-27, 6);
curveVertex(-18, 5);
curveVertex(-9, 6);
curveVertex(0, 5);
curveVertex(9, 6);
curveVertex(18, 5);
curveVertex(27, 6);
curveVertex(36, 5);
vertex(45, 5);
curveVertex(29, 10);
curveVertex(0, 12);
curveVertex(-29, 10);
vertex(-45, 5);
vertex(-45, 6);
endShape();

// suds + bubbles
fill(200, 215, 255);
stroke(100, 140, 240);
strokeWeight(.25);
for (var i=0; i<30; i++) {
bubs[i].show()
}

// text
fill(0);
strokeWeight(1);
text(this.txt, 0, 62);
pop();
}

// bubble
function drawBubble() {
circle(this.x, this.y, this.sz);
}

// plastic bottle
function drawWaterbottle() {
// cap
stroke(0);
strokeWeight(1);
fill(240);
rect(this.x, this.y-45, 15, 10);

// body of bottle
fill(225, 230, 255);
beginShape();
vertex(this.x+21, this.y-15);
vertex(this.x+21, this.y-15);
curveVertex(this.x+20, this.y+6);
curveVertex(this.x+23, this.y+30);
curveVertex(this.x+19, this.y+50);
curveVertex(this.x, this.y+50);
curveVertex(this.x-19, this.y+50);
curveVertex(this.x-23, this.y+30);
curveVertex(this.x-20, this.y+6);
vertex(this.x-21, this.y-15);
vertex(this.x-21, this.y-15);
endShape();

// lines on cap
stroke(190);
for (var l=0; l<14; l+=2) {
line(this.x-6 + l, this.y-41, this.x-6 + l, this.y - 47);
}

// wrapper
fill(75, 125, 200);
noStroke();
beginShape();
vertex(this.x+21, this.y-15);
vertex(this.x+21, this.y-15);
vertex(this.x+20, this.y-2);
vertex(this.x+20, this.y+8);
vertex(this.x+22, this.y+20);
vertex(this.x-22, this.y+20);
vertex(this.x-20, this.y+8);
vertex(this.x-20, this.y-2);
vertex(this.x-21, this.y-15);
vertex(this.x-21, this.y-15);
endShape();

// details
stroke(190, 210, 250);
noFill();

//wrapper details
strokeWeight(2);
stroke(90, 145, 220);
beginShape();
vertex(this.x+15, this.y+10);
vertex(this.x+15, this.y+10);
curveVertex(this.x+10, this.y);
curveVertex(this.x-8, this.y-5);
vertex(this.x-15, this.y-10);
vertex(this.x-15, this.y-10);
endShape();
beginShape();
vertex(this.x+15, this.y+10);
vertex(this.x+15, this.y+10);
curveVertex(this.x+8, this.y+5);
curveVertex(this.x-10, this.y);
vertex(this.x-15, this.y-10);
vertex(this.x-15, this.y-10);
endShape();

// text
fill(0);
strokeWeight(1);
text(this.txt, this.x, this.y+62);
}

// reusable bottle
function drawReusebottle() {
stroke(0);
strokeWeight(1);

// water
fill(100, 155, 230);
stroke(90, 145, 220);
beginShape();
vertex(this.x-23, this.y-18);
vertex(this.x-23, this.y-18);
curveVertex(this.x-17, this.y-19);
curveVertex(this.x-10, this.y-18);
curveVertex(this.x, this.y-19);
curveVertex(this.x+10, this.y-18);
curveVertex(this.x+17, this.y-19);
vertex(this.x+23, this.y-18);
vertex(this.x+23, this.y-18);
endShape();
noStroke();
beginShape();
vertex(this.x+23, this.y-18);
vertex(this.x+23, this.y-18);
curveVertex(this.x+24, this.y+30);
curveVertex(this.x+19, this.y+50);
curveVertex(this.x-19, this.y+50);
curveVertex(this.x-24, this.y+30);
vertex(this.x-23, this.y-18);
vertex(this.x-23, this.y-18);
endShape();

// body of bottle
noStroke();
fill(200, 100, 150, 100);
noFill();
stroke(0);
fill(200, 100, 150, 100);
beginShape();
vertex(this.x+23, this.y-20);
vertex(this.x+23, this.y-20);
curveVertex(this.x+24, this.y+30);
curveVertex(this.x+19, this.y+50);
curveVertex(this.x-19, this.y+50);
curveVertex(this.x-24, this.y+30);
vertex(this.x-23, this.y-20);
vertex(this.x-23, this.y-20);
endShape();

// lines on body of bottle
stroke(190, 90, 140, 200);
for (var u=0; u<12; u+=1) {
line(this.x-15, this.y-15+(u*5), this.x-22, this.y-15+(u*5));
}

// stickers!
// smiley face
stroke(0);
strokeWeight(.5);
fill(250, 225, 0);
push();
translate(this.x+16, this.y+20);
strokeWeight(1);
line(-2, 0, -2, -2);
line(2, 0, 2, -2);
pop();

// flower
push();
fill(225, 125, 255);
translate(this.x+9, this.y+36);
let rot = 0;
for (var r=0; r<7; r++) {
push();
ellipse(6, 0, 8, 4);
pop();
rot += 360/7;
}
fill(175, 200, 100);
circle(0, 0, 5);
pop();

// cap
noFill();
strokeWeight(2);
stroke(200);
stroke(0)
strokeWeight(1);
fill(200);
rect(this.x, this.y-52, 15, 5, 2);
rect(this.x, this.y-45, 25, 15, 2);
stroke(225);
strokeWeight(6);
line(this.x, this.y-40, this.x, this.y-45);
line(this.x-8, this.y-40, this.x-8, this.y-45);
line(this.x+8, this.y-40, this.x+8, this.y-45);
stroke(0);
strokeWeight(1);
rect(this.x, this.y-38, 28, 2, 3);

// text
fill(0);
text(this.txt, this.x, this.y+62);
}

// car
function drawCar() {
let carcol = color(150, 0, 0);
let detailcol = color(100, 0, 0);

// exhaust pipe
fill(120);
noStroke();
rect(this.x+60, this.y+27, 5, 3);

// body of car
stroke(detailcol);
fill(carcol);
beginShape();
vertex(this.x-40, this.y);
vertex(this.x-40, this.y+1);
curveVertex(this.x-20, this.y-22);
curveVertex(this.x+25, this.y-22);
curveVertex(this.x+48, this.y);
curveVertex(this.x+57, this.y+3);
curveVertex(this.x+58, this.y+10);
curveVertex(this.x+59, this.y+25);
curveVertex(this.x+45, this.y+30);
curveVertex(this.x-60, this.y+30);
curveVertex(this.x-65, this.y+8);
vertex(this.x-40, this.y+1);
vertex(this.x-40, this.y+1);
endShape();

// windows
stroke(150, 160, 200);
fill(150, 160, 200);
beginShape();
vertex(this.x-35, this.y+1);
vertex(this.x-35, this.y+1);
curveVertex(this.x-15, this.y-19);
curveVertex(this.x+20, this.y-19);
curveVertex(this.x+40, this.y-1);
curveVertex(this.x+30, this.y+1);
vertex(this.x-35, this.y+1);
vertex(this.x-35, this.y+1);
endShape();
fill(carcol);
stroke(carcol);
strokeWeight(4);
line(this.x+2, this.y+2, this.x+2, this.y-22);
strokeWeight(2);
line(this.x+33, this.y+2, this.x+33, this.y-16);

// lights
noStroke();
fill(230, 230, 175);
push();
translate(this.x-62, this.y+9);
ellipse(0, 0, 10, 7);
ellipse(-7, 1, 3, 3);
pop();
rect(this.x+57, this.y+21, 5, 7, 2);
rect(this.x+57, this.y+15, 2, 4, 2);

// handles
stroke(detailcol);
line(this.x-5, this.y+7, this.x, this.y+7);
line(this.x+4, this.y+7, this.x+9, this.y+7);

// wheels
fill(25);
stroke(0);
ellipse(this.x+35, this.y+30, 25, 25);
ellipse(this.x-40, this.y+30, 25, 25);
let wrot = 0;
for (var j=0; j<5; j++) {
push();
translate(this.x+35, this.y+30);
line(-10, 0, 10, 0);
pop();
push();
translate(this.x-40, this.y+30);
line(-10, 0, 10, 0);
pop();
wrot+=360/10;
}

// text
fill(0);
strokeWeight(1);
text(this.txt, this.x, this.y+62);
}

// bus
function drawBus() {
let buscol = color(50, 75, 255);
let busdetails = color(20, 40, 200);

// exhaust pipe
fill(120);
noStroke();
rect(this.x+50, this.y-27, 3, 5);

// bus body
fill(buscol);
stroke(busdetails);
strokeWeight(2);
rect(this.x, this.y, 120, 50, 2);

//details
line(this.x-45, this.y+8, this.x+55, this.y+8);
strokeWeight(1);
line(this.x-45, this.y+10, this.x+55, this.y+10);

// windows
stroke(150, 160, 200);
strokeWeight(2);
fill(150, 160, 200);
rect(this.x-57, this.y-5, 5, 25);
for (var r=0; r<10; r++) {
rect(this.x-41+(10*r), this.y-3, 7, 15);
}

// wheels
fill(25);
stroke(0);
ellipse(this.x+38, this.y+30, 15, 15);
ellipse(this.x+22, this.y+30, 15, 15);
ellipse(this.x-45, this.y+30, 15, 15);
ellipse(this.x-29, this.y+30, 15, 15);
strokeWeight(1);
let wrot = 0;
for (var j=0; j<5; j++) {
push();
translate(this.x+38, this.y+30);
line(-7, 0, 7, 0);
pop();
push();
translate(this.x+22, this.y+30);
line(-7, 0, 7, 0);
pop();
push();
translate(this.x-45, this.y+30);
line(-7, 0, 7, 0);
pop();
push();
translate(this.x-29, this.y+30);
line(-7, 0, 7, 0);
pop();
wrot+=360/10;
}

// lights
noStroke();
fill(230, 230, 175);
ellipse(this.x-58, this.y+15, 7, 10);
rect(this.x-59, this.y+15, 5, 10, 3);
rect(this.x+57, this.y+21, 5, 7, 2);

// text
fill(0);
strokeWeight(1);
text(this.txt, this.x, this.y+62);
}

// plane
function drawOnline() {
fill(200);
stroke(0);

push();
translate(this.x, this.y)
//back wing
push();
translate(-25, 13);
circle(-6, 0, 14);
pop();
//small tail wing back
push();
//body
noStroke();
ellipse(-60, -3, 10, 14);
stroke(0);
line(-65, -1, -65, -6);
// windows
strokeWeight(.5);
fill(150, 160, 200);
quad(-61, -6, -65, -1, -40, -1, -40, -6);
for (var r=0; r<10; r++) {
ellipse((10*r)-30, -7, 6, 7);
}
pop();
//back fin thingy
stroke(0);
fill(200);
push();
translate(52, 13);
beginShape();
vertex(0, 0);
vertex(0, 0);
curveVertex(15, -3);
vertex(30, -15);
vertex(30, 16);
vertex(30, 16);
endShape();
pop();
//small tail wing front
//front wing
push();
translate(25, 13);
circle(-10, 0, 14);
pop();

// text
fill(0);
strokeWeight(1);
text(this.txt, 0, 62);

pop();
}

// store
function drawLocal() {
square(this.x, this.y, 100);
noFill();
push();
translate(this.x, this.y);

// building
fill(150, 170, 150);
stroke(0);
rect(0, 15, 100, 70);
stroke(125, 145, 125);
for(var l=0; l<11; l++) {
line(-49, -15+(l*6), 49, -15+(l*6));
}

//roof
stroke(0);
fill(150, 100, 50);
quad(-60, -20, 60, -20, 50, -50, -50, -50);

//door
stroke(15, 75, 15);
fill(15, 75, 15);
rect(0, 37, 16, 25);
stroke(0);
line(7, 30, 2, 30);

//windows
strokeWeight(.5);
fill(150, 160, 200);
rect(-30, 15, 30, 40);
rect(30, 15, 30, 40);
for (var m=0; m<3; m++) {
line(-38+(m*8), 35, -38+(m*8), -5);
line(38-(m*8), 35, 38-(m*8), -5);
line(-45, 5+(m*10), -15, 5+(m*10));
line(45, 5+(m*10), 15, 5+(m*10));
}

//signs in windows
fill(180);
line(15, 15, 30, 25);
line(45, 15, 30, 25);
rect(30, 25, 15, 8);
line(-15, 15, -30, 25);
line(-45, 15, -30, 25);
rect(-30, 25, 15, 8);
noStroke()
fill(150, 0, 0);
textAlign(CENTER);
textSize(4);
text('OPEN!', -30, 26);
fill(0);
textSize(3);
text('Hours:', 28, 25);
stroke(0);
line(26, 26, 35, 26);
line(26, 27, 35, 27);
fill(100);

// text
fill(0);
textSize(13);
strokeWeight(1);
text(this.txt, 0, 62);

pop();
}

``````

My program depicts an iceberg that is melting, and presents the player with common choices that people have to make on a regular basis. The canvas reacts to the decisions that the player makes. The choices all have varying environmental impacts, and they correspond to the melting of the iceberg. If the player picks the environmentally more harmful choice, more of the ice falls into the ocean. If the player chooses an environmentally friendly option, the ice continues to melt at the same rate. This interactive program is not a winnable game. The ice will always melt. As it melts, a splash sound is played. **

I chose this design as my project because I wanted to highlight the way that industries pressure individuals to make good personal choices while producing products that only cause more damage. Individual choices will not stop climate change. To help preserve our planet, we need to rethink the entire system of the global enocomy.

The program must use a local server to run, and two sound files should be included in the folder to make the splashing noises when the ice melts. There are instructions included on the opening page.

I am really happy with how this project turned out. I only have a few choice that are presented to the player, but my goal was to make at least four sets of options, and that is what I have done. I screen recorded a majority of my process, and I have included a time-lapsed video at the bottom of this post.

** for the WordPress upload, my splash sound file was not working so I used a substitute file with a similar sound.

## Project 11: Generative Landscape

sketch
``````// this program displays a landscape of a stone path along a stream in the woods.

// empty arrays for objects:
var bkgrndtrees = [];
var path = [];
var stream = [];
var trees = [];
var allArrays = [bkgrndtrees, path, stream, trees];

// speed of landscape shift, per frame
// adjusted for background+foreground so 'closer' objects move faster than 'far away' ones
var shift = -1;

function setup() {
createCanvas(480, 300);
background(80, 125, 60);
frameRate(200);

// create initial collections of objects:
fillBkgrndtrees();
fillPath();
fillStream();
fillTrees();

// sort objects in arrays to be drawn properly:
for (a=0; a<allArrays.length; a++) {
if (allArrays[a]!=stream){          // stream should not be ordered
orderObjects(allArrays[a]);
}
}
}

// fills backgroundTrees array with initial trees:
function fillBkgrndtrees() {
for (var i = 0; i < 320; i++) {
var rx = random(width+75);
var ry = random(-height/25, 2*height/5);      // top of canvas = background
bkgrndtrees[i] = makeTree(rx, ry);
}
}

// fills foreground tree array with initial trees:
function fillTrees() {
for (var i = 0; i < 5; i++) {
var rx = random(width+75);
var ry = random(4*height/5, 3*height/2);    // bottom of canvas = foreground
trees[i] = makeTree(rx, ry);
}
}

// fills path array with initial stones:
function fillPath() {
for (var i = 0; i < 15; i++) {
var size = random(width/25, width/10);
var col = color(random(90, 120));
var x = i*50;
var ry = random(height/2, 3*height/5);
path[i] = makeStone(x, ry, size, col);
}
}

// fills stream array with objects based on stone path:
function fillStream() {
for (var i = 0; i < path.length; i++) {
var x = i*50;
var y = path[i].y + path[i].size;
stream[i] = makePoint(x, y);
}
}

// creates a tree object
function makeTree(tx, ty) {
// background trees:
if (ty < height/2) {
var tree = {x:tx,
y:ty,
age:random(2, 11),
trunkc: color(random(70, 90), random(55, 75), random(30, 55)),
leavesc: color(random(0, 75), random(65, 175), random(50)),
show: showTree,
move: moveObject,
speed: shift*.85 }
}
// foreground trees:
else {
var tree = {x:tx,
y:ty,
age:random(4, 7),
trunkc: color(random(50, 80), random(20, 50), random(10, 20)),
leavesc: color(random(0, 100), random(100, 200), random(30)),
show: showTree,
move: moveObject,
speed: shift*1.15 }
}

return tree;
}

// creates a stone object
function makeStone(sx, sy, ssize, scol) {
var stone = {x:sx,
y:sy,
size:ssize,
c: scol,
show: showStone,
move: moveObject,
speed: shift}
return stone;
}

// creates a point object (for stream curve);
function makePoint(px, py) {
var pnt = {x:px,
y:py,
size:width/50,
c: color(50, 125, 175),
show: showStone,
move: moveObject,
speed: shift}
return pnt;
}

function draw() {
background(80, 125, 60);

// draw all objects in all arrays:
for (j=0; j<allArrays.length; j++) {
thisArray = allArrays[j];
drawObjects(thisArray);
if (thisArray!=stream) {        // stream is updated with path
orderObjects(thisArray);
updateArray(thisArray);
}
}
}

// draws and moves objects in an array
function drawObjects(array) {
for (i=0; i<array.length; i++) {
thisObj = array[i];
thisObj.show();
thisObj.move();
}
if (array==stream) {
drawStream();
}
}

// uses curve shape and array points ot draw stream
function drawStream() {
push();
noFill();
stroke(50, 125, 175);
strokeWeight(15);
curveTightness(-.2);
beginShape();
curveVertex(stream[0].x, stream[0].y);
curveVertex(stream[0].x, stream[0].y);
for (var m=1; m<stream.length-1; m++){
curveVertex(stream[m].x, stream[m].y);
}
curveVertex(stream[stream.length-1].x, stream[stream.length-1].y);
endShape();
pop();
}

// sorts objects in an array according to the y feild
// this ordering ensures accurate depth on canvas
function orderObjects(array) {
for (var j=0; j<array.length-1; j++) {
var counter = 0;
for (var i=0; i<array.length-1; i++) {
if (array[i].y > array[i+1].y) {
var tmp = array[i];
array[i] = array[i+1];
array[i+1] = tmp;
counter += 1;
}
}
if (counter==0) {
break;
}
}
}

// adds new objects to end and deletes unused objects (off canvas)
function updateArray(array) {
if (array==trees || array==bkgrndtrees) {
// add new trees to array off-canvas:
// background trees:
var treeLikelihoodB = 320/555;
if (random(1) < treeLikelihoodB) {
var ry = random(-height/25, 2*height/5);    // top of canvas = background
bkgrndtrees.push(makeTree(width+75, ry));
}
// foreground trees:
var treeLikelihoodF = 5/555;
if (random(1) < treeLikelihoodF) {
var ry = random(3*height/4, 3*height/2);      // bottom of canvas = foreground
trees.push(makeTree(width+75, ry));
}
}
else {
// path and stream use fixed x values:
if (frameCount%50==0) {
var size = random(width/25, width/10);
var col = color(random(90, 120));
var ry = random(height/2, 3*height/5);
path.push(makeStone(700, ry, size, col));
col = color(0, 50, 150);
var y = ry + size;
stream.push(makePoint(700, y));
}
}

// remove any object no longer on canvas:
var keep = [];
for (var k=0; k<array.length; k++) {
if (array[k].x > 0) {
keep.push(array[k]);
}
}
array = keep;
}

// draws tree based on age
function showTree() {
if (this.y < height/2) {     // top of canvas = background
var h = this.age*10;
var w = this.age*2;
}
else {                      // bottom of canvas = foreground
var h = this.age*12;
var w = this.age*2.5;
}
var cx = this.x + w/2;
var cy = this.y - h*1.5;

if (this.age > 5) {             // older trees
stroke(50, 40, 30);
fill(this.trunkc);
rect(this.x, this.y-h, w, h);
fill(this.leavesc);
for (var i=0; i<3; i++) {
stroke(0, 75, 0);
circle(cx - (this.age*4*i) + (this.age*4),
cy - (this.age*1.5) + (this.age*3*i),
this.age*5);
circle(cx + (this.age*4*i) - (this.age*4),
cy - (this.age*1.5) + (this.age*3*i),
this.age*5);
ellipse(cx - (this.age*6*i) + (this.age*6),
cy + this.age,
this.age*5,
this.age*6);
ellipse(cx,
cy - (this.age*3) + (this.age*4*i),
this.age*7,
this.age*5);
}
noStroke();
ellipse(cx, cy + this.age, this.age*12, this.age*9);
}
else {                          // younger trees
stroke(50, 40, 30);
fill(this.trunkc);   // tree trunk lighter brown
rect(this.x, this.y-h, w/2, h);
fill(this.leavesc);
for (var i=0; i<3; i++) {
stroke(0, 75, 0);
circle(cx - (this.age*3*i) + (this.age*3),
cy - (this.age*1.5) + (this.age*2*i),
this.age*4);
circle(cx + (this.age*3*i) - (this.age*3),
cy - (this.age*1.5) + (this.age*2*i),
this.age*4);
ellipse(cx - (this.age*4*i) + (this.age*4),
cy + this.age,
this.age*4,
this.age*5);
ellipse(cx,
cy - (this.age*4) + (this.age*4*i),
this.age*6,
this.age*4);
}
noStroke();
ellipse(cx, cy + this.age/2, this.age*11, this.age*9);
}
}

// draws stone for path
function showStone() {
stroke(75);
fill(this.c);
ellipse(this.x, this.y, this.size, this.size/2);
}

// updates x values to move the landscape along
function moveObject() {
this.x += this.speed;
}
``````

For this project, I was inspired by a trail I used to walk a lot growing up. I struggled a bit to order the objects in a way that looks nice and is recognizable as a landscape, but I think the orderObjects() function that I created helped a lot with that. I also found it difficult to create the stream using a curve shape, but I am again really pleased with how well it came out. I wanted the path and stream to flow along together, and they do. To give more of a landscape effect, I adjusted the shift values for moving the objects so that things in the foreground move faster than things in the background.

## Looking Outwards 11: Societal Impacts of Digital Art

The reading I chose for this blog post is ‘Women in Media Arts: Does AI think like a (white) man?’ The article focuses on female digital artists and feminism within the digital art realm. Through looking at a number of female-created art pieces and projects, the article discusses the ways in which AI and other creative practices shed light on the biases within creators and consumers. One project by Mary Flanagan entitled [“help me know the truth”] explores the way people reinforce their own biases based on others’ physical appearance. Other projects in this article were created with the purpose of combatting the obvious marginalization and discrimination that result from the lack of diversity among those creating digital tools. One example of this is facial recognition software which generally does not accurately recognize people of color. This is just one of the many ways human bias influences artificial intelligence, and there are very real and dangerous consequences that can arise if these types of creative habits are not broken (ex: medical equipment that is only accurate for white male patients).

## 4. Project 10: Sonic Story

sketch – Copy
``````// This program displays a game of cup pong.
// The 'characters' are the ball, cups, table, and water in the cups.
// When the ball bounces on different surfaces (on the floor, into a cup, etc.),
// it produces different sounds.
// One side wins when all of the cups are empty
//(or just a few if you change the 'win' variable in the draw function).

// sounds:
var watercup;
var emptycup;
var bouncefloor;
var throwing;
var gameover;

// objects:
var cup; // will have fields for cx, cy, and boolean isFull
var ball; // will have fields for x, y, dx, dy, and functions

// arrays:
var leftCups = [];
var rightCups = [];

}

// makes a cup object, always full to begin
function makeCup(cx, cy) {
cup = {x:cx, y:cy, isFull: true, drawc: drawCup};
return(cup);
}

// makes a ball object
function makeBall(cx, cy, dirx, diry) {
ball = {x:cx, y:cy,
dx:dirx, dy:diry,
drawb: drawBall,
throwb: throwBall,
resetb: resetBall,
leftright: true};
return(ball);
}

function setup() {
createCanvas(480, 300);
frameRate(35);	// high framerate is better for this program
useSound();
rectMode(CENTER);

// fill cup arrays:
// set x and y positions to create pyramid shape.
// the if statements create the rows for the pyramid
for (var i=0; i<10; i++) {
if (i<4) {
lcup = makeCup(width/10+10,	(i*20)+120);
rcup = makeCup(9*width/10-10, (i*20)+120);
}
else if (i<7) {
lcup = makeCup(width/10+30,	(i*20)+50);
rcup = makeCup(9*width/10-30, (i*20)+50);
}
else if (i<9) {
lcup = makeCup(width/10+50,	i*20);
rcup = makeCup(9*width/10-50, i*20);
}
else {
lcup = makeCup(width/10+70,	height/2);
rcup = makeCup(9*width/10-70, height/2);
}
leftCups.push(lcup);
rightCups.push(rcup);
}

// create ping pong ball
let dy = random(-4, 4);
ball = makeBall(5, height/2, 5, dy);
}

function soundSetup() { // setup for audio generation
watercup.setVolume(0.5);
bouncefloor.setVolume(0.5);
throwing.setVolume(0.5);
gameover.setVolume(0.5);
}

function draw() {
background(133, 94, 66);

// draw the table
drawTable();

// draw the cups
for (var c=0; c<leftCups.length; c++) {
leftCups[c].drawc();
rightCups[c].drawc();
}

// draw the ball
ball.drawb();

// throw the ball
ball.throwb();

// check for winner
let checkl = 0;
let checkr = 0;
for (l=0; l<leftCups.length; l++) {
if (leftCups[l].isFull == false) { checkl+=1 }
}
for (r=0; r<rightCups.length; r++) {
if (rightCups[r].isFull == false) { checkr+=1 }
}
// when one side wins, display text and play sound
// change 'win' value to see game over sooner:
let win = 10;
if (checkl == win|| checkr == win) {
gameover.play();
textAlign(CENTER, CENTER);
textSize(25);
fill(0);
rect(width/2, height/2, width/2, 50, 10);
fill(255);
text('Game Finished!', width/2, height/2);
noLoop();
}

}

// draws a green table with offwhite diagonal lines:
function drawTable() {
let linescol = color(250, 250, 225);
let tcol = color(0, 100, 100);
strokeWeight(7);
stroke(linescol);
fill(tcol);
rect(width/2, height/2, 4*width/5, 4*height/5);
line(width/10, height/10, 9*width/10, 9*height/10);
line(width/10, 9*height/10, 9*width/10, height/10);
}

// draws individual cups based on x and y fields:
function drawCup() {
let diam = 20;
fill(200, 0, 0);	// red cup
stroke(255);
strokeWeight(2);
circle(this.x, this.y, diam);
// if the cup hasnt been hit, it has water in it:
if (this.isFull == true) {
noStroke();
fill(0, 0, 200);	// blue water
circle(this.x, this.y, diam/2);
}
}

// draws the ball
function drawBall() {
stroke(0);
strokeWeight(.5);
fill(250);
circle(this.x, this.y, 10);
}

//throws the ball
function throwBall() {
if (this.leftright==true) {		// going left to right
if (this.x < width/2) {
this.x += this.dx;
this.y -= this.dy;
} else {
this.x += this.dx;
this.y += (1.5*this.dy);
}
// remove the water if the ball goes into a full cup and reset ball placement
// play water cup noise if cup is hit
for (var j=0; j<rightCups.length; j++) {
if (dist(rightCups[j].x, rightCups[j].y, this.x, this.y)<=7 &
rightCups[j].isFull==true) {
watercup.play();
rightCups[j].isFull = false;
this.resetb();
}
}
} else {				// going right to left
if (this.x > width/2) {
this.x -= this.dx;
this.y -= this.dy;
}
else {
this.x -= this.dx;
this.y += (1.5*this.dy);
}
// same code to remove water from hit cup
for (var j=0; j<leftCups.length; j++) {
if (dist(leftCups[j].x, leftCups[j].y, this.x, this.y)<=7 &
leftCups[j].isFull==true) {
watercup.play();
leftCups[j].isFull = false;
this.resetb();
}
}
}
// no cups are hit, reset the ball position & play floor bounce track
if (this.x > width || this.x < 0 ) {
this.resetb();
bouncefloor.play();
}
}

//resets the ball to the next players starting position
function resetBall() {
this.leftright = -this.leftright;
if (this.leftright == true) {this.x=10}
else {this.x =width-10}
this.y = height/2;
this.dy = random(-4, 4);
// plays a 'whoosh' track for each throw
throwing.play();
}
``````

I chose to make a program that displays a game of water pong. The sounds I use are a ‘whoosh’ noise when a ball is thrown, a ‘clink’ sound if the ball goes into a full cup, a bouncing pingpong ball noise if the ball does not go into any full cups, and a bell noise if there is a winner. My code is randomized in a way that every game will be slightly different.

## Project 09: Computational Portrait

sketch
``````// This program deconstructs an image and then reconstructs it in a new way.
// The image is sliced into pieces and then drawn together in a new order
// Horizontally and vertically

var img;
var lineHeight = [];
var lineWidth = [];
var numSlices = 6;   //must be even

// slices are ordered and grouped into 4 arrays
var imgSlices1 = [];
var imgSlices2 = [];
var imgSlices3 = [];
var imgSlices4 = [];

}

function setup() {
createCanvas(480, 480);
imageMode(CENTER);
noStroke();

// get x and y values for the slices based on canvas size and number of slices
for (var i=0; i<numSlices; i++) {
let h = height/(numSlices+1);
let h1 = height/numSlices;
let w = width/(numSlices+1);
let w1 = width/numSlices;
lineHeight[i] = h1*i + h1/2;
lineWidth[i] = w1*i + w1/2;
// rearrange image pieces based on odd or even and fill arrays
if (i%2==0) {
imgSlices2.push(img.get(0, lineHeight[i], width/2, h));
imgSlices4.push(img.get(lineWidth[i], 0, w, height));
}
else {
imgSlices1.push(img.get(0, lineHeight[i], width/2, h));
imgSlices3.push(img.get(lineWidth[i], 0, w, height));
}
}
}

function draw() {
//there is some background that shows ecpecially with small numSlices values
// this sets background to match original image
var bgrnd = img.get(random(width), random(height));
background(bgrnd);

//draw image in new order
for (var i=0; i<numSlices/2; i++) {
image(imgSlices1[i], width/4, lineHeight[i]*2);
image(imgSlices2[i], 3*width/4, lineHeight[i]*2);
image(imgSlices3[i], lineWidth[i], height/2);
image(imgSlices4[i], lineWidth[i]+width/2, height/2);
}
noLoop();
}
``````

Based on what I’ve seen classmates post, I am not sure I did this project correctly. I am really happy with my outcome, though, and it is close to what I visualized before I started writing any code. When I read the prompt for this assignment, I was inspired by I video I had seen of an artist cutting up pictures and then putting them back together in strange ways that added new perspective and dimension. With this in mind, I decided to cut up the image and then layer it on top of itself in new ways. Technically, the image is displayed twice, but not the same way either time. With a quick change of code you can see how the portrait is drawn with more or fewer ‘slices’.

## Looking Outwards 09: A Focus on Women and Non-binary Practitioners in Computational Art

The artist I’ve chosen to write about this week is the amazing electronic music and effects composer, Suzanne Ciani. Beginning at the age of 22, Ciani has wom awards throughout her life for her musicianship, creativity, leadership, and contribution to the electronic music industry. She is the subject of a documentary that was filmed in 2014 to highlight her life and uniquely successful music career. As one of very few women in the music technology major here at CMU, I feel very inspired personally by Suzanne Ciani and I look up to her as a role model. Her design and production of the soundtrack to Greg Kmiec’s Xenon pinball machine is considered a significant milestone in game design history. With the use of new technology (in 1980), Ciani created a composition of interesting and catchy music that reacted directly to gameplay. This added a completely new layer to the game by providing a complex sensory experience that interacted directly with the person playing. I love this concept, and I am excited to try and implement this idea into some of my own projects.

Xenon by Suzanne Ciani, 1980

Suzanne’s website

## Looking Outwards 08: The Creative Practice of an Individual

The artist I chose to learn about this week is Alexander Chen, a Creative Director at Google Creative Lab who focuses primarily on music visualization. He has contributed to a plethora of tools and gadgets that bring a new perspective to sound. One of his projects, the Spectrogram, is probably the most visually appealing spectrogram widely used on the internet. In his Eyeo Festival presentation, he demonstrated how different types of sounds look on the spectrogram and how harmonies can be easier to conceptualize when the frequencies are mapped out spatially. Other projects of his, like a string instrumental version of the New York City subway system and its real-time schedule, bring together many abstract ideas into one cohesive work. I personally love the simple style of his pieces and the way he chooses to follow curiosity to create unique experiences. As a musician working in production and engineering, it is exciting to see new ways of experiencing music and to think about sound through a brand new lens.

Alexander Chen

## Project 07: Composition with Curves

sketch
``````// This program displays a grid of Epitrochoid curves with increasing nPoints and cusps.
// The canvas sets up with a 5x5 grid;
// When the mouse is pressed, more curves are drawn.
// Pressing the spacebar deletes one row and column.

var c;				// color
var density = 5;	// number of curves in each row and column
var nPoints;		// number of points used to draw each curve
var cusp;			// number of cusps on each curve
var mode = 0		// default mode uses Epitrochoid formula, other mode uses an edited Epitrochoid-like formula

function setup() {
createCanvas(480, 480);
}

function draw() {
background(200, 200, 255);
// draw a grid of curves based on density value:
for (var k=0; k<density; k++) {
cusp = k+1;							// number of cusps increases from left to right
for (var j=0; j<density; j++) {
push();
translate(k*width/density+240/density, 	// keeps grid centered regardless of density changes
j*height/density+240/density);
nPoints = (j+1)*density			// number of nPoints increases from top to bottom

//color based on grid and mouse location:
let r = map(j, 0, density, 0, 255);
let g = map(k, 0, density, 0, 255);
let b = map(mouseX, 0, width, 0, 255);
c = color(r, g, b);

// check which mode we are in and draw curves:
if (mode == 0) {drawEpitrochoidCurve(nPoints, cusp, c)}
else {drawEpitrochoidyCurve(nPoints, cusp, c)}
pop();
}
}
// labeling for easier understanding of the grid pattern:
textFont('monospace');
text('click mouse for more curves,	press spacebar for fewer curves', 20, height-12);
text('press "w" for weird curves,	press "r" for default curves', 20, height-3);
text('n u m b e r		o f		c u s p s   ----- >', 5, 10);
text('n P o i n t s', 5, 30, 1, 150);
push();
text('----- >', 140, -5);
pop();

}

// code adapted from sample in project description:
function drawEpitrochoidCurve(nPoints, cusp, color) {
// Epitrochoid:
//https://mathworld.wolfram.com/Epitrochoid.html

var x;
var y;

var a = 15;
var b = a / cusp;
var h = map(mouseY/8, 0, height, 0, a*5);
var ph = -mouseX / 40;

fill(color);

// shape of curve:
beginShape();
for (var i = 0; i < nPoints; i++) {
var t = map(i, 0, nPoints, 0, TWO_PI);

x = (a + b) * cos(t) - h * cos(ph + t * (a + b) / b);
y = (a + b) * sin(t) - h * sin(ph + t * (a + b) / b);
vertex(x, y);
}
endShape(CLOSE);
}

// code adapted from sample in project description and edited further:
// these curves do not interact with mouseY:
function drawEpitrochoidyCurve(nPoints, cusp, color) {
// Epitrochoidy: not quite an Epitrochoid, but follows a very similar formula

var x;
var y;

var a = 15;
var b = a / cusp;
var ph = -mouseX / 40;

fill(color);

// shape of curve:
beginShape();
for (var i = 0; i < nPoints; i++) {
var t = map(i, 0, nPoints, 0, TWO_PI);

x = (a + b) * cos(t) * cos(ph + t * (a + b) / b);
y = (a + b) * sin(t) * sin(ph + t * (a + b) / b);
vertex(x, y);
}
endShape(CLOSE);
}

// add one more row and column each time the mouse is pressed:
function mousePressed() {
density +=1;
}

//delete a row and column when the SPACEBAR is pressed:
function keyPressed() {
if (keyCode == 32) {
if (density == 0) {density = density }		// dont let density variable go below 0
else {density -= 1 }
}
if (keyCode == 87) {
if (mode == 0) {mode = 1}		// pressing 'w' switches to 'weird' mode
}
if (keyCode == 82) {
if (mode == 1) {mode = 0}		// pressing 'r' switches back to default 'regular' mode
}
}``````

After deciding to use the Epitrochoid curve formula, I realized that the sample in our project description is a very similar curve. Using the sample formula, I adapted the code until I liked what was being drawn on the canvas. Then I made a grid of curves and built them using various nPoint values and cusp values. As you can see in the drawing, as the curves are drawn from left to right, there are more cusps; and as they are drawn from top to bottom, there are more nPoints. Similarly to the sample code, my project uses mouseX and mouseY to interact with the angle and width of the curves. Additionally, the mouseX and mouseX variables are used to determine the fill color of each curve. When the mouse is clicked, the grid gets denser; when the spacebar is pressed, the density decreases. After playing around with the formula, I decided that I also wanted to include a mode for Epitrochoid-like curves that are not technically following the Epitrochoid formula. If you press ‘w’, the ‘weird’ mode turns on and the curves look strange and interesting. Pressing the ‘r’ key puts the program back into the default ‘regular’ mode. Below are a few screen shots that document some of my proccess:

## Looking Outwards 07: Information Visualization

The project I have chosen for this blog post is Kim Rees’s data visualization, “Revealing the overwhelming magnitude of loss from U.S. gun deaths.” This project used data from the FBI’s Uniform Crime Report to map every individual gun murder victim from a given year at once. I find this type of data visualization to be extremely important because it gives a more realistic perspective to viewers who would not otherwise be able to grasp the magnitude of impact by simply looking at the raw numbers. In this project, Rees includes a visualization of what she labels as “Stolen Years”, or the number of years that each murder victim is estimated to have likely lived had they not been shot. The website allows viewers to use filters with can show the proportion of gun murder victims who fall into a specific category or demographic. For example, using the race and age group filters, I am able to see that 433 black children were killed at the hands of gun violence in 2018. As put by Rees, this project reveals “a massive collection of human emotion hidden within rows and rows of numbers.”

Revealing the overwhelming magnitude of loss from U.S. gun deaths, Kim Rees

## Project 06 – Abstract Clock

sketch
``````// this program draws a 24/hour abacus that keeps track of day and time.
// each row of the abacus represents a different time-telling parameter:
var rowHeight = [-140, -74, -8, 58, 125];	//  [month, day, hr, min, sec]
var rowColor;
// arrays will be filled with bead objects:
var resetMonth = [];
var resetDay = [];
var resetHr = [];
var resetMin = [];
var resetSec = [];

function setup() {
createCanvas(480, 480);
background(220);
rectMode(CENTER);

rowColor = [color(145, 110, 205), 		// [month
color(200, 140, 205), 		//  day
color(145, 140, 255), 		//  hour
color(145, 185, 215), 		//  minute
color(145, 200, 130)];		//  second]

for (i=0; i<12; i++) {
}
for (i=0; i<31; i++) {
}
for (i=0; i<24; i++) {
}
for (i=0; i<30; i++) {
}
for (i=0; i<30; i++) {
}
}

function draw() {
background(220);

//center canvas
translate(width/2, height/2);

// draw frame and rows of abacus
drawAbacus();

// draw all of the beads using the array for their row:
for (j=0; j<allRows.length; j++) {
}

// check the time and move the beads accordingly:
//noLoop();
}

//Draws the frame of the abacus:
function drawAbacus() {
fill(80, 55, 30);
//base:
//left post:
rect(-195, 0, 7, 400);
//right post:
rect(195, 0, 7, 400);
for (i=0; i<rowHeight.length; i++) {
rect(0, rowHeight[i], 375, 5);
}
//labels:
textSize(16);
textFont('monospace');
fill(0);
text('month', -181, rowHeight[0]+28);
text('day', -181, rowHeight[1]+28);
text('hour', -181, rowHeight[2]+28);
text('minute', -181, rowHeight[3]+28);
text('second', -181, rowHeight[4]+28);
}

// draws all of the beads into their rows
fill(rowColor[j]);				// color taken from for loop in draw function
strokeWeight(1.5);
}
}

// checks time and moves the corresponding beads:
function moveBeads(row, func, ind, rowReset) {
var dist;
if (row.length == 12) {dist = 283 }		//different distances based on number of beads in the row
if (row.length == 31) {dist = 150 }
if (row.length == 24) {dist = 199 }
if (row.length == 30) {dist = 157 }
for (i=0; i<func; i++) {
// the minutes and seconds beads will move every 2x they are passed bc it looks better like that:
if (dist == 157) {
row[i/2] = {x: beadw*i/2-dist, y: rowHeight[ind]}
} else {
row[i] = {x: beadw*i-dist, y: rowHeight[ind]}
}
// february has 28 days:
if (func == 28 & row.length == 31 && month()==2) { resetBeads(rowReset, monthBeads) }
// 30 days has september, april, june, and november:
if (func == 30 && row.length == 31 && month()==9) { resetBeads(rowReset, monthBeads) }
if (func == 30 && row.length == 31 && month()==4) { resetBeads(rowReset, monthBeads) }
if (func == 30 && row.length == 31 && month()==6) { resetBeads(rowReset, monthBeads) }
if (func == 30 && row.length == 31 && month()==11) { resetBeads(rowReset, monthBeads) }

}
// once the time parameter resets to 0, so do the beads:
if (func == 0) { resetBeads(rowReset, row) }
}

// sets the beads back to their original position, by row: