Rachel Lee and Jenna Kim Section E Final Project

Space Crunch

/* Rachel Lee and Jenna Kim
rwlee@andrew.cmu.edu and jeeyoonk@andrew.cmu.edu
Section E
Final Project */

// Radius of cereal loops
var radius = 20;
// Number of cereal pieces displayed (maximum number possible)
var cerealNum = 50;
// Empty array that holds cheerio elements
var cereal = [];
// Variable to store clicked cereal pieces
var clicked;
// Game time elapsed
var time_elapsed = 0;
// Game duration (60 seconds)
var gameLength = 120;
// Changes states according to instructions screen, game and game over
var gameMode = 0;

var x = [];
var y = [];

function setup() {
    createCanvas(600, 500);
    frameRate(10);

    // Variables for spacing of cereal loops according to triangle principles
    var tw = 90; // horizontal spacing between cheerios
    var th = 70; // vertical spacing between cheerios
    var cy = 50; // original y position
    var cx = 30; // original x position

    // Creates and pushes cereal into empty array
    for (var y = 0; y < 10; y ++) {
        // creates and displays even rows
        if ((y % 2) == 0) {
            for (var x = 0; x < 15; x++) {
                var px = cx + x * tw;
                var py = cy + y * th;
                cereal.push(new cheerios(px, py));
            }
        }
        // Creates and displays offset odd rows
        else {
            for (var x = 0; x < 10; x++) {
                th = 60 * (sqrt(3) / 2);
                var px = cx + x * tw;
                var py = cy + y * th;
                cereal.push(new cheerios (px + tw / 2, py + 150));
            } 
        } 
    } 
    
    // Initializes the player's score
    clicked = 0;

    // Updates cereal every second to determine if they pop up or not
    // Updates timer every second (1000 = 1 second in milliseconds)
    setInterval(function() {
        for (var i = 0; i < cerealNum; i ++) {
            cereal[i].updateC();
        }
        time_elapsed += 2;
    }, 1000);
}

function draw() {
	// Color gradient sky
    var color1 = color(0, 0, 153); // Blue purple
    var color2 = color(204, 51, 0); // Red

    for (var i = 0; i < height; i++) {
        var gradient = map(i, 0, height / 2, 0, 1);
        // Lerp used to ease color transitions 
        var mesh = lerpColor(color1, color2, gradient); 
        fill(mesh);
        stroke(mesh);
        line (0, i, width, i);
    }

    hillShadow();
    hill();

    noStroke();

    // Instruction page/ start game mode
    if (gameMode == 0) {
        gameStart();
    }

    // If game is still running, draws cereal, UFO, game score and time tracker
    if (gameMode == 1) {
        for (var i = 0; i < cerealNum; i ++){
            cereal[i].drawC();
        }  
        drawUFO();
        gSgT();
    }

    // Game ends when timer runs out
    if (time_elapsed >= gameLength) {
        gameMode = 2;
        gameOver();
    }

    // Press s key to start game
    if (keyIsPressed) {
    	if((key == 's') || (key == 'S')) {
    		gameMode = 1;
    	}
    	// When the game is over, press r key to reset game
        if ((key == 'r') || (key == 'R')) {
            gameMode = 1;
            time_elapsed = 0;
            clicked = 0;
        }
    }
}

// Draws UFO that follows mouse position
function drawUFO() {
    strokeCap(ROUND);
    stroke(230);
    strokeWeight(4);
    line(mouseX + 12, mouseY + 2, mouseX + 27, mouseY + 14);
    line(mouseX - 12, mouseY + 2, mouseX - 27, mouseY + 14);
    noStroke();
    fill(245, 210, 145); // orange peach
    ellipse(mouseX, mouseY + 7, 57, 16);
    fill(55, 70, 140); // blue 
    ellipse(mouseX, mouseY - 1.5, 85, 25);
    fill(255, 105, 80); // orange 
    ellipse(mouseX, mouseY - 3.5, 85, 25);
    fill(245, 210, 145); // orange peach
    arc(mouseX, mouseY - 7, 57, 37, PI, TWO_PI);
    ellipse(mouseX, mouseY - 7, 57, 16);
}

// Defines object constraints
function cheerios(x, y, duration, drawC, updateC, clickedC) {
    var pos = {x: x, y: y, duration: duration, drawC: drawCheerios, 
    updateC: updateCheerios, clickedC: clickedCheerios};
    return pos;
}

function drawCheerios() {
    if (this.duration > 0) {
        stroke(180, 147, 119);
        strokeWeight(8);
        noFill();
        ellipse(this.x, this.y, radius, radius);
        stroke(240, 225, 190);
        ellipse(this.x + 2, this.y + 2, radius, radius);
    }
}

//Determines whether or not the cheerios appear
function updateCheerios() {
    if (this.duration > 0) {
        this.duration = this.duration - 0.5;
    } 
    else {
        var ratio = random(1);
        if (ratio < 0.15) {
            this.duration = random(5);
        }
    }
}

// Checks to see if user clicked the cereal, and adds to score
function clickedCheerios (clickedX, clickedY) {
    if ((dist(clickedX, clickedY, this.x, this.y - radius) < radius) & (this.duration >= 0)) {
        this.duration = 0;
        clicked += 1;  
    }
}

// Checks to see if the mouse was pressed, and if the cereal was clicked
function mousePressed() {
    for (var i = 0; i < cerealNum; i++) {
        cereal[i].clickedC(mouseX, mouseY);
    }
}

function gameStart() {
    // Color gradient sky start
    var colorStart1 = color(0, 0, 153); // Blue purple
    var colorStart2 = color(204, 51, 0); // Red

    for (var i = 0; i < height; i++) {
        var gradientStart = map(i, 0, height / 0.9, 0, 1);
        // Lerp used to ease color transitions 
        var meshStart = lerpColor(colorStart1, colorStart2, gradientStart); 
        fill(meshStart);
        stroke(meshStart);
        line (0, i, width, i);
    }

    fill(255);
    textFont('Futura');
    textSize(13);
    text("Collect cheerios to fuel the U.F.O.'s journey through outer space!", width / 10 + 50, height / 2);
    text("Press S to Start", width/ 10 + 200, height/ 2 + 30);
}

// Display game score and remaining game time
function gSgT() {
    fill(255);
    textFont('Futura');
    textSize(12);
    text("Score : " + clicked, 500, height - 40);
    text("Time Remaining: " + (60 * 2 - time_elapsed)/2 + " secs", 50, height - 40);	
}

// Game over screen
function gameOver() {
    // Color gradient sky end
    var colorEnd1 = color(204, 51, 0); // Red
    var colorEnd2 = color(0, 0, 153); // Blue purple

    for (var i = 0; i < height; i++) {
        var gradientEnd = map(i, 0, height / 0.7, 0, 1);
        // Lerp used to ease color transitions 
        var meshEnd = lerpColor(colorEnd1, colorEnd2, gradientEnd); 
        fill(meshEnd);
        stroke(meshEnd);
        line (0, i, width, i);
    }
    noStroke();
    fill(255);
    text("Game Over!", width/ 2 - width/ 20, height/ 2 - 30);
    text("Cheerios Collected : " + clicked, width/ 3 + 35, height/ 2);
    text("Press R to restart", width/ 3 + 55, height/ 2 + 30);
}

//drawing hill
function hill() {
    var hillSpeed = 0.0002;
    var hillDetail = 0.0025;
    stroke(210, 105, 95, 80);
    for (var x = 0; x < width; x++) {
        var t = (x * hillDetail) + (millis() * hillSpeed);
        var y = map(noise(t * 2), 0, 1, 330, 200);
        line(x, y, x, height);
    }
} 

function hillShadow() {
    var hillSpeed = 0.0001;
    var hillDetail = 0.0045;
    stroke(160, 70, 70, 55);
    for (var x = 0; x < width; x++) {
        var t = (x * hillDetail) + (millis() * hillSpeed);
        var y = map(noise(t * 2.2), 0, 1, 100, 300);
        line(x, y, x, height);
    }   
}

Backstory and Instructions

Help the U.F.O. continue its intergalactic journey by collecting cheerio fuel! Click on the cheerios before they disappear, and collect as many as possible before the timer runs out. Running game time is 60 seconds.

Work Distribution 

For our project, Rachel primarily focused on the structure and mechanics of the game. Rachel created the mechanism for the flashing cheerios on screen, the game timer and the score keeping system when cheerios were pressed. Jenna focused more on the visual aspect of the game, designing the background / scenery, U.F.O., as well as the ‘Start Game’ and ‘Game Over’ pages and the mechanics of the game modes (Start, actual game, end game).

Working Together

Overall, we had a lot of fun working together. We were able to make revisions and improvements to our code in terms of style, efficiency of structure and visuals through continued collaboration.

First iteration
Second Iteration

 

Rachel Lee Proposal Section E

For my final project, I plan on creating an interactive game with my proposed collaborator, Jenna Kim, from Section E. In the game, the main protagonist will be a rocket navigating its way through cereal planet, and its objective is to collect cereal for fuel so that it can continue its journey in outer space. It will potentially be timed e.g. around a minute long, so that after a given duration, the game will end or draw to some sort of conclusion. Alternatively, there might be toxic substances floating around that if encountered, will cause the game to end. This project will draw upon older skills and exercises we previously completed, such as the Generative Landscape project, self defined functions and arrays. This will allow me to brush up on techniques that I want to gain a better understanding for, such as removing elements from an array.

Sketch of what our project might look like

Rachel Lee Looking Outwards 12 Section E

Living Library by Design I/O (2016).

 by Béatrice Lartigue (2009).

For my final project, I am interested in the idea of telling a story. As such, I selected two projects that are described as educational projects, used to facilitate teaching or stories amongst children. The two projects I have chosen are Mü by Béatrice Lartigue (educational installation) and Living Library by Design IO (interactive projected encyclopaedia).

I really admired how both projects were able to harness technology to create a story/ experience that went beyond the screen. For example, in Mü, interaction and creativity is encouraged through the educational application, but are used to guide class activities and discussions in the real world. Similarly, Living Library is housed in a museum, supplementing interaction with the surrounding Connected World exhibit, as well as interaction with others due to the interactive and dynamic nature of the project. Most importantly, both projects are able to delight and appeal to our senses regardless of age, which I hope to evoke in those who view my project. Something that was not considered in the two projects that I might try to address in my storyline for my final deliverable is how specific interactions might trigger divergent storyline paths for the viewer, or be a catalyst for the next section of the story, rather than having the interactions contained within one section of the narrative. This might help keep viewers more engaged, and feel a deeper connection with the piece.

Rachel Lee Project 11 Section E

sketch

/* Rachel Lee
Section E
rwlee@andrew.cmu.edu
Project 11: Freestyle Turtles
*/

function setup() {
    createCanvas(480, 480);
    background(10, 50, 100);
}

function draw() {
    var m = 300; // variable to be mapped
	// maps mouseX position and controls scale/ how far turtle turns right
	var scale = map(m, 0, mouseX, 100, 300);

	// drawing first turtle (violet)
    var turtle1 = makeTurtle(mouseX, mouseY);
    turtle1.setColor(color(240, 185, 240, 60));
    turtle1.setWeight(0.25);

    for (i = 0; i < 360; i++) {
    	turtle1.penDown();
    	turtle1.right(60);
    	turtle1.forward(30);
    	turtle1.right(scale); 
    	turtle1.forward(15);
    }
    
    // drawing second turtle (orange)
    var turtle2 = makeTurtle(mouseX, mouseX);
    scale = map(m, 0, mouseX, 20, 250); 
    turtle2.setColor(color(240, 165, 140, 150));
    turtle2.setWeight(0.1);

    for (i = 0; i < 100; i++) {
    	turtle2.penDown();
    	turtle2.right(60);
    	turtle2.forward(40);
    	turtle2.right(scale);
    	turtle2.forward(5);
    }

    frameRate(5); // slows down frame rate
}

// turtle graphics implementation for p5.js
function turtleLeft(d) {
    this.angle -= d;
}
 
 
function turtleRight(d) {
    this.angle += d;
}
 
 
function turtleForward(p) {
    var rad = radians(this.angle);
    var newx = this.x + cos(rad) * p;
    var newy = this.y + sin(rad) * p;
    this.goto(newx, newy);
}
 
 
function turtleBack(p) {
    this.forward(-p);
}
 
 
function turtlePenDown() {
    this.penIsDown = true;
}
 
 
function turtlePenUp() {
    this.penIsDown = false;
}
 
 
function turtleGoTo(x, y) {
    if (this.penIsDown) {
      stroke(this.color);
      strokeWeight(this.weight);
      line(this.x, this.y, x, y);
    }
    this.x = x;
    this.y = y;
}
 
 
function turtleDistTo(x, y) {
    return sqrt(sq(this.x - x) + sq(this.y - y));
}
 
 
function turtleAngleTo(x, y) {
    var absAngle = degrees(atan2(y - this.y, x - this.x));
    var angle = ((absAngle - this.angle) + 360) % 360.0;
    return angle;
}
 
 
function turtleTurnToward(x, y, d) {
    var angle = this.angleTo(x, y);
    if (angle < 180) {
        this.angle += d;
    } else {
        this.angle -= d;
    }
}
 
 
function turtleSetColor(c) {
    this.color = c;
}
 
 
function turtleSetWeight(w) {
    this.weight = w;
}
 
 
function turtleFace(angle) {
    this.angle = angle;
}
 
 
function makeTurtle(tx, ty) {
    var turtle = {x: tx, y: ty,
                  angle: 0.0, 
                  penIsDown: true,
                  color: color(128),
                  weight: 1,
                  left: turtleLeft, right: turtleRight,
                  forward: turtleForward, back: turtleBack,
                  penDown: turtlePenDown, penUp: turtlePenUp,
                  goto: turtleGoTo, angleto: turtleAngleTo,
                  turnToward: turtleTurnToward,
                  distanceTo: turtleDistTo, angleTo: turtleAngleTo,
                  setColor: turtleSetColor, setWeight: turtleSetWeight,
                  face: turtleFace};
    return turtle;
}

For this week’s assignment, I wanted to create a turtle graphics composition that was reminiscent of a spirograph. I really enjoyed playing with these when I was younger, and was constantly excited by what kinds of mandalas would yield from the different tracing tools. This assignment was really fun, especially experimenting with the intricacies of each mark made, playing with transparencies and investigating scale.

Example composition created via changing mouseX positions

Rachel Lee Looking Outwards 11 Section E

Screenshot from the anti game, with machine intelligence posing and asking questions to create a generative song.

Atlås from binaura on Vimeo (2017).

This week, I decided to investigate Atlås, an anti game environment that creates generative music, by the creative computing collective binaura. Atlås was built using the p5.js library, while simultaneously investigating the autonomy of algorithms and machine intelligence. At the crux, this project generates sounds that correspond to certain qualities of  answers to questions that are solved by machine intelligence. While this concept in itself is fascinating, what I find the most interesting about this project is how the artistic collective was able to generate an appropriate soundscape for the types of questions generated, as the questions are often quite lofty deal with cognitive processes, human communication and environmental factors (these topics are not the easiest things to talk about). Further, I was impressed by how binaura was able to create a compelling visual narrative to complement the experience, which was guided appropriately by the tempo and feeling of the generated music. Overall, I really admire how well the project was executed, especially with respects to the sensitivity of the relationship between the user, the computer, the music, and visual elements of the anti game environment.

Rachel Lee Project 10 Section E

sketch

/* Rachel Lee
Section E
rwlee@andrew.cmu.edu
Project 19: Generative Landscape
*/

var sailboats = [];

function setup() {
    createCanvas(480, 480);
    noStroke();
    background('blue');

    for(var i = 0; i < 3; i ++) {
    	var rndmX = random(width);
    	sailboats[i] = drawSailboats(rndmX);
    }
    
    frameRate(50);
}

function draw() {
    // Background color changes according to hour time bracket
    // Draw sun and moon according to hour time bracket
	if (hour() >= 0 & hour() <= 8) {
		fill(210, 195, 135);
		rect(0, 0, width, height);
		strokeWeight(15);
		fill(240, 100, 70);
		fill(225, 145, 90);
		ellipse(390, 90, 135);
	} else if (hour() > 8 & hour() <= 16) {
		fill(190, 230, 250);
		rect(0, 0, width, height);
		stroke(255, 231, 101, 80);
        strokeWeight(15);
		fill(230, 210, 120);
		ellipse(390, 90, 135);
	} else if (hour() > 16 & hour() <= 24) {
		fill(10, 50, 70);
		rect(0, 0, width, height);
		strokeWeight(15);
		fill(205, 205, 200);
		fill(225, 225, 210);
		ellipse(390, 90, 135);	
	}	

	// Calls function to draw waves
	waves1();
	waves2();

	updateandDisplaySailboats();
	addSailboats();
}

// Draws wave layer 1 via Perlin Noise
function waves1() {
	noStroke();
    var wavesSpeed = 0.0002;
    var wavesDetail = 0.0004;
    push();
    beginShape();
    fill(0, 100, 140);
    vertex(0, height);
    for (var x = 0; x < width; x++) {
        var w = (x * wavesDetail) + (millis() * wavesSpeed);
        var y = map(noise(w), 0, 1.2, height /2 + 70, height);
        vertex(x, y);
    }
    vertex(x, height);
    vertex(0, height);
    vertex(0, y); 
    endShape();
    pop();
}

// Draws wave layer 2 via Perlin Noise
function waves2() {
	noStroke();
	wavesSpeed = 0.0004;
    wavesDetail = 0.0008;
    push();
    beginShape();
    fill(80, 135, 155);
    vertex(0, height);
    for (var x = 0; x < width; x++) {
        var w = (x * wavesDetail) + (millis() * wavesSpeed);
        var y = map(noise(w), 0, 1, height / 1.5 + 50, height);
        vertex(x, y);
    }
    vertex(x, height);
    vertex(0, height);
    vertex(0, y); 
    endShape();
    pop();
} 

// Updates and displays the position of sailboats
function updateandDisplaySailboats() {
	for(var i = 0; i < sailboats.length; i++) {
		sailboats[i].move();
		sailboats[i].display();
	}
}

// Adds a new sailboat to the canvas according to a marginal probability
function addSailboats() {
    var sailboatProbability = 0.008;
    if (random(0, 1) < sailboatProbability) {
    	sailboats.push(drawSailboats(width));
    }
}

// Updates the position of the sailboat with every frame
function moveSailboats() {
	this.x += this.speed;
}

// Draws the sailboats
function displaySailboats() {
    noStroke();
	push();
	translate(0, 220);
	
	fill(190, 115, 75);
    rect(width / 2 + this.x - 5, height / 2 - 100, 5, 100);

	fill(230, 225, 225);
    quad(width / 2 + this.x - 60, height / 2 - 30, 
    	width / 2 + this.x + 50, height / 2 - 30, 
    	width / 2 + this.x + 30, height / 2,
    	width / 2 + this.x - 40, height / 2);

	fill(230, 85, 60);
	triangle(width / 2 + this.x, height / 2 - 60, 
		width / 2 + this.x, height / 2 - 150, 
		width / 2 + this.x - 60, height / 2 - 40);
	
	fill(225, 120, 110);
    triangle(width / 2 + this.x, height / 2 - 60, 
		width / 2 + this.x, height / 2 - 150, 
		width / 2 + this.x + 40, height / 2 - 60);
	pop(); 
} 

function drawSailboats(birthLocationX, birthLocationY) {
	var boat = {x: birthLocationX,
	    speed: -1,
	    r: random(0.1, 0.3),
	    move: moveSailboats,
	    display: displaySailboats,
	    }
	return boat;
}

For this week’s assignment, I decided to depict some boats sailing across the water. When I was a kid, I would sit by the pier and watch junk boats and ferries glide across the harbour, so this scene really resonated with me. This project was a little challenging for me, but I challenged myself to experiment with Perlin noise, and create a landscape that changes over time (beyond the immediate shifting waves and number of boats). If I had more time, I would try to space the boats out a bit more, so that they don’t overlap, and also add clouds.

Original sketch

 

Rachel Lee Looking Outwards 10 Section E

Liminoid Garden by Filipa Valente (2014)

This week, the project I have chosen to investigate is ‘Liminoid Garden’ by Filipa Valente. Filipa Valente is a Portuguese interactive artist who is based in Los Angles. She studied architecture and media art and architecture at the Bartlett School of Architecture, UCL (London), and SciArc (Los Angles). Valente primarily creates dynamic light architectures, through she also works in experience design, architecture, product design and animation.

‘Liminoid Garden’ features mechanical blooms that are fitted with electronic controllers. Such controllers receive real time data regarding temperature, light and pollution conditions from the outside, and translate the data into varying physical movements and light qualities in the interior installation space. What I admired about Valente’s project was how the installation managed to be so beautiful but reflective of real world conditions and contexts by almost personifying the blooms. I also admire how the piece encourages and fosters audience interaction in a way that allows viewers to develop a constantly changing relationship with the installation, since the data inputs breathe new life into the piece.

Rachel Lee Project 09 Section E

sketch

/* Rachel Lee
Section E
rwlee@gmail.com
Project 09: Custom Pixel*/

var pic;

// preloads image
function preload() {
    pic = loadImage("https://i.imgur.com/iuFu4yy.jpg"); 
}

function setup() {
    createCanvas(358, 480);
    pic.loadPixels();
    background(255);
    imageMode(CENTER);
    frameRate(100);
}

function draw() {
	// randomizes position of pixel appearance 
    var x = constrain(floor(random(width)), 0, width - 1);
    var y = constrain(floor(random(height)), 0, height - 1);
    // randomizes arc width and height
    var w = random(5, 30);
    var h = random(5, 30);

    noStroke();
    // picks colors from pixel position 
    var picCol = pic.get(x, y);
    fill(picCol);
    // draws arcs as 'custom pixel'
    arc(x, y, w, h, HALF_PI, PI);
}

I decided to create a custom pixel portrait of my dad for his birthday this weekend. Due to its personal significance, I found this project to be super fun and rewarding, and hope he enjoys the project! I will be doing more of these in the future to try out different options.

Original photo
Custom pixel portrait stills
Close to near final stage

Rachel Lee Looking Outwards 09

 This week, I have decided to review Jenny Hu’s Week 7 Looking Outwards on Giorgia Lupi and Kaki King’s project titled ‘Bruises: The Data We Don’t See‘ (2017). I decided to review project because last week, I reviewed a few projects by Lupi and really enjoyed how she is able to humanise data and convey it in a visceral way. I really enjoyed Jenny’s note about how the creators of this project tied in the sensorial and emotional experience of documenting Kaki’s child’s journey with the ITP condition, as it makes the user feel a lot more connected and be able to empathise with the patient. Further, something I admired about this project was how Lupi and King began the project by hand documenting symptoms and moods by hand via charts and interviews, but were able to piece the information together in a fluid timeline that was sensitive to the context at hand via digital animation. Something else that I found especially visceral about this project was the motif chosen– the white flowers and hand written annotations created a delicate quality, that allowed for the symbols exploration to supplement with a truthful transcription of the pockets of pain and pleasures of the journey. Finally, I love how the animation of the piece (how the petals, text and symbol embellishments fade onto the screen) fills in a ghostly shell, and helps viewers to understand that the child’s journey is every changing and not static, filled with both highs and lows, thus conveying an important piece of the emotional journey while also allowing for artistic direction to occur.

Initial data visualisation work that was translated digitally
Key of symbols used throughout the video

 

Rachel Lee Looking Outwards 08

Eyeo 2018 – Giorgia Lupi from Eyeo Festival on Vimeo.

Giorgia Lupi is an Italian, New York based information designer and entrepreneur who aspires to create compelling visual narratives that challenge the stereotype of impersonal data. Lupi studied architecture at FAF in Ferrara, Italy, before completing her PHD in design at Milan Politecnico. Her body of work is based on a sole kind of medium, rather, her work traverses the boundaries of digital, print and handcrafted representations, but rather, she uses whatever drawing medium is appropriate to best represent data. Something that I really admire about Lupi and her body of work is how she treats each data analytics case as a blank canvas, and creates a unique visual language for the dataset at hand. I also admire how she is able to visualise numbers in a way that is compelling by creating narratives, storylines, and humanising the data. Something that strikes me as very impressive is how data can be meaningful.

One project that I was really captivated was Dear Data, a project done in collaboration with Stefanie Posavec, a fellow information designer. The two were previously strangers, but were able to cultivate a friendship and tell a story about each other via weekly postcard ‘reportings’ regarding details of their daily lives. While I typically think that numbers and data are quite boring, the result of this project was a creative, engaging and intimate experience that documented the seemingly endless multifaceted aspects of two people’s lives.

Throughout Lupi’s talk, I really enjoyed her use of hand drawn screen transitions that speak to the body of her work, and how she dissects each case study of her project to discuss the brief, the collaborative efforts, process and physical products. Learning about the way in which data was interpreted and the integration of her visual voice into her presentation ultimately made for an effective presentation.