Here is my final project! It’s an interactive animation of a waterpark. Click anywhere to send someone down a random slide — or, if you prefer, click the top of a slide to send someone down that specific one!
//sam rauch, srauch, section B
//final project
//this code creates an animation of a waterpark. It's populated with people objects that have a
//skin tone and swimsuit color randomly picked from an array, so they are different every time
//you reload the page. In the foreground, people go by in a lazy river in tubes. In the background,
//people wait in line for waterslide towers.
//click anywhere to send someone down a random slide, or, click at the top of a slide to send
//a person down that specific slide!
//clouds x, y, and scale
var cloudsx = [];
var cloudsy = [];
var cloudsSc = [];
//skin tones and swimsuit colors
var skincolors;
var swimcolors;
//will hold people waiting for waterslide 1
var topSlide = [];
var slideToppers =[];
var groundWaiters1 = [];
var stairWaiters1 = [];
//will hold people waiting for waterslide 2
var groundWaiters2 = [];
var stairWaiters2 = [];
var topguy;
//bushes and trees
var bushSizes = [];
var bushColors = [];
var treeSizes = [];
var treeColors = [];
//people in lazy river
var tubers = [];
//color of wood in waterslide towers
var woodcolor;
//variables for people going down slides
var slider;
var slidetoggler;
var randomizer1;
//store mouseX and mouseY when clicked
var xWhenClick;
var yWhenClick;
function setup() {
createCanvas(600, 400);
background(220);
woodcolor = color(105, 89, 56);
//fill cloud array with clouds at random scales and positions
for (var i=0; i<8; i++){
cloudsx[i]= random(-10, width-10);
cloudsy[i] = random(-10, 200);
cloudsSc[i] = random(1,3);
}
//skin tones and swimsuit color values
skincolors = [color(235, 207, 176), color(204, 168, 126), color(107, 77, 43),
color(148, 98, 41), color(117, 84, 47), color(217, 195, 154),
color(235, 213, 192), color(69, 48, 29), color(247, 220, 195)];
swimcolors = [color(194, 78, 130), color(224, 49, 22), color(224, 144, 22),
color(102, 23, 11), color(102, 23, 11), color(102, 93, 11),
color(207, 195, 87), color(159, 207, 87), color(49, 145, 42),
color(42, 145, 89), color(92, 191, 186), color(92, 140, 191),
color(24, 73, 125), color(105, 89, 156), color(154, 89, 156),
color(55, 84, 179)]
//filling the array that handles the people in the lazy river
var tubespacer = 50;
for (var i=0; i<10; i++){
tubers[i] = makeTuber(tubespacer, random(340, 350));
tubespacer += random(30, 100);
}
//creating the size and color of the bushes and trees
for (var i=0; i<width+30; i++){
bushColors[i] = color(100, random(150, 170), 100);
bushSizes[i] = random(30, 60);
treeColors[i] = color(100, random(150, 170), 100);
treeSizes[i] = random(100, 150);
}
//filling the array of people waiting on top of waterslide1
var slideLocation = 410;
for (var i=0; i<3; i++){
topSlide[i] = makeDude(365+(i*15), 125, false);
slideToppers[i] = makeDude(slideLocation, 125, false);
slideLocation +=40;
}
//filling the array of people waiting for waterslide1 on the ground
var groundWaiters1Loc = 280;
for (var i=0; i<9; i++){
groundWaiters1[i] = makeDude(groundWaiters1Loc, 280, false);
groundWaiters1Loc += 20;
}
//filling the array of people waiting on the waterslide1 stairs
var stairWaiters1Loc = 460;
var stairHeight1 = 280;
for (var i=1; i<13; i++){
stairWaiters1[i] = makeDude(stairWaiters1Loc, stairHeight1, false);
stairWaiters1Loc += 17;
if (i<4 || i>=3) {stairHeight1-= 20;}
if (i%4 == 0) {stairWaiters1Loc = 460;}
if (i>4 & i<=8) {stairHeight1 = 205;}
}
//filling array of people waiting on ground for waterslide 2
var groundWaiters2Loc = 240;
for (var i=0; i<6; i++){
groundWaiters2[i] = makeDude(groundWaiters2Loc, 270, false);
groundWaiters2Loc -= 20;
}
//filling array of people waiting on stairs for waterslide 2
var stairWaiters2Loc = 120;
var stairHeight2 = 265;
var sidestep = 14;
var counter2 = 0;
for (var i=0; i<20; i++){
stairWaiters2[i] = makeDude(stairWaiters2Loc, stairHeight2, false);
counter2 += 1;
if (counter2%4==0){
sidestep = sidestep*-1;
}else{
stairWaiters2Loc -= sidestep;
}
stairHeight2 -= 11;
}
topguy = makeDude(85, 50, false);
//when this is true (set to true when mouse is clicked), an if statement in draw
//runs that sends someone down a slide
slidetoggler = false;
}
function draw() {
background(162, 219, 222);
backgroundstuff(); //draw clouds, trees, waterslides
slidego(); //send someone down slide when the mouse is clicked
foregroundstuff(); //draw lazy river and bushes
}
//trigger someone to go down slide in draw function
function mousePressed(){
xWhenClick = mouseX;
yWhenClick = mouseY;
slidetoggler = true;
slider = makeDude(0, 0, true);
randomizer1 = random(0,4);
}
//translate and rotate to the top of a specific slide
function slidemover(xpos,ypos,rot){
push();
translate(xpos,ypos);
rotate(radians(rot));
slider.y += 4;
slider.draw();
pop();
}
//send someone down specific slide if clicked, random otherwise
function slidego(){
if (slidetoggler==true){
//if click at the top of a specific slide, send someone down that slide
if (xWhenClick >= 70 & xWhenClick <= 130 && yWhenClick >= 40 && yWhenClick <= 100){
slidemover(88,50,-25);
} else if (xWhenClick >= 400 & xWhenClick <= 435 && yWhenClick >= 110 && yWhenClick <= 170){
slidemover(432,140,30);
} else if (xWhenClick >= 440 & xWhenClick <= 475 && yWhenClick >= 110 && yWhenClick <= 170){
slidemover(472,140,30);
} else if (xWhenClick >= 480 & xWhenClick <= 515 && yWhenClick >= 110 && yWhenClick <= 170){
slidemover(512,140,30);
} else { //otherwise, send down a random slide
push();
if(randomizer1<=3){ //for three slides on right tower
if (randomizer1 < 1){
translate(432,140);
} else if (randomizer1 >= 1 & randomizer1 < 2){
translate(472,140);
} else {
translate(512, 140);
}
rotate(radians(30));
} else { //for tall slide on left tower
translate(88,50);
rotate(radians(-25));
}
slider.y+= 4;
slider.draw();
pop();
}
}
}
//creates a person object
function makeDude(xpos, ypos, armpos){
var dude = {x:xpos, y:ypos, armsup:armpos,
swimsuit:swimcolors[floor(random(0,15))],
swimtype:random(0,2),
skin:skincolors[floor(random(0,9))],
draw:drawDude};
return dude;
}
function drawDude(){
noStroke();
fill(this.skin);
ellipse(this.x, this.y, 10, 10); //head
rect(this.x-5, this.y+7, 10, 16); //torso
stroke(this.skin);
strokeWeight(4.5);
line(this.x, this.y, this.x, this.y+10); //neck
strokeWeight(3.5); //toggles if arms are up or down based on this.armsup var declared in constructor
if (this.armsup == true){
line(this.x-4, this.y+7, this.x-11, this.y-2); //right arm
line(this.x+4, this.y+7, this.x+11, this.y-2); //left arm
} else {
line(this.x-4, this.y+7, this.x-10, this.y+16); //right arm
line(this.x+4, this.y+7, this.x+10, this.y+16); //left arm
}
strokeWeight(4);
line(this.x-3, this.y+22, this.x-3, this.y+35); //right leg
line(this.x+3, this.y+22, this.x+3, this.y+35); //left leg
noStroke();
if (this.swimtype<1){ //swim trunks
fill(this.swimsuit);
rect(this.x-6, this.y+18, 12, 5);
rect(this.x-6, this.y+23, 5, 5);
rect(this.x+1, this.y+23, 5, 5);
} else { //bikini
fill(this.swimsuit);
rect(this.x-5, this.y+10, 10, 13);
ellipse(this.x-2, this.y+10.5, 5, 5);
ellipse(this.x+2, this.y+10.5, 5, 5);
stroke(this.swimsuit);
noStroke();
fill(this.skin);
triangle(this.x-1, this.y+24, this.x-5, this.y+24, this.x-5, this.y+18);
triangle(this.x+1, this.y+24, this.x+5, this.y+24, this.x+5, this.y+18);
rect(this.x-5, this.y+13, 10, 4);
}
}
//creates a person in a tube object
function makeTuber(xpos, ypos){
var tuber = {x:xpos, y:ypos,
swimsuit:swimcolors[floor(random(0,15))],
swimtype:random(0,2),
skin:skincolors[floor(random(0,9))],
draw:drawTuber};
return tuber;
}
function drawTuber(){
push();
//inner tube
stroke(this.skin);
strokeWeight(4);
line(this.x-1, this.y+12, this.x-11, this.y+22);
noStroke();
fill(62, 158, 62);
rect(this.x-14, this.y+18, 28, 16);
ellipse(this.x-14, this.y+26, 16);
ellipse(this.x+14, this.y+26, 16);
stroke(30, 94, 30);
strokeWeight(4);
line(this.x-10, this.y+23, this.x+10, this.y+23);
//head, neck, torso, and back arm
push();
translate(this.x+6, this.y+4);
rotate(radians(10));
noStroke();
fill(this.skin);
ellipse(0, 0, 10, 10); //head
rect(-5, 7, 10, 16); //torso
stroke(this.skin);
strokeWeight(4.5);
line(0, 0, 0, 10); //neck
noStroke();
if (this.swimtype<1){ //swim trunks
fill(this.swimsuit);
rect(-6, 18, 12, 5);
rect(-6, 23, 5, 5);
rect(1, 23, 5, 5);
} else { //bikini
fill(this.swimsuit);
rect(-5, 10, 10, 13);
ellipse(-2, 10.5, 5, 5);
ellipse(+2, +10.5, 5, 5);
stroke(this.swimsuit);
noStroke();
fill(this.skin);
triangle(-1, 24, -5, 24, -5, 18);
triangle(1, 24, 5, 24, 5, 18);
rect(-5, 13, 10, 4);
}
pop();
strokeWeight(4.5);
stroke(this.skin);
line(this.x+4, this.y+25, this.x+4, this.y+25);
line(this.x, this.y+25, this.x-6, this.y+21);
//a few more details
noStroke();
fill(62, 158, 62);
rect(this.x-10, this.y+25, 20, 9);
strokeWeight(4.5);
stroke(this.skin);
line(this.x+10, this.y+14, this.x+16, this.y+25); //front arm
line(this.x-6, this.y+21, this.x-10, this.y+31); //right leg
line(this.x+2, this.y+25, this.x, this.y+34); //left leg
//water texture
noStroke();
fill(81, 181, 201, 100);
rect(this.x-22, this.y+30, 44, 6);
pop();
}
//draws the waterslide on the left
function waterslide1(x,y){
//stairs
fill(woodcolor);
beginShape();
vertex(x+100, y+80);
vertex(x+100, y+90);
vertex(x+170, y+20);
vertex(x+170, y+10);
endShape();
rect(x+100, y+80, 70, 8);
beginShape();
vertex(x+100, y+160);
vertex(x+113, y+160);
vertex(x+170, y+95);
vertex(x+170, y+80);
endShape();
//pillars
fill(95, 79, 46);
rect(x+5, y+5, 5, 155);
rect(x+100, y+5, 5, 155);
rect(x+170, y+5, 5, 155);
//shade poles
rect(x+3, y-50, 3, 50);
rect(x+180-6, y-50, 3, 50);
rect(x+50, y-55, 3, 55);
rect(x+130-6, y-55, 3, 55);
//shades
fill(35, 82, 168);
rect(x+3, y-55, 47, 6);
triangle(x+3, y-55, x+50, y-55, x+27, y-70);
rect(x+50, y-60, 77, 5);
triangle(x+50, y-60, x+127, y-60, x+88.5, y-85);
rect(x+127, y-55, 50, 5);
triangle(x+127, y-55, x+176, y-55, x+152, y-70);
//railings
fill(95, 79, 46);
var xpos = x;
for (var i = 0; i < 12; i++){
rect(xpos, y-15, 2, 15);
xpos += 5;
}
for (var j = 0; j<2; j++){
xpos += 22
for (var i = 0; i < 4; i++){
rect(xpos, y-15, 2, 15);
xpos += 5;
}
}
xpos += 19
for (var i = 0; i < 4; i++){
rect(xpos, y-15, 2, 15);
xpos += 5;
}
//deck
fill(woodcolor);
rect(x, y, 180, 10);
rect(x, y-15, 180, 3);
//draw slides
var color1 = color(166, 61, 124);
slide1(x+60, y, color1);
var color2 = color(186, 176, 67);
slide1(x+100, y, color2);
var color3 = color(57, 94, 163);
slide1(x+140, y, color3);
}
//draws slides for waterslide1 function
function slide1(x,y,color){
var slidewide = 22;
fill(color);
beginShape();
vertex(x, y);
vertex(x+slidewide, y);
vertex(x-80, y+180);
vertex(x-80-slidewide, y+180);
endShape();
var margin = 4;
fill(150, 255, 255, 50);
beginShape();
vertex(x+margin, y);
vertex(x+slidewide-margin, y);
vertex(x-80-margin, y+180);
vertex(x-80-slidewide+margin, y+180);
endShape();
}
//draws waterslide on left
function waterslide2(x,y){
//pillars and stairs
var loc = 310; //increment 46
for (var i = 0; i< 5; i++){
fill(woodcolor);
if (i%2==0 || i==0) {
beginShape();
vertex(x-30, loc-51);
vertex(x-30, loc-41);
vertex(x+30, loc);
vertex(x+30, loc-10);
endShape();
} else {
beginShape();
vertex(x-30, loc);
vertex(x-30, loc-10);
vertex(x+30, loc-51);
vertex(x+30, loc-41);
endShape();
}
loc -= 44;
}
fill(95, 79, 46);
rect(x-30, y, 6, 230); //bottom y = 310
rect(x+24, y, 6, 230);
rect(x-30, y-40, 3, 40);
rect(x+27, y-40, 3, 40);
fill(189, 65, 43);
rect(x-30, y-45, 60, 6);
triangle(x-30, y-45, x+30, y-45, x, y-60);
fill(woodcolor);
rect(x-30, y, 60, 10);
rect(x-30, y-15, 15, 2.5);
rect(x+15, y-15, 15, 2.5);
var location = x-30;
for (var i = 0; i<8; i++){
rect(location, y-15, 2, 15);
if (i != 3){location += 5;}
else {location += 28;}
}
fill(252, 165, 3);
beginShape();
vertex(x-15, y);
vertex(x+15, y);
vertex(x+140, y+260);
vertex(x+110, y+260);
endShape();
fill(150, 255, 255, 50);
beginShape();
vertex(x-10, y);
vertex(x+10, y);
vertex(x+130, y+250);
vertex(x+115, y+260);
endShape();
}
//draws cloud
function cloud(x,y, amt){
noStroke();
fill(218, 237, 236);
push();
translate(x,y);
scale(amt);
ellipse(0,0,40);
ellipse(-20, 5, 30);
ellipse(20, 5, 30);
pop();
}
//draws background
function backgroundstuff(){
//clouds
for (var i=0; i<8; i++){
cloud(cloudsx[i], cloudsy[i], cloudsSc[i]);
}
//trees
noStroke();
for (var i=0; i<width+30; i+= 80){
fill(treeColors[i]);
ellipse(i, 270, treeSizes[i]);
}
//fence
strokeWeight(2);
stroke(120);
line(0, 280, width, 280);
strokeWeight(.75);
for (var i=-12; i<width+12; i+=4){
line(i, 300, i+16, 280);
line(i, 300, i-16, 280);
}
//concrete
noStroke();
fill(199, 196, 163); //concrete color
rect(0, 300, width, 100);
fill(168, 162, 28);
//pool at base of slides
fill(81, 181, 201);
rect(80, 325, 420, 100);
ellipse(80, 350, 50);
ellipse(500, 350, 50);
//people in waterslide1 line
for (var i=0; i<topSlide.length; i++){
topSlide[i].draw();
slideToppers[i].draw();
}
for (var i=0; i<groundWaiters1.length; i++){
groundWaiters1[i].draw();
}
for (var i=1; i<stairWaiters1.length; i++){
stairWaiters1[i].draw();
}
//people in waterslide2 line
for (var i=0; i<stairWaiters2.length; i++){
stairWaiters2[i].draw();
}
for (var i=0; i<groundWaiters2.length; i++){
groundWaiters2[i].draw();
}
topguy.draw();
//draw waterslides
waterslide1(350, 160);
waterslide2(100, 80);
}
//draws bushes, draws and updates lazy river
function foregroundstuff(){
//bushes
for (var i=0; i<width+30; i+= 30){
fill(bushColors[i]);
ellipse(i, 350, bushSizes[i]);
}
//lazy river
fill(179, 176, 143);
rect(0, 350, width, 50);
fill(81, 181, 201);
rect(0, 355, width, 40);
//lazy river inhabitants
for (var i = 0; i < tubers.length; i++){
if (tubers[i].x <= -30){
var mover = tubers.shift();
mover.x = width + 28;
tubers.push(mover);
}
tubers[i].draw();
tubers[i].x -= 0.5;
}
}
My code shows a scrolling New Mexico landscape, with sagebrush in the foreground, sage-covered land stretching to the mountains in the middleground, and a mountain range in the background.
//Sam Rauch, section B, srauch@andrew.cmu.edu, project
//this code creates a randomly generated scrolling new mexico landscape with sagebrush
//in the foreground, distant bushes in the middleground, and mountains in the background.
//A late afternoon moon hangs low in the sky.
//arrays for objects
var sageArray = [];
var mountainArray = [];
var backbushes = [];
function setup() {
createCanvas(400, 200);
background(220);
text("p5.js vers 0.9.0 test.", 10, 15);
frameRate(30);
//fill sage array with random sagebrush
var initsageY = random(30, 40);
for (var i = 0; i<20; i++){
sageArray.push(newSage(initsageY, random(160, 190)));
initsageY += random(20, 40);
}
//fill mountain array with random starter mountains
var initmountX = random(0,40);
for (var i = 0; i < 20; i ++){
var mont = makeMountain(initmountX, random(70, 90), random(40, 60));
mountainArray.push(mont);
initmountX += random(30, 40);
}
//fill backbushes array with random starter bushes
var initBushX = random(0,10);
for (var i = 0; i<100; i++){
backbushes.push(newBush(initBushX, random(100, 150)));
initBushX += random(0, 10);
}
}
function draw() {
background(129, 173, 223);
noStroke();
//draw moon
var moonfill = color(235, 243, 247, 200);
fill(moonfill);
ellipse(300, 50, 20, 20);
//draw middleground
fill(111, 158, 148);
rect(0, 100, 400, 100);
//draw mountains, push in new mountains on right as they go off screen to left
for (var i = 0; i < mountainArray.length; i++){
mountainArray[i].peakX -= 0.25;
mountainArray[i].draw();
if (mountainArray[i].peakX <= -70){
mountainArray.shift();
mountainArray.push(makeMountain(random(440, 460), random(60, 90), random(40, 60)));
}
}
//draw backbushes, push in new ones as go off screen
for (var i = 0; i < backbushes.length; i++){
backbushes[i].x -= 0.5;
backbushes[i].draw();
if (backbushes[i].x <= -10){
backbushes.shift();
backbushes.push(newBush(random(402, 420), random(100, 150)));
}
}
//draw foreground
fill(156, 127, 70);
rect(0, 150, 400, 50);
//draw sagebrush in front
for (var i = 0; i < sageArray.length; i++){
//draw each sagebrush shadow; in seperate loop so it will always draw before
//(thus underneath) the sagebrush
fill(117, 98, 56);
ellipse(sageArray[i].x, sageArray[i].y, 20, 8);
}
for (var i = 0; i < sageArray.length; i++){
sageArray[i].x -= 2; //move each sagebrush along to left
sageArray[i].draw(); //draw each sagebush
}
if (sageArray[0].x < -10){ //if sagebrush is off the canvas, shift and push in a new one
sageArray.shift();
sageArray.push(newSage(random(410, 430), random(160, 190)));
}
}
//objects for sagebrush, mountain, and backbush
function newSage(xpos,ypos){
var sage = {x: xpos, y:ypos, draw:drawSagebrush};
return sage;
}
function drawSagebrush(){
stroke(66, 46, 23);
var bushstart = this.x-10;
for (var i = 0; i<8; i++){
line(this.x,this.y,bushstart,this.y-15);
bushstart += 3;
}
stroke(66, 110, 90);
fill(93, 135, 111);
ellipse(this.x-8, this.y-15, 7, 7);
ellipse(this.x+8, this.y-15, 7, 7);
ellipse(this.x-5, this.y-17, 8, 8);
ellipse(this.x+5, this.y-17, 8, 8);
ellipse(this.x,this.y-18, 10, 10);
noStroke();
}
function makeMountain(x, y, wide){
var mountain = {peakX: x, peakY: y, base:wide, draw: drawMountain};
return mountain;
}
function drawMountain(){
fill(96, 129, 181);
beginShape();
vertex(this.peakX, this.peakY);
vertex(this.peakX-this.base, 100);
vertex(this.peakX+this.base, 100);
endShape();
}
function newBush(xpos,ypos){
var bush = {x: xpos, y:ypos, draw: drawBush};
return bush;
}
function drawBush(){
strokeWeight(5);
stroke(106, 135, 124);
point(this.x, this.y);
strokeWeight(1);
noStroke();
}
I found a very interesting episode of Sidedoor, a podcast created by the Smithsonian, that interviewed artist Stephanie Dinkins about her work on how Black stories interact with AI.
Many AIs that are programmed to generate language pull information from the internet. This makes sense – if you’re looking to train an AI to talk, why not use the biggest and most easily accessible database of language in the world? Well, because, as Dinkins articulates, the internet is racist. It’s full of language that, even if it’s not overtly racist, is filled with the inherent racist biases and assumptions that have become ingrained within the English language. Those biases, by extension, become embedded in the AI. To counter this phenomena, Dinkins created Not the Only One: an AI that creates a memoir of a Black American family. It draws its language ability not from the internet, but from the familial stories from Dinkins, her mother, and her grandmother. Her work raises valuable questions about where our information is coming from. While we think of computers as completely logical, we can’t forget that they’re a product of human creation, and thus always susceptible to our biases and human errors.
]]>Here is my “glass door” portrait. You can change the fidelity of it by moving the mouse up and down. By moving your mouse all the way to the bottom of it, the full portrait is revealed.
//Sam Rauch, section B, srauch@andrew.cmu.edu, project 09
//this code makes a "glass door" picture of me with colored ellipses, like you're looking
//at me through one of those textured glass doors. It samples the image
//color at each pixel location (though it skips every other y row to help it run a bit faster)
//and draws a circle there. The circle size is tied to mouseY, so when the mouse is higher up,
//the circles are larger and the image is lower-fidelity, but it gets higher fidelity as the
//user moves the mouse down the image.
var samImg;
var uploadImg;
var dotArray = [];
var first = 0;
function preload(){
uploadImage = "https://i.imgur.com/nTNXyHg.jpg";
}
function setup() {
createCanvas(400, 533);
background(220);
samImg = loadImage(uploadImage);
frameRate(1);
}
function draw() {
background(50);
image(samImg, 0, 0);
if (first == 0){ // define all the dot objects; run once at start of draw
for (var y = 0; y < 533; y+=2){ //fill array with dots all over canvas
for (var x = 0; x < 400; x++){
var clr = samImg.get(x, y);
dotArray.push(makeDot(x, y, 20, clr))
}
}
first ++;
}
var sizeTicker = map(mouseY, 0, height, 45, 3); //set dot size to move with mouseY
shuffle(dotArray, true);
for(var i = 0; i < dotArray.length; i++){ //draw dots in array
dotArray[i].size = sizeTicker;
dotArray[i].drawDot();
}
}
//dot object and draw function
function makeDot(xpos,ypos,siz,clr){
var dot = {x: xpos, y:ypos, size:siz,
color:clr,
drawDot: drawDotFunction}
return dot;
}
function drawDotFunction(){
stroke(this.color);
//print(this.color)
strokeWeight(this.size);
point(this.x, this.y);
}
For my piece, I picked ENIGMA, a computer-generated film created by Lillian Schwartz in 1972.
Schwartz worked out of Bell Labs’ Acoustical and Behavioral Research Center from 1968 to 2002, and during her time there, she created a series of videos that were visual output of computer algorithms, ENIGMA being one of them.
What I find so remarkable about her videos is that they were algorithmically generated, but because of the technology at the time, they were physically produced. Each frame the computer output had to be burned into 35mm film one at a time, and then the film itself had to be developed before Schwartz could see the image. Schwartz fluently used the kind of creative thinking afforded by computer-generated art decades before such computational art was accessible or even necessarily made sense to make, allowing her to produce art that is completely unlike anything else being made at the time. As she said in an interview, “I’ve always been interested in what different media could provide me in terms of creating something that had never been seen before or provoke me to create in ways I had not created before.”
Her work prompts me to think about how art can inspire technology. Art is perhaps an excellent place for an algorithm or computer program to begin its life, because there isn’t initially an expectation that it’s perfect or exactly efficient. Freed from the initial constraints of functionality, new creative ideas can flow and grow, and by the time the program is being translated into the practical realm, it’s become something completely new.
]]>I watched Deb Chachra’s talk on architectural biology and biological architecture. Chachra considers herself an engineer rather than an artist; she’s a professor of engineering at Olin College in Massachusetts. While her primary focus is on making engineering more accessible and inclusive, particularly in the realm of where engineering and gender intersect, she also has an interest in where material science and biology intersect. This talk was about the overlaps between biological structures and architectural engineering. As she explains, with architectural calculation and fabrication techniques evolving as they are, we’re approaching being able to create structures that imitate bone that provide the highest possible strength to weight ratio and can “heal” themselves.
Bone-like building structures would be a spectacular example of biomimicry, a branch of design I find fascinating. Biomimicry is the practice of imitating nature to create the most efficient design possible – after all, there’s no better designer than 3.7 billion years of evolution. Because biology and engineering don’t traditionally intersect, it takes someone who is interested in both to ideate such solutions, which is why I admire Chachra’s ingenuity to be crossing fields and embracing the emergent potential. Beyond her work itself, I admired her presentation. She gave a very comprehensive but brief history of architecture, and gave an easy-to-understand but not infantilizing explanation of some of the basic biological processes of bones.
]]>Here is my program that runs on curves. I call it “laser ballerinas”. Move your mouse left to right to increase the size of the curves and the overall rotation, and move your mouse up and down to change the number of “lobes” on each curve. Click to change the colors of each lobe!
//Sam Rauch, section B, srauch@andrew.cmu.edu, project 07
//This code produces a "laser ballerinas" using epicycloid curves. Ten epicycloid curves of decreasing
//size and random colors are drawn on top of each other to produce "nested curves", and six nested curves
//are drawn in a hexagon pattern around the center of the canvas, which spins. MouseX controls the size of
//the curves and the rotation of the hexagon pattern, while mouseY controls the number of "lobes" produced
//in each epicycloid curve. Click to change the colors of the ballerinas!
var coloroptions;
function setup() {
createCanvas(400, 400);
background(220);
text("p5.js vers 0.9.0 test.", 10, 15);
coloroptions = [];
for (var i = 0; i < 9; i++) {
coloroptions[i] = color(random(255), random(255), random(255));
}
}
function draw() {
background(0);
translate(width/2, height/2);
//create spin the hexagon of curves around the center of the canvas according to mouseX
push();
var spinamt = map(mouseX, 0, width, 0, 2*PI)
rotate(spinamt);
//draws six nested curves in a hexagon pattern
for (var i = 0; i < 6; i++){
push();
translate(100, 0);
nestedCurve();
pop();
rotate(radians(60));
}
pop();
}
//generates epicycloid curve (https://mathworld.wolfram.com/Epicycloid.html) with center at 0,0
function drawCurve(color){
noFill();
stroke(color);
strokeCap(ROUND);
strokeJoin(ROUND);
strokeWeight(3);
var points = 80;
var xValues = [];
var yValues = [];
var a = mouseX/4; //radius of larger inner circle
var b = constrain(mouseY/10, 0, a) ; //40; //radius of smaller outer circle
var theta = 0;
//generating x and y values based on epicycloid curve forumla, and increasing the center angle
//by a small amount
for (var i = 0; i < points; i++) {
theta += radians(5);
xValues[i] = (a+b)*cos(theta) - b*cos(((a+b)/b)*theta);
yValues[i] = (a+b)*sin(theta) - b*sin(((a+b)/b)*theta);
}
//plotting x and y values
beginShape();
for (var i = 0; i <points; i++) {
vertex(xValues[i], yValues[i]);
}
endShape();
}
//draws ten epicycloid curves of random colors nested inside each other
function nestedCurve() {
push();
var size = 1;
var sizeIncrement = 0.1;
//draws ten epicycloid curves, but for each one, decreases scale and change color
for (var i = 0; i < 9; i++) {
scale(size);
var col = coloroptions[i];
drawCurve(col);
size -= sizeIncrement;
}
pop();
}
function mousePressed(){
for (var i = 0; i < 9; i++) {
coloroptions[i] = color(random(255), random(255), random(255));
}
}
I enjoy these data visualizations of the 2021 summer olympics, conceived by designer Eden Weingart and created by the New York Times graphic department. This one is specifically for swimming, the 400-meter freestyle.
To make these animations, the graphic team created a program that can apply the raw data of the race onto animations, allowing twitter users to see a sped-up version of the race’s events (not just the results!). This raw data included each swimmer’s time for every meter of the race, the time they hit the end of the pool and turned around, and the time they finished. The program then mapped this data onto an animated avatar for each athlete.
I find this approach to sports reporting really cool, since it provides a different way for us to interact with the data of the sport. It can be quite tempting to throw all of the data out the window once we know who won, but an approach such as this allows casual readers to see those intricacies – who led at first, any surprising turnovers, etc. – in a consumable and exciting way.
]]>Here is my abstract clock. It’s a beach with tides: high tide is at noon and low tide is at midnight. The grass rustles in the breeze, and the beach is strewn with starfish and shells. It’s dark from 8 pm to 5am. Sunrise is from 6am to 7am, and sunset is from 7pm to 8pm.
//Sam Rauch, section B, srauch@andrew.cmu.edu, project 06: abstract clock
//this code creates a beach with a tide. High tide is at noon, and low tide is at midnight.
//the grass rustles in the sea breeze. it gets darker from 7pm 8 pm and lighter from 6 to 7am.
var grasslength = []; //length of blades of grass
var sandspotX = []; //x position of specks in sand
var sandspotY = []; //y position of specks in sand
var waveY; //height of the tide
var waveShift; //amount the water's edge fluctuates by
var daytime; //the time of day in minutes
var darkness; //amount of darkness it is
function setup() {
createCanvas(400, 400);
background(220);
text("p5.js vers 0.9.0 test.", 10, 15);
//initializing x position of specks in sand
for (var i = 0; i < 50; i++){
sandspotX[i] = random(0, width);
}
//initializing y position of specks in sand
for (var i = 0; i < 50; i++){
sandspotY[i] = random(200, 400);
}
frameRate(5);
//creating y position of tide. High tide is at noon, low tide is at midnight
daytime = hour()*60 + minute(); //time of day, in minutes
if (daytime < 690) { //if before noon
waveY = map(daytime, 0, 690, 400, 200); //before noon, wave gets higher with time
} else { //if after noon
waveY = map(daytime, 690, 1380, 200, 400); //after noon, wave gets lower with time
}
//making it get lighter and darker at sunrise and sunset
if (daytime >= 300 & daytime < 360) { //if from 5 to 6 am, it gets lighter
darkness = map(daytime, 300, 360, 120, 0);
} else if (daytime >= 1140 & daytime < 1200) { //from 7 to 8 pm, it gets darker
darkness = map(daytime, 1140, 1200, 0, 120);
} else if (daytime >= 360 & daytime < 1140) { //in the middle of the day, it's not dark
darkness = 0;
} else { //in the middle of the night, it's dark
darkness = 120;
}
}
function draw() {
noStroke();
background(155, 207, 207);
//row of grass bunches
for (var i = 0; i <2; i++){
for(var j = 0; j<=width; j += 20) {
grass(j, 220);
}
}
//sand
fill(196, 178, 122);
rect(0, 200, width, 200);
//little specks in sand
strokeWeight(3);
stroke(146, 128, 72)
for (var i = 0; i<50; i++){
point(sandspotX[i], sandspotY[i]);
}
noStroke();
//assorted shells and starfish
starfish(250, 250, color(120, 130, 120), 10);
starfish(50, 340, color(50, 74, 64), 50);
starfish(180, 370, color(120, 79, 97), 5);
shell(140, 260, color(60, 50, 70), color(100, 80, 107), 60);
shell(300, 300, color(80, 50, 50), color(120, 80, 97), -5);
//waves
waveShift = randomGaussian(1, 2);
fill(30, 100, 155, 240);
rect(0, waveY+waveShift, width, 300);
fill(30, 100, 155, 100);
rect(0, waveY-(random(1,3))+waveShift, width, 300);
//rectangle that makes darkness and lightness of environment using alpha channel
blendMode(MULTIPLY);
fill(35, 50, 70, darkness);
rect(0,0,width,height);
blendMode(BLEND);
}
//makes starfish
function starfish(x,y, color, spin){
push();
translate(x,y);
rotate(radians(spin));
strokeWeight(4);
stroke(color);
for (var i = 0; i<5; i++){ //draw five arms from 0,0, rotating after each one
line(0,0,0,-10);
rotate(radians(72));
}
noStroke();
pop();
}
//makes seashells
function shell(x,y, color1, color2, spin) {
push();
translate(x,y);
rotate(radians(spin));
strokeWeight(1);
stroke(color1);
fill(color2);
var radius = 7; //radius of seashell, ie distance from circles to center
var size = 12; //size of circles themselves
var angle = radians(0); //theta of circle location
for (var i = 0; i<20; i++) { //draw circles that get smaller as they get closer to the center
ellipse ( cos(angle)*radius, sin(angle)*radius, size);
angle-=radians(24);
size -= i/20; //decrease size of circles
radius -= i/30; //decrease radius of seashell
}
noStroke();
pop();
}
//makes a bunch of grass
function grass(x,y){
push();
translate(x,y);
stroke(65, 100, 52);
strokeWeight(2);
rotate(radians(-40));
for (var i = 0; i < 20; i++){ //creating each blade of grass in a bunch
for (var j = 0; j < 20; j++){ //creating random lengths for each blade of grass
grasslength[j] = random(35, 40);
}
line(0,0,0, -1*grasslength[i]);
rotate(radians(4));
}
pop();
}
I find Paul Dunham’s installation Click::RAND to be fascinating. It’s based on the book A Million Random Digits with 100,000 Normal Deviates, which was published by the RAND corporation in 1955 to allow computer programmers to have an extensive amount of truly random numbers on hand. (The numbers themselves were generated by a program designed to work as a roulette wheel.) The book was available in standard print, but also as computer punchcards, and it’s the latter version that Dunham was inspired by. He created “instruments” by wiring together a grid of old-fashioned electromagnetic relays that make an audible click when they open and close, then feeding them the random numbers provided by the punchcards as instructions on when to move. The result is an audible experience of randomness, with ephemeral patterns seemingly flashing in and out of the composition.
The listening experience seems to say something about how we as humans tend to try to impose order on our surroundings. Because of the way our neural networks work, we aren’t capable of thinking in a truly random way, and we have an inherent tendency to seek patterns. So, it’s an interesting experience to listen to something random and watch your brain spin itself out looking for patterns that actually aren’t there.
Here is a video of Click::RAND in action. Scroll to about halfway in to see it in all its glory: