// Tiffany Lai (thlai) & Helen Wu (hdw)
// 15-104, Section A
// Project 12 - Final
//var font; // variable for font
var music; // variable for music
var fft; // variable for p5.FFT library
var amp; // variable for p5.Amplitude library
var leftArrows = [];
var opacity = 255;
var scoreCount = 0;
var playMode = 0;
function preload() {
//font = loadFont('font/slkscr.ttf'); // load font 'silk screen'
music = loadSound("https://courses.ideate.cmu.edu/15-104/f2017/wp-content/uploads/2017/12/music-2.mp3"); // load music
}
function setup() {
createCanvas(480, 480);
fft = new p5.FFT(0.9, 256); // load p5.FFT library
amp = new p5.Amplitude(); // load p5.Amplitude
}
function draw() {
background(22, 35, 42);
circleBeat(); // background sound visualization
displayTriangles(); // display top triangles
moveTriangles(); // top triangles go down when pressing specific keys
generateArrows();
for (var i = 0; i < leftArrows.length; i++) {
leftArrows[i].move();
leftArrows[i].render();
if (leftArrows[i].y < -500) {
leftArrows.shift();
}
}
scoreCounter(); // score counter
endScreen();
startScreen();
}
//start screen
function startScreen() {
fill(255, 56, 115, opacity);
rect(0,0,width,height);
textSize(12);
if (frameCount%2 == 0){
fill(255,opacity);
//textFont(font);
textAlign(CENTER);
textSize(12);
text("CLICK TO PLAY", width/2, height/2);
}
}
//music plays
function mouseClicked() {
displayTriangles(); // display main triangles
opacity = 0;
moveTriangles();
if (playMode == 0) {
music.play();
playMode = 'sustain';
} else {
playMode = 0;
music.stop();
}
}
// background sound visualization
function circleBeat() {
var spectrum = fft.analyze(); // analyze music spectrum
// drawing the circle
push();
translate(width/2, height/2);
for (var i = 0; i < spectrum.length; i++) {
var angle = map(i, 0, spectrum.length, 0, 360); // map spectrum to degrees
var spec = spectrum[i];
var r = 2 * map(spec, 0, 256, 20, 100);
var x = r * cos(angle);
var y = r * sin(angle);
fill(200, i*3, i*5); // fill with gradient
noStroke();
ellipse(x, y, 3, 3); // tiny center circles
}
pop();
}
//==========CREATE TOP TRIANGLES ==========//
// make triangles object
function makeTriangles (l, d, u, r) {
var triangles = {
left: l,
down: d,
up: u,
right: r,
display: displayTriangles,
move: moveTriangles
}
return triangles;
}
// display triangles
function displayTriangles() {
fill(255);
noStroke();
// draw four anchor triangles
triangle(115, this.l+66, 154, this.l+88, 154, this.l+44); // left
triangle(185, this.d+53, 207, this.d+92, 229, this.d+53); // down
triangle(272, this.u+40, 250, this.u+79, 295, this.u+79); // up
triangle(325, this.r+44, 325, this.r+89, 364, this.r+66); // right
// draw 3D part of anchor triangles
fill(116, 136, 133);
quad(115, this.l+66, 154, this.l+88, 154, 93, 115, 71); // left
quad(250, this.u+79, 295, this.u+79, 295, 85, 250, 85); // up
fill(230, 160, 133);
quad(185, this.d+53, 185, 62, 207, 102, 207, this.d+92); // down (left)
quad(207, this.d+92, 207, 102, 229, 62, 229, this.d+53); // down (right)
quad(325, this.r+89, 325, 94, 364, 71, 364, this.r+66); // right
}
function moveTriangles(){
// move triangles down when you press specific arrow key
if (keyIsDown(LEFT_ARROW)) {
this.l = 3;
} else if (keyIsDown(DOWN_ARROW)) {
this.d = 5;
} else if (keyIsDown(UP_ARROW)) {
this.u = 4;
} else if (keyIsDown(RIGHT_ARROW)) {
this.r = 3;
} else {
this.l = 0;
this.d = 0;
this.u = 0;
this.r = 0;
}
}
//========== GENERATE ARROWS ==========//
function makeArrows(aX, aY, spd, rot) {
var arrows = {
x: aX,
y: aY,
speed: spd,
rotateArrow: rot,
move: moveArrows,
render: displayArrows,
generate: generateArrows
}
return arrows;
}
function displayArrows() {
push();
stroke(0, 200, 200);
strokeWeight(5);
noFill();
translate(this.x, this.y);
rotate(this.rotateArrow);
triangle(115, 66, 154, 88, 154, 44);
pop();
//triangle(this.x+115, this.y+66, this.x+154, this.y+88, this.x+154, this.y+44);
}
function moveArrows() { // speed of clouds
this.y -= this.speed;
}
function generateArrows() {
var vol = amp.getLevel();
if ((vol > 0.33 & vol < 0.34) || (vol > 0.18 && vol < 0.2) || (vol > 0.03 && vol < 0.032)) {
var randomizer = int(random(0,4)); // if the volume level is over 0.3
if(randomizer == 0){
var newArrow = new makeArrows(0, 420, 4, 0); // make new arrow object
leftArrows.push(newArrow);
}
if (randomizer == 1) {
var newArrow = new makeArrows(140, 840, 4, 3/2*PI); // make new arrow object
leftArrows.push(newArrow);
}
if (randomizer == 2) {
var newArrow = new makeArrows(340, 420, 4, 1/2*PI); // make new arrow object
leftArrows.push(newArrow);
}
if (randomizer == 3) {
var newArrow = new makeArrows(480, 840, 4, PI); // make new arrow object
leftArrows.push(newArrow);
}
}
}
function endScreen() {
if (music.isPlaying() ) {
var endOpacity = 0;
} else {fill(255, 56, 115, endOpacity);
var endOpacity = 255;
rect(0, 0, width, height);
textSize(12);
if (frameCount%2 == 0){
fill(255,endOpacity);
textFont(font);
textAlign(CENTER);
textSize(12);
text("GAME OVER", width/2, height/2);
text("SCORE: " + scoreCount, width/2, height/2+20);
}
}
}
// ========== COUNTER ========== //
function scoreCounter() {
// draw borders on screen
noStroke();
fill(110, 120, 120);
rect(0, 0, width, 7);
rect(0, 0, 7, height);
rect(0, height-7, width, 7);
rect(width-7, 0, 7, height);
fill(116, 136, 133);
quad(182, height, 187, height-25, 292, height-25, 297, height);
fill(255);
text("SCORE: " + scoreCount, width/2, 472);
for (var i = 0; i < leftArrows.length; i++) {
if((keyIsDown(LEFT_ARROW))) {
if (leftArrows[i].y > -10 & leftArrows[i].y < 30 && (leftArrows[i].x ==0)) {
scoreCount = scoreCount + 10;
leftArrows.shift();
}
}
if(keyIsDown(DOWN_ARROW)){
if (leftArrows[i].y > 180 & leftArrows[i].y < 220 && (leftArrows[i].x == 140)) {
scoreCount = scoreCount + 10;
leftArrows.shift();
}
}
if(keyIsDown(UP_ARROW)){
if (leftArrows[i].y > -90 & leftArrows[i].y < -50 && (leftArrows[i].x == 340)) {
scoreCount = scoreCount + 10;
leftArrows.shift();
}
}
if(keyIsDown(RIGHT_ARROW)){
if (leftArrows[i].y > 110 & leftArrows[i].y < 150 && (leftArrows[i].x == 480)) {
scoreCount = scoreCount + 10;
leftArrows.shift();
}
}
}
}
Author: thlai@andrew.cmu.edu
thlai-Project12-Proposal
thlai-LookingOutwards12-Priors-and-Precursors
thlai-LookingOutwards11-Computer-Music
The artist I am choosing for this Looking Outwards is actually the same from my Looking Outwards 08: Yuri Suzuki. I wanted to explore his projects more in depth and choose another one that drew me in.
Suzuki’s project This Looks Like Music takes a whimsical approach to computational music. Suzuki created an audiovisual installation for Mudam Publics Summer Project – the project consists of a mini robot that detects and follows a black line on paper. The robot responds to colored reference points and translates it into sound. I imagine that the robots sense and calculate the color of the markers (kind of like this week’s assignment) and outputs a sound that corresponds to it.
The best part about this installation is that it invites the audience to co-create the music (the audience includes small children!). Having an interactive installation makes it much more relatable and tangible, especially for something like music.
thlai-Project11-Composition
I still have trouble creating compositions using turtle graphics, but this project certainly helped me learn a lot. I created a hexagonal grid (inspired by a past assignment) and a spiral web that responds to the mouse position. The web and hive are both natural phenomena created by small creatures and have unique and distinctive shapes.
// Tiffany Lai
// 15-104, Section A
// thlai@andrew.cmu.edu
// Project 11 - Turtle
var ox = 80; // offset of x position
var oy = 90; // offset of y position
var tw = 15; // spacing between hexagons (width)
var th = 18; // spacing between hexagons (height)
function setup() {
createCanvas(480, 480);
}
function draw() {
background(35, 36, 49, 50);
// draw hexagons
for (var x = 0; x < 6; x++) {
for (var y = 0; y < 20; y++) {
var t1 = makeTurtle(ox + x * tw, oy + y * th);
hexagon(t1);
}
if (x%2 == 0) { // offset every other column
oy = 80;
} else {
oy = 90;
}
}
// draw spiral of circles
var t2 = makeTurtle(0, 0);
var translateX = map(mouseX, 0, width, 100, 200);
var translateY = map(mouseY, 0, height, 100, 200);
var radius = map(mouseX, 0, width, 160, 170);
translate(translateX, translateY); // translate based on mouse position
for (var i = 0; i < 100; i++) {
t2.setColor(color(97, 121, 120, 20)); // yellow-y color
t2.forward(i * 5); // creating spiral
t2.left(radius);
circle(t2);
}
pop();
function hexagon(t1) {
for (var i = 0; i < 6; i++) {
t1.setColor(color(175, 35, 45));
t1.forward(10);
t1.left(60);
}
}
function circle(t2) {
for (var i = 0; i < 20; i++) {
t2.setColor(color(213, 215, 181, 60));
t2.forward(0.5);
t2.left(360/20);
}
}
//======= CONDENSED TURTLE CODE =======//
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;}}
thlai-LookingOutwards-10
thlai-Project-10-Landscape
Generative Landscape
I struggled a lot with this project. There were so many little things I wanted to do but didn’t know how to execute. I wanted to create a calming lake and mountains landscape with the sun in the background and used a purple color scheme and gradients to achieve this. The starter code helped me a lot with the clouds. The most difficult part, I think, was creating the reflections in the water – I used the noise() function to create them and used a similar code to the mountains. In the end, I used fog image to spice things up and create atmosphere.
// Tiffany Lai
// 15-104, Section A
// thlai@andrew.cmu.edu
// Project 10 - Landsape
var clouds = []; // array for clouds
var waterX = []; // array for x position of reflection lines
var waterY = []; // array for y position of reflection lines
var speed1 = 0.0002; // for mountain1
var peaks1 = 0.03; // for mountain1
var speed2 = 0.0005; // for mountain2
var peaks2 = 0.03; // for mountain2
var boatX = 250; // x coordinates of boat
var img; // variable to store fog filter
function preload() {
img = loadImage("https://i.imgur.com/aoZ4YwF.png"); // fog image
}
function setup() {
createCanvas(480, 480);
// initial collection of clouds
for (var i = 0; i < 5; i++) {
var rx = random(width);
clouds[i] = makeClouds(rx);
}
for (var i = 0; i < 50; i++) {
var sparkleX = random(0, width);
var sparkleY = random(height * 2 / 3, height);
waterX.push(sparkleX);
waterY.push(sparkleY);
}
}
function draw() {
var num = height; // number of lines for gradient bg
// gradient background
for (var i = 0; i < num; i++) {
strokeWeight(2);
stroke(100 + 1.2 * i, 50 + i, 100 + 0.7 * i);
line(0, i, width, i);
}
sun(); // draw sun
updateClouds(); // update and display clouds
removeClouds(); // remove clouds that have slippsed out of view
addClouds(); // add new clouds with random probability
mountain1(); // draw first mountains
mountain2(); // draw second mountains
water(); // draw water and reflections
boat(boatX); // draw boat
boatX -= 0.5; // boat speed
if (boatX < -50) { // if boat exits left, make it come in right
boatX = width;
}
image(img, 0, 0, width, height); // load fog
}
function sun() { // sun and rays
noStroke();
fill(255, 200);
ellipse(width / 3, height / 3, 170, 170); // sun
for (var i = 0; i < 55; i++) { // sun rays
strokeWeight(1);
stroke(color(220 + 2 * i, 120 + i, 130 + 1 * i, 100)); // gradient
noFill();
ellipse(width / 3, height / 3, 170 + i * i * 0.2, 170 + i * i * 0.2); // distance between circles
}
}
function updateClouds() {
for (var i = 0; i < clouds.length; i++) {
clouds[i].move();
clouds[i].display();
}
}
function removeClouds() {
var keepClouds = [];
for (var i = 0; i < clouds.length; i++) {
if (clouds[i].x + clouds[i].breadth > 0) {
keepClouds.push(clouds[i]);
}
}
clouds = keepClouds;
}
function addClouds() {
// probability of cloud appearing
var cloudChance = 0.007;
if (random(0, 1) < cloudChance) {
clouds.push(makeClouds(width));
}
}
function moveClouds() { // speed of clouds
this.x += this.speed;
}
function displayClouds() {
fill(255, 50);
noStroke();
ellipse(this.x, this.y, this.width, this.height);
ellipse(this.x + 10, this.y + 10, this.width - 10, this.height - 10);
ellipse(this.x + 20, this.y - 10, this.width / 2, this.height / 2);
ellipse(this.x - 20, this.y, this.width - 20, this.height - 10);
}
function makeClouds(cloudX) {
var cloud = {
x: cloudX,
y: random(0, height / 2 - 100),
speed: random(-1, -0.1),
width: random(40, 100),
height: random(20, 50),
breadth: 50,
move: moveClouds,
display: displayClouds,
}
return cloud;
}
function mountain1() { // background mountains
noStroke();
fill(135, 86, 110);
beginShape();
for (var x = 0; x < width; x++) {
var t = (x * peaks1) + (millis() * speed1);
var y = map(noise(t), 0, 1, height / 5, height / 2);
vertex(x, y);
}
vertex(width, height);
vertex(0, height);
endShape();
}
function mountain2() { // middleground mountains
// noStroke();
stroke(70, 63, 86);
strokeWeight(1);
// beginShape();
for (var x = 0; x < width; x++) {
var t = (x * peaks2) + (millis() * speed2);
var y = map(noise(t), 0, 2, height / 2, height / 3);
line(x, 330 + y / 2, x, 330 - y / 2);
}
}
function water() { // water and reflections
noStroke();
fill(170, 120, 126, 100);
rect(0, height - height / 3, width, height);
for (var i = 0; i < 15; i++) {
var t = (i * peaks2) + (millis() * speed2);
var widthChange = (map(noise(t), 0, 1, 0, 100));
stroke(255, 70);
line(waterX[i] - widthChange, waterY[i], waterX[i] + widthChange, waterY[i]);
}
}
function boat(boatX) { // make boat
noStroke();
fill(50, 43, 76);
triangle(boatX + 31, 367, boatX + 31, 393, boatX + 16, 393);
triangle(boatX + 33, 374, boatX + 34, 393, boatX + 47, 393);
quad(boatX + 15, 396, boatX + 23, 405, boatX + 40, 405, boatX + 47, 396);
stroke(255, 50);
line(boatX + 15, 405, boatX + 45, 405);
}
thlai-Project-09-Portrait
I went through many iterations of shapes, trying to see which one looked the best with my photo. I tried rectangles, lines, and ellipses, but ended up going with straight diagonal lines. This gives the photo the feeling of looking through rainy glass:
// Tiffany Lai
// 15-104, Section A
// thlai@andrew.cmu.edu
// Project 09
var portrait; // variable to store image
function preload() {
var imageURL = "https://i.imgur.com/2w1VggQ.jpg";
portrait = loadImage(imageURL);
}
function setup() {
createCanvas(480, 480);
background(255);
portrait.loadPixels();
}
function draw() {
var px = random(width); // x location of line
var py = random(height); // y location of line
var ix = constrain(floor(px), 0, width-1);
var iy = constrain(floor(py), 0, height-1);
var colorAtLocation = portrait.get(ix, iy); // get color at location x y
var randomSize = random(0, 15); // random size of line
stroke(colorAtLocation);
strokeWeight(3);
line(px, py, px + randomSize, py + randomSize); // draw lines
var colorAtMouse = portrait.get(mouseX, mouseY);
fill(colorAtMouse);
line(mouseX, mouseY, mouseX + randomSize, mouseY + randomSize); // draw line where mouse is
}
thlai-LookingOutwards-09
2016 AICP Sponsor Reel
I asked my friend Heidi what her favorite Looking Outwards project was, and this was it. I had actually watched this video before – I think it went viral about a year or so ago. I agree with what Heidi says in her Looking Outwards post: “…it looks so believable, and yet the textures and behaviors of the people dancing…would never make sense in real life.” When I first watched the video, I actually thought it was people dancing in intricate costumes because the movements and textures looked so realistic. I think the dancing and colors really capture the energy of the music, and the fast paced changes in costume keeps the viewers on their feet and keeps them interested. I think the creators did motion capture technology justice. I agree with what Heidi says her her post. The project is incredibly imaginative and entertaining to watch.
thlai-LookingOutwards-08
Above is Yuri Suzuki’s presentation at the Eyeo Festival in 2015.
Below is one of Suzuki’s works I was most interested in.