## Timothy Liu — Final Project

``````// Timothy Liu
// 15-104, Section C
// tcliu@andrew.cmu.edu
// FINAL PROJECT: Crossy Chicken, the game!

var showStart = true; // start off the game showing the start screen

var myChar; // the player (the chicken)
var charSpeed = 2; // speed player moves at

var borderW = 40; // border used in all popup screens

var firstCars = []; // arrays of cars (from bottom of screen to top)
var secondCars = [];
var thirdCars = [];
var fourthCars = [];

var carSpacing;

// river and bridge characteristics
var riverStart = 243; // identifies the top of the river
var riverWidth = 100; // identifies the width of the river
var riverEnd = riverStart + riverWidth; // identifies the bottom of the river
var bridgeWidth = 30; // width of the bridge
var bridgeMiddle = riverStart + riverWidth / 2; // the top part of the middle bridge segment
var randomBridgeStart; // where the first segment of the bridge starts
var randomBridgeLength; // how long the middle segment of the bridge is

// variables that help draw/locate the egg and nest
var eggLocY = 50;
var eggW = 12;
var eggH = 16;
var nestLocY = 60;
var nestW = 30;
var nestH = 18;

// sound
var soundtrack;
var winSound;
var loseSound;

// background track
soundtrack.setVolume(0.5);

// losing jingle
loseSound.setVolume(0.6);

// winning jingle
winSound.setVolume(0.9);

}

function setup() {

createCanvas(600, 600); // create the canvas

soundtrack.play(); // play the frogger soundtrack that's been preloaded

myChar = makeCharacter(); // makes the character

carSpacing = random(30, 45); // determines spacing between cars; ensures they don't hit each other (but varies the spacing randomly)

randomBridgeStart = random(50, 500); // determines the random location of the bridge
randomBridgeLength = random(60, 250); // determines the random length of the bridge

firstCarSetup(); // defines properties for all the different rows of cars
secondCarSetup(); // second row of cars (2nd from bottom)
thirdCarSetup(); // third row of cars (3rd from bottom)
fourthCarSetup(); // top row of cars

}

function draw() {

background(87, 168, 84); // grass floor

noStroke();

drawRiver(); // draw in the river

drawNest(); // draw in the nest

// draw in and move the character!
myChar.draw();
myChar.move();

// draws in the cars on the road using drawCars()
showFirstCars();
showSecondCars();
showThirdCars();
showFourthCars();

characterWins(); // function that runs when the character reaches the egg and wins
characterLoses(); // function that identifies when the character loses

startScreen(); // starting screen shows right at the beginning

}

// the starting screen that shows rules and game info
function startScreen() {

// right at start of game, this statement is true UNTIL SPACE IS PRESSED
if (showStart === true) {

noLoop();

// the frame/popup screen
fill(235, 131, 131);
rect(width / 6, height / 6, width * 2 / 3, height * 2 / 3);

fill(255);
rect(width / 6 + borderW / 2, height / 6 + borderW / 2, width * 2 / 3 - borderW, height * 2 / 3 - borderW);

// Start screen
fill(0);
textSize(20);

textAlign(CENTER); // center the text
textFont('Avenir'); // text font

// starting screen text: the intro!
textStyle(NORMAL);
text("Welcome to Crossy Chicken!", width / 2, 225);
text("Watch out for cars, and", width / 2, 255);
text("don't let your feet get wet!", width / 2, 285);

text("Use the WASD keys to move, and", width / 2, 345);
textStyle(BOLD);
text("press SPACE to get started!", width / 2, 375);

}

}

// triggers to start and reset the game
function keyPressed() {

if (keyCode === 32) { // if SPACEBAR is pressed, let the game begin!
showStart = false;
loop();
}
if (keyCode === ENTER) { // if ENTER is pressed, the level restarts!
restart(); // restart function resets the stage
}

return false; // prevents the webpage from moving when arrows are pressed

}

// this function initializes all variables and then reruns the setup function, effectively restarting the game
function restart() {

firstCars = []; // initialize all of the car arrays so they can be redrawn/setup
secondCars = [];
thirdCars = [];
fourthCars = [];

setup(); // re-run setup, which resets the stage
loop(); // start running the code again!

}

// define all character properties (the chicken)
function makeCharacter() {

return {
x: 300,
y: 550,
c: 255,
w: 25,
h: 40,
r: 25,
hX: 300,
hY: 490,
hW: 15,
hH: 20,
eyeC: 0,
eyeW: 2,
beakC: color(252, 202, 3),
crownW: 5,
crownH: 10,
crownC: color(207, 58, 58),
footW: 7,
footH: 4,
draw: drawCharacter,
move: moveCharacter
}

}

// uses the obj characteristics and var myChar to draw the chicken
function drawCharacter() {

var eyeOffsetX = 3;
var eyeOffsetY = 21;
var beakOffsetX = 4;
var beakOffsetY = 19;
var beakOffsetY2 = 15;
var crownOffsetY = 27;
var footOffset = 5;

fill(myChar.crownC);
arc(myChar.x, myChar.y - crownOffsetY, myChar.crownW, myChar.crownH, PI, TWO_PI);

// feet
fill(myChar.beakC);
arc(myChar.x - footOffset, myChar.y, myChar.footW, myChar.footH, 0, PI);
arc(myChar.x + footOffset, myChar.y, myChar.footW, myChar.footH, 0, PI);

fill(myChar.c);
arc(myChar.x, myChar.y, myChar.w, myChar.h, PI, TWO_PI);
arc(myChar.x, myChar.y - headOffset, myChar.hW, myChar.hH, PI, TWO_PI);

// eyes
fill(myChar.eyeC);
ellipse(myChar.x - eyeOffsetX, myChar.y - eyeOffsetY, myChar.eyeW, myChar.eyeW);
ellipse(myChar.x + eyeOffsetX, myChar.y - eyeOffsetY, myChar.eyeW, myChar.eyeW);

// beak
fill(myChar.beakC);
triangle(myChar.x - beakOffsetX, myChar.y - beakOffsetY, myChar.x + beakOffsetX, myChar.y - beakOffsetY,
myChar.x, myChar.y - beakOffsetY2);

}

// this function allows the character to move
function moveCharacter() {

if (keyIsDown(65)) { // if pressing the A key
myChar.x -= charSpeed; // move the character's x position left
if (myChar.x + myChar.r < 0) { // if the character moves off the screen, have it wrap to the other side
myChar.x = width + myChar.r;
}
}

if (keyIsDown(68)) { // if pressing the D key
myChar.x += charSpeed; // move the character's x position right
if (myChar.x - myChar.r > width) { // if the character moves off the screen, have it wrap to the other side
myChar.x = -myChar.r;
}
}

if (keyIsDown(87)) { // if pressing the W key
myChar.y -= charSpeed; // move the character's y position up
}

if (keyIsDown(83)) { // if pressing the S key
myChar.y += charSpeed; // move the character's y position down
if (myChar.y + myChar.r > height) { // if it hits the bottom, don't let it go off the page
myChar.y = height - myChar.r;
}
}

}

// what happens if the character reaches the egg
function characterWins() {

// if the character reaches the egg successfully
if (myChar.y > eggLocY - eggH / 2 & myChar.y < nestLocY + nestH / 2) {
if (myChar.x > width / 2 - nestW / 2 && myChar.x < width / 2 + nestW / 2) {
noLoop(); // game stops
winningScreen(); // display winning screen
winSound.play(); // play winning jingle!
}
}

}

// this function determines if the character loses using two other functions: hit by car, or fall in river
function characterLoses() {

characterHitByCar(); // function if myChar is hit by a car
characterFallsInRiver(); // function if myChar falls into the river

}

// if the character is hit by a car
function characterHitByCar() {

// if char is hit by first row of cars
for (var i = 0; i < firstCars.length; i++) {

var carMiddleX = firstCars[i].x + (firstCars[i].w / 2);
var carMiddleY = firstCars[i].y + (firstCars[i].h / 2);

var charMiddleX = myChar.x;
var charMiddleY = myChar.y - (myChar.h / 2);

// if the edges of the character fall within the edges of the car, you lose!
if (charMiddleX - myChar.w / 4 < carMiddleX + firstCars[i].w / 2 & charMiddleX + myChar.w / 4 > carMiddleX - firstCars[i].w / 2
&& myChar.y > carMiddleY - firstCars[i].h / 2 && charMiddleY - myChar.h / 5 < carMiddleY + firstCars[i].h / 2) {
noLoop(); // game stops
losingScreenCar(); // show losing screen
loseSound.play(); // losing sound plays
}

}

// if char is hit by second row of cars
for (var j = 0; j < secondCars.length; j++) {

var carMiddleX = secondCars[j].x + (secondCars[j].w / 2);
var carMiddleY = secondCars[j].y + (secondCars[j].h / 2);

var charMiddleX = myChar.x;
var charMiddleY = myChar.y - (myChar.h / 2);

// if the edges of the character fall within the edges of the car, you lose!
if (charMiddleX - myChar.w / 4 < carMiddleX + secondCars[j].w / 2 & charMiddleX + myChar.w / 4 > carMiddleX - secondCars[j].w / 2
&& myChar.y > carMiddleY - secondCars[j].h / 2 && charMiddleY - myChar.h / 5 < carMiddleY + secondCars[j].h / 2) {
noLoop(); // game stops
losingScreenCar(); // show losing screen
loseSound.play(); // losing sound plays
}

}

// if char is hit by third row of cars
for (var k = 0; k < thirdCars.length; k++) {

var carMiddleX = thirdCars[k].x + (thirdCars[k].w / 2);
var carMiddleY = thirdCars[k].y + (thirdCars[k].h / 2);

var charMiddleX = myChar.x;
var charMiddleY = myChar.y - (myChar.h / 2);

// if the edges of the character fall within the edges of the car, you lose!
if (charMiddleX - myChar.w / 4 < carMiddleX + thirdCars[k].w / 2 & charMiddleX + myChar.w / 4 > carMiddleX - thirdCars[k].w / 2
&& myChar.y > carMiddleY - thirdCars[k].h / 2 && charMiddleY - myChar.h / 5 < carMiddleY + thirdCars[k].h / 2) {
noLoop(); // game stops
losingScreenCar(); // show losing screen
loseSound.play(); // losing sound plays
}

}

// if char is hit by fourth row of cars
for (var l = 0; l < fourthCars.length; l++) {

var carMiddleX = fourthCars[l].x + (fourthCars[l].w / 2);
var carMiddleY = fourthCars[l].y + (fourthCars[l].h / 2);

var charMiddleX = myChar.x;
var charMiddleY = myChar.y - (myChar.h / 2);

// if the edges of the character fall within the edges of the car, you lose!
if (charMiddleX - myChar.w / 4 < carMiddleX + fourthCars[l].w / 2 & charMiddleX + myChar.w / 4 > carMiddleX - fourthCars[l].w / 2
&& myChar.y > carMiddleY - fourthCars[l].h / 2 && charMiddleY - myChar.h / 5 < carMiddleY + fourthCars[l].h / 2) {
noLoop(); // game stops
losingScreenCar(); // show losing screen (car version)
loseSound.play(); // losing sound plays
}
}

}

// if the strays from the bridge and falls into the river
function characterFallsInRiver() {

// if the character is crossing the river...
if (myChar.y > riverStart & myChar.y < riverEnd) {

// first segment of the bridge (vertical)
if (myChar.y > bridgeMiddle + bridgeWidth && myChar.y < riverEnd) {

// if char is outside the bridge boundaries
if (myChar.x < randomBridgeStart || myChar.x > randomBridgeStart + bridgeWidth) {
noLoop(); // game stops
losingScreenRiver(); // show losing screen (splash version)
loseSound.play(); // play losing sound
}
}

// the bridge randomly goes left or right based on starting position...
// if the bridge goes rightward:
if (randomBridgeStart < width / 2) {

// second segment of the bridge (horizontal)
if (myChar.y > bridgeMiddle & myChar.y < bridgeMiddle + bridgeWidth) {
// if char is outside the boundaries of the horizontal bridge segment
if (myChar.x < randomBridgeStart || myChar.x > randomBridgeStart + randomBridgeLength + bridgeWidth) {
noLoop(); // game stops
losingScreenRiver(); // show losing screen (splash version)
loseSound.play(); // play losing sound
}
}

// last segment of the bridge (vertical)
if (myChar.y > riverStart & myChar.y < bridgeMiddle) {
if (myChar.x < randomBridgeStart + randomBridgeLength || myChar.x > randomBridgeStart + randomBridgeLength + bridgeWidth) {
noLoop(); // game stops
losingScreenRiver(); // show losing screen (splash version)
loseSound.play(); // play losing sound
}
}

// if the bridge goes leftward:
} else {

// second segment of the bridge (horizontal)
if (myChar.y > bridgeMiddle & myChar.y < bridgeMiddle + bridgeWidth) {
if (myChar.x < randomBridgeStart - randomBridgeLength || myChar.x > randomBridgeStart + bridgeWidth) {
noLoop(); // game stops
losingScreenRiver(); // show losing screen (splash version)
loseSound.play(); // play losing sound
}
}

// last segment of the bridge (vertical)
if (myChar.y > riverStart & myChar.y < bridgeMiddle) {
if (myChar.x < randomBridgeStart - randomBridgeLength || myChar.x > randomBridgeStart - randomBridgeLength + bridgeWidth) {
noLoop(); // game stops
losingScreenRiver(); // show losing screen (splash version)
loseSound.play(); // play losing sound
}
}
}
}
}

// the popup screen when you win
function winningScreen() {

soundtrack.stop(); // stop background sound

// border + screen
fill(255, 213, 0);
rect(width / 4, height / 4, width / 2, height / 2);

fill(255);
rect(width / 4 + borderW / 2, height / 4 + borderW / 2, width / 2 - borderW, height / 2 - borderW);

// winning screen text for when you make it to the egg
fill(0);
textSize(20);
textAlign(CENTER); // center the text
textFont('Avenir'); // text font

// text:
textStyle(NORMAL);
text("You protected your egg;", width / 2, 270);
textStyle(BOLD);
text("Congratulations!", width / 2, 300);
textStyle(NORMAL);
text("Hit ENTER to play again!", width / 2, 330);

}

// the popup screen when you're hit by a car
function losingScreenCar() {

soundtrack.stop();

fill(235, 131, 131);
rect(width / 4, height / 4, width / 2, height / 2);

fill(255);
rect(width / 4 + borderW / 2, height / 4 + borderW / 2, width / 2 - borderW, height / 2 - borderW);

// losing screen text for when a car runs into you
fill(0);
textSize(20);
textAlign(CENTER); // center the text
textFont('Avenir'); // text font

// text:
textStyle(NORMAL);
text("Oh no, a car got you!", width / 2, 285);
text("Hit ENTER to try again!", width / 2, 315);

}

// the popup screen when you fall into the river
function losingScreenRiver() {

soundtrack.stop();

fill(53, 99, 240);
rect(width / 4, height / 4, width / 2, height / 2);

fill(255);
rect(width / 4 + borderW / 2, height / 4 + borderW / 2, width / 2 - borderW, height / 2 - borderW);

// losing screen text for when you fall into the water
fill(0);
textSize(20);

textAlign(CENTER); // center the text
textFont('Avenir'); // text font

// text:
textStyle(BOLD); // bold the sound effect (splash)
text("Splash...", width / 2, 270);
textStyle(NORMAL);
text("You fell into the water!", width / 2, 300);
text("Hit ENTER to try again!", width / 2, 330);

}

// draw in the bottom (first) road

var lineLocY = 434;

fill(180);

fill(245, 224, 66);

}

// draw in the top (second) road

var lineLocY = 149;

fill(180);

fill(245, 224, 66);

}

// draw in the river
function drawRiver() {

var riverEdgeL = 0;

fill(100, 100, 255); // blue color
rect(riverEdgeL, riverStart, width, riverWidth);

drawBridge(); // draw in bridge using bridge function

}

// draws the bridge the crosses the river. the bridge is randomly generated each time the game is played!
// it will go rightward if the starting point is on the left of the screen, and leftward if the starting point is to the right
function drawBridge() {

fill(186, 145, 104); // brown bridge color

rect(randomBridgeStart, bridgeMiddle, bridgeWidth, riverWidth / 2); // first bridge segment

// this if-else statement determines which direction the bridge goes in
if (randomBridgeStart < width / 2) {
rect(randomBridgeStart, bridgeMiddle, randomBridgeLength, bridgeWidth); // second bridge segment
rect(randomBridgeStart + randomBridgeLength, riverStart, bridgeWidth, riverWidth / 2 + bridgeWidth); // final bridge segment
} else {
rect(randomBridgeStart - randomBridgeLength, bridgeMiddle, randomBridgeLength, bridgeWidth); // second bridge segment
rect(randomBridgeStart - randomBridgeLength, riverStart, bridgeWidth, riverWidth / 2 + bridgeWidth); // final bridge segment
}

}

// characteristics of the car objects
function makeCars(px, py) {
return {
x: px,
y: py,
c: color(random(255), random(255), random(255)),
w: 50,
h: 30,
lightsW: 2,
lightsH: 5,
lightsC: color(242, 235, 19),
tireW: 9,
tireH: 3,
tireC: color(0, 0, 0),
windshieldW: 4,
windshieldH: 25,
windshieldC: color(100, 100, 100),
draw: drawCars
}
}

// uses the obj characteristics to draw cars
function drawCars() {

var tireOffsetX = 6;
var tireOffsetY = 2;
var windshieldOffsetX = 10;
var windshieldOffsetY = 2.5;

// car body
fill(this.c);
rect(this.x, this.y, this.w, this.h);

fill(this.lightsC);
rect(this.x + this.w - this.lightsW, this.y, this.lightsW, this.lightsH);
rect(this.x + this.w - this.lightsW, this.y + this.h - this.lightsH, this.lightsW, this.lightsH);
rect(this.x, this.y, this.lightsW, this.lightsH);
rect(this.x, this.y + this.h - this.lightsH, this.lightsW, this.lightsH);

// car tires
fill(this.tireC);
rect(this.x + this.w - (2.5 * tireOffsetX), this.y - tireOffsetY, this.tireW, this.tireH);
rect(this.x + tireOffsetX, this.y - tireOffsetY, this.tireW, this.tireH);
rect(this.x + this.w - (2.5 * tireOffsetX), this.y + this.h - (tireOffsetY / 2), this.tireW, this.tireH);
rect(this.x + tireOffsetX, this.y + this.h - (tireOffsetY / 2), this.tireW, this.tireH);

// car windshields
fill(this.windshieldC);
rect(this.x + (3.5 * windshieldOffsetX), this.y + windshieldOffsetY, this.windshieldW, this.windshieldH);
rect(this.x + (1.2 * windshieldOffsetX), this.y + windshieldOffsetY, this.windshieldW, this.windshieldH);

if (this.x > width) {
this.x = -this.w;
}

if (this.x + this.w < 0) {
this.x = width;
}
}

// draws the nest using nest variables
function drawNest() {

// the nest
fill(219, 181, 75);
ellipse(width / 2, nestLocY, nestW * 1.3, nestH);

fill(163, 129, 34);
ellipse(width / 2, nestShadowY, nestW, nestH / 2);

// the egg
fill(250, 242, 220);
ellipse(width / 2, eggLocY, eggW, eggH);

}

// setting up the first row of cars and placing them (very bottom row of cars)
function firstCarSetup() {

var carPlacement; // car placement variable
var numCars = 5; // number of cars in the first row of cars
var carLocY = 445;

var newCar = makeCars();
firstCars.push(newCar); // add each new car to the empty array firstCars

// place each car in the first row of cars and add to the array firstCars
for (var i = 0; i < numCars; i++) {
carPlacement = random((i * width / numCars) + carSpacing, ((i + 1) * width / numCars) - carSpacing);
firstCars[i] = makeCars(carPlacement, carLocY); // placement of the first row of cars
}

}

// setting up the second row of cars and placing them
function secondCarSetup() {

var carPlacement; // car placement variable
var numCars = 5; // number of cars in the first row of cars
var carLocY = 395;

var newCar = makeCars();
secondCars.push(newCar); // add each new car to the empty array secondCars

// place each car in the second row of cars and add to the array secondCars
for (var i = 0; i < numCars; i++) {
carPlacement = random((i * width / numCars) + carSpacing, ((i + 1) * width / numCars) - carSpacing);
secondCars[i] = makeCars(carPlacement, carLocY);
}

}

// setting up the third row of cars and placing them
function thirdCarSetup() {

var carPlacement; // car placement variable
var numCars = 5; // number of cars in the first row of cars
var carLocY = 160;

var newCar = makeCars();
thirdCars.push(newCar); // add each new car to the empty array secondCars

// place each car in the third row of cars and add to the array secondCars
for (var i = 0; i < numCars; i++) {
carPlacement = random((i * width / numCars) + carSpacing, ((i + 1) * width / numCars) - carSpacing);
thirdCars[i] = makeCars(carPlacement, carLocY);
}

}

// setting up the fourth row of cars and placing them
function fourthCarSetup() {

var carPlacement; // car placement variable
var numCars = 5; // number of cars in the first row of cars
var carLocY = 110;

var newCar = makeCars();
fourthCars.push(newCar); // add each new car to the empty array secondCars

// place each car in the fourth row of cars and add to the array secondCars
for (var i = 0; i < numCars; i++) {
carPlacement = random((i * width / numCars) + carSpacing, ((i + 1) * width / numCars) - carSpacing);
fourthCars[i] = makeCars(carPlacement, carLocY);
}

}

// draws the first row of cars (closest to the bottom of the screen)
function showFirstCars() {

for (var i = 0; i < firstCars.length; i++) {
firstCars[i].draw(); // draws in cars
firstCars[i].x += 1; // makes the cars drive rightward on the first road (bottom)
}

}

// draws the second row of cars (next row up from bottom of the screen)
function showSecondCars() {

for (var i = 0; i < secondCars.length; i++) {
secondCars[i].draw();
secondCars[i].x -= 1; // makes the cars move in the opposite direction (leftward) on the first road
}

}

// draws the third row of cars (next row up from bottom of the screen)
function showThirdCars() {

for (var i = 0; i < thirdCars.length; i++) {
thirdCars[i].draw();
thirdCars[i].x += 1.8; // faster cars to increase the difficulty (rightward, top road)!
}

}

// draws the fourth row of cars (row of cars near top of screen)
function showFourthCars() {

for (var i = 0; i < fourthCars.length; i++) {
fourthCars[i].draw();
fourthCars[i].x -= 1.8; // faster cars in opposite direction to increase the difficulty (leftward, top road)!
}

}``````

Welcome to Crossy Chicken, my final project for 15-104! A tribute to the original Frogger, Crossy Chicken involves a chicken trying to reach her beloved egg. But to get there, the chicken must first cross two busy streets as well as a perilous bridge; can you get her back to her egg?

To play, use the WASD keys (W = Up, S = Down, A = Left, D = Right) to direct the chicken and follow the instructions on screen! Make sure you dodge all the cars and don’t let the chicken’s feet touch the water!

I had a fantastic time coding this project, and I’m proud of how I distributed the work. I broke my project down into phases, and I worked through them in the following order:

1. Character movement
2. Introducing cars
4. Creating the river and bridge
5. Adding the next 3 rows of cars
6. Losing screen
7. Winning screen
8. Reset button (ENTER)
9. Start button (SPACE)

In the end, I wound up with what I felt was a very polished and complete game. I’m really happy that I was able to build a visually pleasing game with fun and retro graphics while using a multitude of elements from p5js. From objects, to loops, to sound, my game really has a bit of everything in it. The soundtrack (which is actually the original Frogger music!) adds an element of excitement and nostalgia, and the losing and winning sound effects complete the experience for the gamer. I hope you have a great time playing!

(*Note 2: as is typically the case with p5js, the sound is sometimes a bit glitchy on Chrome and certain other browsers. However, the implementation of sound is all correct, as can be seen in my code.)

## Timothy Liu — Project 12 — Proposal

For my final project, I want to build an homage to Frogger. I’d like to try programming a game that involves a character trying to reach the end of the stage by making it through a series of moving obstacles and objects. This is an idea that really appeals to me, because

1. I haven’t had the chance to try coding a game in this class and I think it’d be a really fun challenge to take on, and
2. I’m excited to play around with the graphics, design, and gameplay in this project!

I’ve come up with a few ideas for gameplay and graphics which mainly involve animal characters moving across a terrain filled with natural obstacles. For instance, a penguin waddling across snow while dodging moving snowballs to try to make it to an igloo is something I’d love to build.

From a functional standpoint, my ultimate goals for this project are to build a smooth, well-oiled game with a soundtrack and irresistible charm. I want the arrow keys to direct the character’s motion and for the movement of the obstacles to feel natural, not mechanistic.

In terms of art-style/design, I really admire video games that incorporate cel-shading and minimalism. I hope to utilize color palettes and shapes to establish a calming, consistent feel with my game, as I think it would add to the desired natural ambience.

I can’t wait to get started!

## Timothy Liu — Looking Outwards — 12

It’s final project time! When I read the prompt for the final project, I immediately knew I wanted to do something related to a game. As referenced in my last 2 Looking Outwards, I love video games and I think it would be really cool to try and build a game experience through p5.js. A game that came to mind was Frogger, a classic concept where a character must cross a road with incoming traffic to reach the other side. For this Looking Outwards, I looked at two examples of Frogger-style games: the classic Frogger on the C64, and Crossy Road on the iPhone.

The original Frogger is a clear flash-to-the-past, with insanely retro graphics and color schemes. Frogger was released in 1981 by Konami, and it was an instant hit; players loved the simplicity of the game as well as its addictive appeal. The premise is straightforward: the player, playing as a frog, must weave through traffic and jump across a variety of animal-backs and logs to reach safety on the other side of the screen. The player then goes through a series of levels to try and set their high score without losing all of their lives. Frogger’s simplicity was part of its charm, but I personally felt their was a level of animated complexity lacking; of course, this was likely because of the datedness of the game (1981 was a while ago, and computers have come a long way since!).

Crossy Road, released in 2014 by the gaming studio Hipster Whale, is an attempt to fix this issue by adding in flashy new graphics, fun animations, and 3D characters. It’s modeled with block/pixel-style characters, seemingly a reference to the game’s historical roots, but it’s clear after playing Crossy Road that gameplay is immensely smoother and more flowy. I’ve played Crossy Road in the past, and it was a great experience. The game was fun, and losing doesn’t even feel bad because of the charm of the game. However, one thing I felt could be improved on is the simplicity of the game; in a weird way, Crossy Road almost does too much to the original frogger to the point where the various rocks, trees, cars, and rivers the player needs to avoid become almost distracting. There’s something sophisticatedly simple about the original Frogger that reduces gameplay distractions, and that’s something I think Crossy Road misses at times.

## Timothy Liu — Project 11 — Landscape

``````// Timothy Liu
// 15-104, Section C
// tcliu@andrew.cmu.edu
// OpenProject-11

var cowArray = []; // array for all cows
var birdArray = []; // array for all birds

// variables to control the shape and speed of the rows of trees
var backTreesStructure = 0.04;
var backTreesSpeed = 0.0009;
var frontTreesStructure = 0.04;
var frontTreesSpeed = 0.0009;

// hill in the front
var rollingPlain = 0.005;
var rollingPlainSpeed = 0.0001;

function setup() {

createCanvas(600, 240);

for (var i = 0; i < 3; i++){
var cowPlacement = random(0, width);
cowArray[i] = cowObj(cowPlacement); // places the 3 cows in random locations

}

for (var j = 0; j < 5; j++) {
birdArray.push(birdObj(j * 30 + random(40, width - 40),
random(20, 60), random(10, 25))); // gives the birds random x, y, and size
}

frameRate(40);

}

function draw() {

background(124, 196, 230); // sky color

drawTrees(); // draw the two rows of trees first (in the background)

updateAndShowCows(); // draw and have the cows move

updateCowsOffScreen(); // account for when cows move off screen

updateAndShowBirds(); // draw and have the birds move

updateBirdsOffScreen(); // account for when birds move off screen

newCows(); // draws in new cows (from edge of screen)

newBirds(); // draws in new birds (from edge of screen)
}

function drawTrees() {

// the back (darker) row of trees
beginShape();
fill(40, 120, 61);
noStroke();
vertex(0, height);

// use noise function to draw random tree line
for (var x = 0; x < width; x++) {
var t = (x * backTreesStructure) + (millis() * backTreesSpeed);
var y = map(noise(t), 0, 1, 40, 150);
vertex(x, y);
}
vertex(width, height);
endShape();

// the front (lighter) row of trees
beginShape();
fill(50, 168, 82);
noStroke();
vertex(0, height);

//use noise function to draw random, lower tree line to create depth
for (var x = 0; x < width; x++) {
var t = (x * frontTreesStructure) + (millis() * frontTreesSpeed);
var y = map(noise(t * 0.7), 0, 1, 80, 200);
vertex(x, y);
}
vertex(width, height);
endShape();

// the light-green hills in front
beginShape();
fill(154, 196, 118);
noStroke();
vertex(0, height);

// use a dampened version of the noise function to make slowly rolling hills
for (var x = 0; x < width; x++) {
var t = (x * rollingPlain) + (millis() * rollingPlainSpeed);
var y = map(noise(t * 0.5), 0, 1, 170, 200);
vertex(x, y);
}
vertex(width, height);
endShape();

}

// puts the cows on the canvas and makes them move
function updateAndShowCows(){

for (var i = 0; i < cowArray.length; i++){

cowArray[i].move(); // move property of the cow obj
cowArray[i].show(); // show property of the bird obj

}
}

// puts the birds on the canvas and makes them move
function updateAndShowBirds(){

for (var i = 0; i < birdArray.length; i++){

birdArray[i].moveB(); // move property of the bird obj
birdArray[i].showB(); // show property of the bird obj

}
}

// eliminates the cows that move off screen
function updateCowsOffScreen(){

var cowsToKeep = []; // to determine which cows are still on screen

for (var i = 0; i < cowArray.length; i++) {
if (cowArray[i].x + cowArray[i].cowWidth > 0) { // if the cow is still on the screen
cowsToKeep.push(cowArray[i]); // put it into the new array
}
}

// cowsToKeep[] becomes the new array of cows currently on screen
cowArray = cowsToKeep;
}

// eliminates the birds that move off screen
function updateBirdsOffScreen(){

var birdsToKeep = []; // to determine which birds are still on screen

for (var i = 0; i < birdArray.length; i++) {
if (birdArray[i].bx + birdArray[i].sizeOfBird > 0) { // if the bird is still on the screen
birdsToKeep.push(birdArray[i]); // put it into the new array
}
}

// birdsToKeep[] becomes the new array of birds currently on screen
birdArray = birdsToKeep;
}

// occasionally, add a new cow to the end of the screen
function newCows() {
var newCowOdds = 0.008;
if (random(0, 1) < newCowOdds) {
cowArray.push(cowObj(width)); // add a new cow to the array
}
}

// occasionally, add a new bird to the end of the screen
function newBirds() {
var newBirdOdds = 0.008;
if (random(0, 1) < newBirdOdds) {
birdArray.push(birdObj(width, random(20, 50), random(10, 25))); // add a new bird to the array
}
}

// makes the cows move
function movingCows() {
this.x += this.speed;
}

// makes the birds move
function movingBirds() {
this.bx += this.speedBird;
}

// this is what actually draws in, and displays, the cows using various cow object properties
function showCows() {

var cowPlacement = this.randomLoc;
var earOffset = 7;
var eyeOffset = 10;
var spot2Offset = 5;
var spot3Offset = 4;
var snoutOffset = 6;

push();

translate(this.x, height); // places the cows onto the canvas

noStroke();
fill(7, 66, 25);
ellipse(0, -cowPlacement + 10, this.cowWidth, this.cowHeight / 4);

// cow body
fill(this.randomColor);
arc(0, -cowPlacement, this.cowWidth, this.cowHeight * 0.75, 2 * PI / 3, PI / 3, CHORD);

// cow ears
arc(-headOffset - earOffset, -cowPlacement - headOffset, this.earW, this.earW * 0.75, 4 * PI / 3, PI / 3, CHORD);
arc(-headOffset + earOffset, -cowPlacement - headOffset, this.earW, this.earW * 0.75, 2 * PI / 3, 5 * PI / 3, CHORD);

// three spots
fill(0);
ellipse(this.spot1, -cowPlacement, this.spotW, this.spotH);
ellipse(this.spot2, -cowPlacement + spot2Offset, this.spotW * 1.2, this.spotH);
ellipse(this.spot3, -cowPlacement - spot3Offset, this.spotW, this.spotH * 1.2);

// eyes
ellipse(-18, -cowPlacement - eyeOffset, this.eye, this.eye * 1.2);
ellipse(-12, -cowPlacement - eyeOffset, this.eye, this.eye * 1.2);

// snout
fill("#eba4dd");
ellipse(-15, -(cowPlacement + snoutOffset), this.snout * 1.6, this.snout);

pop();

}

// this is what actually draws in, and displays, the birds using various bird object properties
function showBirds() {

var birdBod = 5; // body size of bird

noFill();
stroke(0);
strokeWeight(2);

push();

// places and sizes the birds
translate(this.bx, this.by);
scale(this.sizeOfBird / 60);
ellipseMode(CENTER);

// uses arcs and ellipses to draw the wings and body
arc(35, 35, 100, 100, 5 * PI / 4, 3 * PI / 2);
arc(-35, 35, 100, 100, 3 * PI / 2, 7 * PI / 4);
fill(0);
ellipse(0, 0, birdBod, birdBod);

pop();

}

// here are all the properties of the cow object
function cowObj(x) {

let colors = ["#8f6846", "#ffffff", "#918880"] // different colors of cows

// cow obj properties
var cow = {x: x,
cowWidth: random(30, 40), // each cow has a diff size and shape
cowHeight: random(25, 40),
snout: 7,
spot1: random(-12, -8),
spot2: random(1, 5),
spot3: random(5, 10),
eye: 3,
earW: 8,
spotW: 8,
spotH: 6,
speed: random(-3, -1), // each cow moves at a diff speed
move: movingCows,
show: showCows,
randomLoc: random(35, 60),
randomColor: random(colors), // each cow has a different color
}

return cow;

}

// here are all the properties of the bird object
function birdObj(x, y, size) {

// bird obj properties
var bird = {bx: x,
by: y,
sizeOfBird: size, // each bird has a random size
speedBird: random(-4, -1), // each bird has a random speed
showB: showBirds,
moveB: movingBirds
};

return bird;

}
``````

When I thought about the “generative landscape” prompt for this week’s assignment, one of the first things that came to mind was the scenery during the long road trips my family takes every year from the Bay Area to Southern California. I distinctly remember staring out the window as a kid, watching cows idly graze in fields and the rolling trees and hills zoom by as we drove down I-5. Thus, I wanted to model my project after this landscape because it’s a very fond childhood memory of mine.

In my generative landscape, I have cows of all sorts of shapes, colors, and sizes on grassy plains as the screen pans leftward. In the background, rows of trees and grassy hills roll by, and birds fly overhead at varying speeds. My piece is meant to represent an idyllic view of what a kid sees when they stare out the window on a road trip through California’s central valley. The blue sky, the flocking birds, and the vast greenery all convey warmth and happiness. Ultimately, I think it’s the motion and dynamism of my piece that really help capture the feeling of blissfulness.

## Timothy Liu — Project 10 — Sonic Sketch

Click on a drum to hear its sound!

``````// Timothy Liu
// 15-104, Section C
// tcliu@andrew.cmu.edu
// OpenEnded-10

// bass drum variables
var bassDrumW = 200;
var bassDrumX;
var bassDrumY;
var rimWidth = 20;
var bassRadius = bassDrumW / 2;

// high tom tom variables
var tomTomW = 100;
var tomTomH = 40;
var tomTomBaseW = 100;
var tomTomBaseH = 50;
var tomTomX1 = 220;
var tomTomY1 = 200;
var tomTomX2 = 380;
var tomTomY2 = 200;
var baseOffset = tomTomW / 2;
var tomTomRodW = 5;
var tomTomRodH = 250;
var httRodOffset = tomTomRodW / 2;

// floor tom tom variables
var fTomTomW = 150;
var fTomTomH = 60;
var fTomTomBaseW = 150;
var fTomTomBaseH = 100;
var fTomTomX = 135;
var fTomTomY = 300;
var fBaseOffsetX = fTomTomW / 2;
var fBaseOffsetY = 100;
var fTomTomRodW = 8;
var fTomTomRodH = 100;
var fRodOffset = fTomTomRodW / 2;

// snare drum
var snareW = 120;
var snareH = 40;
var snareBaseW = 120;
var snareBaseH = 30;
var snareX = 450;
var snareY = 275;
var snareOffsetX = snareW / 2;
var snareOffsetY = snareBaseH;
var snareRodW = 8;
var snareRodH = 200;
var snareRodOffset = snareRodW / 2;

// cymbals
var cymbalX1 = [100, 130];
var cymbalY1 = [30, 150];
var cymbalZ1 = [170, 150];
var cymbalX2 = [500, 130];
var cymbalY2 = [570, 150];
var cymbalZ2 = [430, 150];
var cymbalRodH = 350;
var cymbalRodW = 4;
var cymbalOffset = cymbalRodW / 2;
var cymbalH = 20;

// variables for sounds
var hTTDSound;
var FTTDSound;
var bassDSound;
var cymbalSound;
var snareDSound;

// preload all the different drum sounds: snare, bass, high tom tom, floor tom tom, and cymbal
snareDSound.setVolume(0.5);

bassDSound.setVolume(0.9);

hTTDSound.setVolume(0.5);

fTTDSound.setVolume(0.5);

cymbalSound.setVolume(0.5);

}

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

function draw() {

background(200, 200, 255); // blue background

noStroke();

highTomTomDrum(tomTomX1, tomTomY1); // calls the high Tom Tom Drum drawing function and places it at the first x, y
highTomTomDrum(tomTomX2, tomTomY2); // calls the high Tom Tom Drum drawing function again and places it at the other x, y
cymbals(cymbalX1, cymbalY1, cymbalZ1); // calls the cymbal drawing function and places it at the first x, y
cymbals(cymbalX2, cymbalY2, cymbalZ2); // calls the cymbal drawing function again and places it at the other x, y
floorTomTomDrum(fTomTomX, fTomTomY); // calls the floor Tom Tom Drum drawing function and places it at the given x, y
snareDrum(snareX, snareY); // calls the snare drum drawing function and places it at the given x, y
bassDrum(); // calls the bass drum drawing function

}

function mousePressed() {
// play the high tom tom drum sound when the mouse is pressed on the left high tom tom drum
if (insideHTTDrumLeft() === true) {
hTTDSound.play();
}

// play the high tom tom drum sound when the mouse is pressed on the right high tom tom drum
if (insideHTTDrumRight() === true) {
hTTDSound.play();
}

// play the floor tom tom drum sound when the mouse is pressed on the floor tom tom drum
if (insideFTTDrum() === true) {
fTTDSound.play();
}

// play the bass drum sound when the mouse is pressed on the bass drum
if (insideBassDrum() === true) {
bassDSound.play();
}

// play the bass drum sound when the mouse is pressed on the snare drum
if (insideSnare() === true) {
snareDSound.play();
}

// play the bass drum sound when the mouse is pressed on the left cymbal
if (insideCymbalLeft() === true) {
cymbalSound.play();
}

// play the bass drum sound when the mouse is pressed on the right cymbal
if (insideCymbalRight() === true) {
cymbalSound.play();
}
}

function insideHTTDrumLeft() {
var dHTTDrumLeft = dist(mouseX, mouseY, tomTomX1, tomTomY1 + 30); // dist from mouse to center of left HTT Drum
if (dHTTDrumLeft <= hTTDRadius) { // checks if the distance from mouse to the center is less than the radius of the HTT drum (which would mean it's inside the drum)
return true;
}
}

function insideHTTDrumRight() {
var dHTTDrumRight = dist(mouseX, mouseY, tomTomX2, tomTomY2 + 30); // dist from mouse to center of right HTT Drum
if (dHTTDrumRight <= hTTDRadius) { // checks if the distance from mouse to the center is less than the radius of the HTT drum (which would mean it's inside the drum)
return true;
}
}

function insideFTTDrum() {
var dFTTDrum = dist(mouseX, mouseY, fTomTomX, fTomTomY + 75); // dist from mouse to center of FTT Drum
if (dFTTDrum <= fTTDRadius) { // checks if the distance from mouse to the center is less than the radius of the FTT drum (which would mean it's inside the drum)
return true;
}
}

function insideBassDrum() {
var dBass = dist(mouseX, mouseY, bassDrumX, bassDrumY); // dist from mouse to center of bass drum
if (dBass <= bassRadius) { // checks if the distance from mouse to the center is less than the radius of the bass drum (which would mean it's inside the drum)
return true;
}
}

function insideSnare() {
var dSnare = dist(mouseX, mouseY, snareX, snareY + 25); // dist from mouse to center of snare drum
if (dSnare <= snareRadius) { // checks if the distance from mouse to the center is less than the radius of the snare drum (which would mean it's inside the drum)
return true;
}
}

function insideCymbalLeft() { // is the mouse inside the left cymbal?
if (mouseX >= 30 & mouseX <= 170 && mouseY >= 130 && mouseY <= 150) { // checks if the mouse is inside the edges of the cymbal
return true;
}
}

function insideCymbalRight() { // is the mouse inside the right cymbal?
if (mouseX >= 430 & mouseX <= 570 && mouseY >= 130 && mouseY <= 150) { // checks if the mouse is inside the edges of the cymbal
return true;
}
}

function highTomTomDrum(x, y) { // function that draws the high tom tom drums (the 2 small middle ones)

fill(64, 40, 15);
rect(x - httRodOffset, y + baseOffset, tomTomRodW, tomTomRodH);

fill(163, 47, 29);
ellipse(x, y + baseOffset, tomTomW, tomTomH);
rect(x - baseOffset, y, tomTomBaseW, tomTomBaseH);

fill(255, 245, 212);
ellipse(x, y, tomTomW, tomTomH);

}

function floorTomTomDrum(x, y) { // function that draws the floor tom tom drum (the bigger left one)

fill(64, 40, 15);
rect(x - fRodOffset, y + fBaseOffsetY, fTomTomRodW, fTomTomRodH);

fill(163, 47, 29);
ellipse(x, y + fBaseOffsetY, fTomTomW, fTomTomH);
rect(x - fBaseOffsetX, y, fTomTomBaseW, fTomTomBaseH);

fill(255, 245, 212);
ellipse(x, y, fTomTomW, fTomTomH);
}

function bassDrum() { // function that draws the bass drum (the big round middle one)

bassDrumX = width / 2;
bassDrumY = 2 * height / 3;

fill(163, 47, 29);
ellipse(bassDrumX, bassDrumY, bassDrumW + rimWidth, bassDrumW + rimWidth)

fill(255, 245, 212);
ellipse(bassDrumX, bassDrumY, bassDrumW, bassDrumW);

}

function snareDrum(x, y) { // function that draws the snare drum (flatter one on right)

fill(64, 40, 15);
rect(x - snareRodOffset, y + snareOffsetY, snareRodW, snareRodH);

fill(163, 47, 29);
ellipse(x, y + snareOffsetY, snareW, snareH);
rect(x - snareOffsetX, y, snareBaseW, snareBaseH);

fill(255, 245, 212);
ellipse(x, y, snareW, snareH);

}

function cymbals(x, y, z) { // function that draws the cymbals

fill(64, 40, 15);
rect(cymbalX1[0] - cymbalOffset, cymbalX1[1] + cymbalH, cymbalRodW, cymbalRodH);
rect(cymbalX2[0] - cymbalOffset, cymbalX2[1] + cymbalH, cymbalRodW, cymbalRodH);
fill(255, 227, 46);
triangle(x[0], x[1], y[0], y[1], z[0], z[1]);

}``````

For my sonic sketch, I decided to build an interactive drum set. My drum set is comprised of a bass drum, 2 high tom-tom drums, a snare drum, a floor tom-tom drum, and 2 cymbals (as labeled in the diagram).

The drum set took a while to create, as I wanted to create separate functions for each drum in order to simplify my draw function. After drawing the drums, I went to freesound.org and found audio files to match each of the drums. I then implemented the logic within mousePressed() to make sure that the sound played when the mouse clicked on the corresponding drum. I really enjoyed this project because it felt very systematic to program and it encouraged me to utilize various functions as well as ample amounts of logic. In addition, getting to learn how to preload sounds was beneficial for my development as a programmer!

## Timothy Liu — Looking Outwards — 10

When I read the prompt for this week’s Looking Outwards, I immediately thought of video game music. I’ve always been a fan of videos games—especially Nintendo franchises such as Pokémon, Mario, and more—and their soundtracks have long been considered the gold standard of technical music. One of the most prominent composers in video game history has been Junichi Masuda, the mastermind behind most of the soundtracks in the Pokémon series. His works have ranged from being techno-like in nature to beautifully symphonic in his newer games. But the commonality among all of the works he’s composed is that they were each computationally created.

I first listened to some of Masuda’s soundtracks from his earlier games like Pokémon Red and Blue (1998). I loved the techno-funk feeling conveyed by the music, and after reading up more about Masuda’s processes, I learned that this was partly a byproduct of technical limitations of that era, but also due to Masuda’s self-proclaimed affinity for techno music at the time. Pokémon Red and Blue were developed on UNIX computer stations called the Sun SPARCstation 1, which made programming files susceptible to crashing. These were clear programming limitations that likely limited the quality of sound files and sound effects.

Next, for the sake of comparison, I listened to music from Pokémon Black and White, games from 2012. I was blown away by the difference; the soundtracks from the newer games were not only crisper, smoother, and rendered more cleanly, but they legitimately sounded like orchestral movements. It was incredible to me how much Masuda’s work evolved, and after reading more about his inspirations, I learned that he was a big fan of the classical composers Igor Stravinsky and Dmitri Shostakovich. This was evident in the elegance of his compositions, and it blew my mind to learn that he programmed these tunes just like he did the techno-style music of 1998. It’s a testament to Masuda’s talent and understanding of the interplay between technology, computation, and music.

Sources:

https://www.polygon.com/interviews/2018/9/27/17909916/pokemon-red-blue-junichi-masuda-interview

https://en.wikipedia.org/wiki/Junichi_Masuda

## Timothy Liu — Looking Outwards — 11

For this week’s Looking Outwards, I examined the work of Heather Kelley, a game designer, digital artist, and media curator focused on sensory interactions and aesthetics in video games. Heather holds a Masters of Arts from the University of Texas at Austin and has worked in a variety of entertainment-technology realms throughout her career. She’s made stops at Subotron, Quake, Unreal, and even the Entertainment Technology Center at Carnegie Mellon, bringing her expertise and work to the classroom as well as to gamer screens worldwide. As a lifelong video game fan, her name immediately caught my eye when I was browsing the list of accomplished women in the field because of her ties to the video game industry and her incredible success within it. After reading over her bio, I was even more impressed by all she’s accomplished, especially her work on the UNFPA Electronic Game to End Gender Violence. It’s clear that Heather is an inspiration to all game designers out there, as she’s managed to combine her creative talents with her desire to create societal change.

One of Heather’s works that I found most impressive was SUGAR, a “cross media collaborative event featuring an original game, scent-generating networked electronics, and couture fashion” (perfectplum.com). SUGAR is, simply put, an immersive video game experience that light-heartedly satirizes the imperial court and Hapsburg history from a romantic perspective. In the game, two players work together to coordinate the dancing of two horses in order to appease the medieval crowd. The art style and imagery are whimsical and geometric, adding a light-hearted flair to her game design and visuals. But what was most unique about SUGAR is the inclusion of an “Action Olofactorizer,” a device that combines hardware, software, and chemicals to produce scents and smells based on the player’s actions. For example, grass, leather, and even horse poop scents are produced to accompany gameplay! Heather’s ability to fully immerse the player and make SUGAR, as well as the rest of her games, interactive experiences is remarkable, and I really enjoyed learning about her work.

Sources:

perfectplum.com

## Timothy Liu — Project 09 — Portrait

I am using 1 grace day on this project.

``````// Timothy Liu
// 15-104, Section C
// tcliu@andrew.cmu.edu
// OpenEnded-09

var Eileen; // variable name of the person in my project!

}

function setup() {
createCanvas(300, 450);
background(255);
frameRate(250);
Eileen.resize(300, 450);
}

function draw() {

// variables to determine the location of each hamburger
var burgerX = random(width);
var burgerY = random(height);

// variables that ensure the hamburger drawn remains on the canvas
var burgerOnCanvasX = constrain(floor(burgerX), 0, width - 1);
var burgerOnCanvasY = constrain(floor(burgerY), 0, height - 1);

// variables to determine the proportions and shape of each burger
var burgerW = random(8, 14);
var burgerH = random(6, 10);
var meatWStart = burgerW / 2 - burgerW * 0.125;
var meatW = burgerW * 0.75;
var meatH = random(2, 4);

// variable that identifies the pixel color of the underlying images
var pixelColor = Eileen.get(burgerOnCanvasX, burgerOnCanvasY);

// applies the pixel color to each hamburger in the foreground
noStroke();
fill(pixelColor);

// drawing each individual hamburger
arc(burgerX, burgerY, burgerW, burgerH, PI, TWO_PI);
rect(burgerX - meatWStart, burgerY, meatW, meatH);
arc(burgerX, burgerY + meatH, burgerW, burgerH, 0, PI);

}``````

For this project, I used a photo I took of my girlfriend Eileen at In-n-out burger in California. I really liked the photo because it has a lot of vibrant colors (red, green, yellow, etc) which I thought would be fun to portray through abstract shapes and pixels. I decided to make each building block of the portrait shaped like a hamburger as a reference to the burgers in the foreground of the picture and the restaurant the photo was taken at. Each burger is proportionally built based on a random bun height and width, which means that each burger is a different, randomly generated size. Together, each of the burgers collects to form a portrait!

## Timothy Liu — Looking Outwards — 09

For my Looking Outwards this week, I looked at my classmate Shannon’s Looking Outwards from Week 3 on Aguahoja, a work by the MIT Media Lab. The prompt for that week was on Computational Fabrication, and I really liked the piece Shannon chose because of how majestic yet haunting it felt. Shannon described Aguahoja as an exploration of how human technology—specifically, 3D printing—could emulate natural and biological materials such as tree branches and bones. The MIT Media Lab took an innovative design approach using algorithms, water-based design, and digital fabrication to erect these massive sculptures that represent nature’s intricacies.

I really enjoyed Shannon’s reflection on Aguahoja because it was concise yet contemplative, explaining how although Aguahoja is an effective representation of nature’s form, it still requires manpower and technology. One thing I would add onto her discussion points are the incredible amount of research the MIT team undertook to develop their algorithms for printing. It took them 6 whole years of research into computationally manufactured functional biopolymers for them to feel confident enough in their ability to emulate these biological structures, a testament to how wonderfully unique nature really is.

Sources:

https://www.media.mit.edu/projects/aguahoja/overview/

## Timothy Liu — Looking Outwards — 08

For this week’s Looking Outwards, I watched Heather Knight’s lecture from the 2011 Eyeo festival on charismatic and beneficial technology creation — namely, how to build robots that work alongside and benefit people. Heather has a degree in Electrical Engineering from MIT, a PhD in Robotics from CMU (!!), and a postdoc from Stanford exploring minimal robots and autonomous car interfaces. She worked in Paris for a few years at the start of her career before returning to the MIT Media Lab to work on a robot called “Kismet” under Dr. Cynthia Breazeal. This was her segway into the realm of robotics, as it enabled her to see what it meant to work on an interactive, human-like robot (Kismet was meant to be a functional robot head).

Heather is immensely passionate about “social robotics,” the study of technology with social intelligence that can communicate with us in human terms. She strives to understand how we can build robot interfaces that adjust to human needs, not the other way around. This made me realize that Heather was, in a way, a pioneer in social/autonomous robotics; she was discussing the concepts of ethics in robots and human-technology interaction way back in 2011, well before any of the technologies of today were developed!

Due to her affinity for technology, the performing arts, and entertainment, Heather started working for Syyn Labs, an creative technology group that, as described on their website, “fuses the worlds of technology and interactive sciences with artistic mediums to design and construct visually dynamic spectacles that inspire thought and provoke conversation.” There, she began working on the installation of robots in music videos while developing her own robot theater company, Marilyn Monrobot. It’s an unconventional path, but it’s what led to her working on the OK Go Rube Goldberg machine in their music video “This Too Shall Pass,” one of the most famous music videos in YouTube’s existence! Heather managed the top floor of the machine, or roughly the first 2 minutes of the video. I remember watching this video multiple times growing up and marveling at its artistic complexity, so it was really cool learning about Heather’s involvement in the project.

Overall, I think what I admired most about Heather’s talk was her ability to ground and personify robots. Robots are inherently cold, mechanistic beings, but Heather’s passion for the subject matter and artistic understandings allowed her to find a way to bring robots to life charismatically. It’s evident in her presentation style too; she’s loose, lively, and a whole lot of fun to listen to. It’s her charisma that rubs off on her robots most, and I love the fact that her projects all seem to be passion projects. Today, she’s an Assistant Professor of Robotics at Oregon State University, where she runs the—you guessed it—CHARISMA research group, which uses entertainment and artistic influences to develop Social Robots. It’s exciting work that’s befitting of her energetic nature.

SOURCES:

http://www.marilynmonrobot.com/

http://syynlabs.com/

https://en.wikipedia.org/wiki/Kismet_(robot)