thlai/hdw – Project 12 – Final

sketch

// 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();
      }
    }

}
}

thlai-Project12-Proposal

I will be working with classmate Helen Wu from Section A.
I was inspired by one of my favorite arcade games, Dance Dance Revolution, and I thought it would be interesting to create something similar using the arrow keys. Essentially, the player will hit the arrow keys to the beat of a song while arrows move up (see picture). We are hoping to generate arrow keys based on the frequency/volume of the beat to create an entertaining game. I want to find a way to keep track of highest scores as well.

thlai-LookingOutwards12-Priors-and-Precursors

Dance Dance Revolution heavily inspired my Project 12 proposal, and although it is not necessarily an art piece, I think the game has several extremely interesting interactions that I could use for my project. Dance Dance Revolution, or DDR, did not originate in America – it originated in Japan in 1998 and was developed by a video game company called Konami. At the time, the game was critically acclaimed for being original and upbeat, and now it is seen not only in arcades but also in homes. The premise of the game is to step on the arrows that correspond with the ones shown on the screen, typically against another player, for points.
I definitely wonder about the code that created this game – I imagine it to be fairly complicated, since I can only comprehend maybe a quarter of what is going on. Either way, I find it an incredibly game and project. It brings back a lot of memories for me.

The second project is by a design creative studio called Design IO LLC called Connected Worlds. Connected Worlds is an interactive connected ecosystem that consists of six different ecosystems on interactive screens. The thing I like most about this installation is how interactive it is to the audience – it is almost like a game, and those are interactions I can use to inspire for my final project for this class, such as moving logs and connecting to creatures. It looks as though you are interacting with these different ecosystems as though you were there.Connected Worlds was developed using openFrameworks.

thlai-LookingOutwards11-Computer-Music

Yuri Suzuki

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.

sketch

// 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

An interfaith mandala DIMONscape® based on the crucifixion
Roz Dimon
Now that it was brought to my attention, most or all of my looking outwards for the past 10 weeks have been about men creators, so it’s a breath of fresh air to focus on something different. Roz Dimon is a digital artist based in NYC – her work has a very unique style that I like, which is why it drew my eye.
Dimon works in a collage style for a lot of her projects, which result in multilayered images and textures composed in a single image. As a communication designer, I find her work engaging and exciting. One of her most recent works is still on exhibition now until November 18th in Connecticut – the exhibition aims to bring together her works that embody multiculturalism and inter-religious unity. I not only like the pieces in this exhibition (which include the works of several artists, Dimon being one of them), but also the message of highlighting the aspects of humanity that unite us rather than divide us. I’ve included an image of Dimon’s piece that is exhibited. It has the same collage-y feel of her other works, although I wish she didn’t write the words “peace, love, justice”. I just don’t think it’s necessary in the context of the piece.

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.

sketch

// 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:

 

sketch

// 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.

Yuri Suzuki is a designer, sound artist, and electronic musician from Tokyo, Japan. He likes to explore the relationship between humans and sound, and how sound affect human minds. He has his own design studio in London and has created works for the Museum of Modern Art in NY.
Suzuki seems to have a very intimate relationship with sound in the work he does. A lot of the work he presented at the Eyeo Festival included using records as his main source of sound. One that impressed me was his spherical record called The Sound of Earth. The record is globe-shaped (hence the name) that plays different music based on which area of land the needle passes over. He presents this work by showing samples of the sounds one would hear if they stood by his project. Video format to showcase his work is definitely the most effective, especially to display sound, which pictures cannot.
The most intriguing thing is how personal this project is. Suzuki traveled around the world, and over the course of four years, collected field recordings (like folk music, national anthems, pop music, etc.) from different countries. A journey around his globe takes 30 minutes.
Suzuki goes on to talk about a lot of his other works, one of which includes embedding 67 speakers into a car that records surrounding road sounds of London. His work seems very playful and whimsical, but he takes his work seriously.