ammar hassonjee and lee chu – final project

sketch

// Ammar Hassonjee | lee chu
// section c
// ahassonj@andrew.cmu.edu | lrchu@andrew.cmu.edu
// final project


var mode = 0; // variable for cycling through frames

// variables for the beachBall ball scene
// choosing an initial point to launch the ball from
var ballx = 240;
var bally = 100;
var dir1 = 1; // direction of ball
var dir2 = 1; // direction of ball
var speedx = 1; // speed of the ball
var speedy = 0.5;
var size = 100; // size of the ball

// variables for rain scene
var raindrops = []; // array to store rain objects
var rainSpeed = 5; // tracking the speed of the rain
var increment = 0.01; //incrementing the rain values
var tone = 255; // the color of the lightning
var strweight = 30; // stroke weight of the lightning bolt
var cloudNumber; //
var cloudwidth;
var cloudheight;
var frame = 8; // keeping track of the frame count

var myCaptureDevice;
var px = [];
var py = [];
var theColorAtPxPy;
var theBrightnessOfTheColorAtPxPy;
var brightnessThreshold;
var darknessThreshold = 10;

// variables for determining silhouette
var borderX = [];
var borderY = [];
// variables for particles
var parties = [];
var ex = 20;
var why = 20;


function setup() {
    createCanvas(480, 360);
    myCaptureDevice = createCapture(VIDEO);
    myCaptureDevice.size(480, 360); // attempt to size the camera. 
    myCaptureDevice.hide(); // this hides an unnecessary extra view.


    // three variables declared to initialize the lighting function
    var xi = random(40, 440);
    var xii = random(30, 450);
    var xiii = random(20, 460);

    //bImage.resize(480, 360);
}


function isColor(c) {
    return (c instanceof Array);
}


//--------------------------------------------------------------------
function draw() {
    background(220);
    myCaptureDevice.loadPixels();

    image(myCaptureDevice, 0, -0, width, height);  // draw the camera at 1:1 resolution

    // ammar's code
    if (mode === 0) {
        initialScene();
    }
    if (mode === 1) {
        // varying the clouds for each time the rain scene is called
        // done before rainFall is called to ensure this code doesn't loop again
        cloudNumber = random(3, 8);
        cloudwidth = random(105, 140);
        cloudheight = random(60, 95);
        //calling the beach ball scene
        ballSlap();
    }
    if (mode === 2) {
        rainFall();
    }

    // lee's code
    findBorder(); // finding silhouette of person
    if (mode === 3) {
    	// particle effects
    	addParti();
	    updateAndDisplayParti();
	    removeParti();
    }
    if (mode === 4) {
    	// censoring effect
    	censor();
    }
}


function mousePressed() {
    mode = (mode + 1) % 5; // code to swtich between modes
}


//-------AMMAR'S CODE----------------------------------------------------------------------
// NOTE: the code works best if users are wearing dark clothing against a light background
function initialScene() { // initial scene for webcam text
    textAlign(CENTER, CENTER);
    textSize(15);
    fill(100);
    text("WELCOME TO AMMAR AND LEE'S WEBCAM MACHINE", width / 2, height / 2);
    text("Press the mouse to cycle between scenes", width / 2, height * .8);
    text("Press 'O' to increase darkness threshold", width / 2, height * .85);
    text("Press 'P' to decrease darkness threshold", width / 2, height * .9);
    text("Press 'R' to detect threshold automatically", width / 2, height * .95);
}

function ballSlap() {
  // Creating the ball and setting its movement by changing
        //  ballx and bally variables
    beachBall(ballx, bally, size);
    ballx += dir1 * speedx;
    bally += dir2 * speedy;

    // Making ball bounce off the right
    if (ballx >= width - size / 2 || ballx <= size / 2) {
        dir1 = -dir1;
      }

      // Making the ball bounce off the top and bottom
    if (bally < size / 2 || bally > height - size / 2) {
        dir2 = -dir2;
      }

    var colorAtBall = myCaptureDevice.get(ballx, (bally + size / 2));
    if (isColor(colorAtBall)) {
            // setting the brightness to a variable and testing if it meets the
            //    the threshold values
            brightnessAtBall = brightness(colorAtBall);
            // if the ball hits the user's head (darker pixel) the ball will bounce up
            if (brightnessAtBall < darknessThreshold & dir2 > 0) {
                dir2 = -dir2;
                speedy = random(0.5, 5);
                speedyx = random(-4, 2);
              }
    }
}

function beachBall(x, y, diameter) { // function for creating rendering the beach ballx
    var ballcolors = ["RED", "BLUE", "YELLOW"];
    noStroke();
    fill(255);
    ellipse(x, y, diameter, diameter);
    fill(ballcolors[0]);
    arc(x, y, diameter, diameter, radians(30), radians(90));
    fill(ballcolors[1]);
    arc(x, y, diameter, diameter, radians(150), radians(210));
    fill(ballcolors[2]);
    arc(x, y, diameter, diameter, radians(270), radians(330));
    fill(255);
    ellipse(x, y, diameter / 5, diameter / 5);
}

function keyPressed() { // allowing the user to alter the beachball size with keys
    // increasing the ball size if the a key is pressed
    if (key === 'a' || key === 'A') {
        if (size < 200) {
            size += 10;
        }
    }

    // decreasing the ball size if the d key is pressed
    if (key === 'd' || key === 'D') {
        if (size > 50) {
            size -= 5;
        }
    }

    // this code allows the user to edit the darkness threshold in order to fix bugs in the webcam
    if (key === 'o' || key === 'O') {
        if (darknessThreshold < 100) {
            darknessThreshold += 2;
        }
    }

    // decreasing the darkness threshold
    if (key === 'p' || key === 'P') {
        if (darknessThreshold > 10) {
            darknessThreshold -= 2;
        }
    }

    // finding threshold automatically
    if (key ==='r' || key === 'R') {
    	findThreshold();
    }

}

function rainFall() { // function for creating the rain scene
    clouds(cloudNumber, cloudwidth, cloudheight); // calling the cloud function
    // these lines of called call the function for the rain objects to make ensure
    //    the rain continuously falls
    updateRain();
    removeRain();
    addRain();

    // these if statements remap the speed of the rain to change as time passes
    rainSpeed += increment;
    if (rainSpeed >= 15) {
        increment = -0.01;
    }
    if (rainSpeed <= 5) {
        increment = 0.01;
    }

    // code to implement the lighting function at a random timeout
    // this first if statement sets the lighting variables to an initial value
    //      and increments the frame variable
    if (frameCount % 200 === 0) {
        frame++;
        tone = 255;
        xi = random(40, 440);
        xii = random(30, 450);
        xiii = random(20, 460);
        strweight = 30;
    }

    // a separate frame variable is used to make sure the lighning stays for multiple frames
    if (frame % 3 === 0) {
        // a random variable is used to make sure the lightning is called unpredictably
        var chance = random(0, 2);
        lightning(strweight, tone, xi, xii, xiii);
        tone -= 4; // altering the lighting render values for aesthetic effect
        strweight -= 1;
    }

}

function makeRain(xpos, ypos) { // function for making the rain objects
    var rain = {x: xpos, y: ypos, move: rainMove, display: rainDraw};
    return rain;
}

function rainDraw() { // function for drawing the rain particles
    noStroke();
    fill(0, 0, 240);
    push();
    translate(this.x, this.y);
    rotate(radians(15));
    ellipse(0, 0, 2, 10);
    pop();
}

function rainMove() { // moving the rain by the rain speed global variable
    this.y += rainSpeed;
}

function updateRain() {
  // for loop that calls train track objects and moves/displays them
    for (var i = 0; i < raindrops.length; i++) {
        var colorAtRain = myCaptureDevice.get(raindrops[i].x, raindrops[i].y);
        raindrops[i].move();
        if (isColor(colorAtRain)) {
              // setting the brightness to a variable and testing if it meets the
              //    the threshold values
              brightnessAtRain = brightness(colorAtRain);

              // this ensures the rain stops falls on top of the figure in the WEBCAM
              //    by comparing to the pixel brightness
              if (brightnessAtRain >= darknessThreshold) {
                  raindrops[i].display();
                }
              }

      }
}

function addRain() { // function for adding rain objects to raindrops array
    for (var i = 0; i < 6; i++) {
       raindrops.push(makeRain(random(0, width), 0));
    }
}

function removeRain() { // function for removing objects from the array
    var rainKeep = [];
    for (var i = 0; i < raindrops.length; i++) {
        if (raindrops[i].y < 380) {
            rainKeep.push(raindrops[i]);
        }

      }
    raindrops = rainKeep; // reassigning the raindrops array to the duplicate array
}

// this function calls the lighting from a list of values that create polylines
function lightning(weight, brightness, xpos, xpos2, xpos3) {
    noFill();
    strokeWeight(weight);
    stroke(brightness);
    var x2 = xpos + random(-10, 10);
    var x3 = xpos2 + random(-20, 25);
    var x4 = xpos3 + random (-30, 40);
    var y1 = random(height / 4, height / 3);
    var y2 = random(height / 8 * 5, height * 2 / 3);
    line(xpos, 0, x2, y1);
    line(x2, y1, x3, y2);
    line(x3, y2, x4, height);
}

// function that calls the clouds from a list of variables
function clouds(number, length, cheight) {
    noStroke();
    for (var i = 0; i < number + 1; i++) {
        fill( map(i, 0, number, 100, 150));
        var cloudx = map(i, 0, number, 0, width);
        ellipse(cloudx, 0, length, cheight + 15 * (i % 3));
    }
}


//-------LEE'S CODE----------------------------------------------------------------------

function findBorder() {
	borderX = [];
	borderY = [];
	// finds the silhouette of person based on darkness threshold in intervals of 5
	for (var i = 0; i < width; i += 5) {
		for (var j = 0; j < height; j += 5) {
			if (brightness(myCaptureDevice.get(i, j)) < darknessThreshold & 
				borderX.length < i / 5) {
					borderX.push(i);
					borderY.push(j);
			}
		}
	}
}

function findThreshold() {
	var brightest = 50;
    var darkest = 50;
    var xColors = [];
    var xx = [];
    // stores brightnesses of all colors along a line in the image displayed
    for (var j = 0; j < width; j += 5) {
        xColors.push(brightness(myCaptureDevice.get(j, height / 2)));
        xx.push(j);
    }
    // finds brightest and darkest pixels
    for (var k = 0; k < xColors.length; k ++) {
        if (xColors[k] > xColors[brightest]) {
            brightest = k;
        }
        if (xColors[k] < xColors[darkest]) {
            darkest = k;
        }
    }
    // elementary ratio to determine thresholds
    brightnessThreshold = (xColors[brightest] - xColors[darkest]) / 3 + xColors[darkest];
    darknessThreshold = (xColors[brightest] - xColors[darkest]) / 5 + xColors[darkest] - 10;
}

function addParti() {
	// creating particles along top of person
	var likelihood = 0.45;
	for (var i = 0; i < borderX.length; i ++) {
		if (random(1) < likelihood) {
			parties.push(createParticle(borderX[i], borderY[i]));
		}
	}
}

function removeParti() {
	// removes particle when size decreases below 1
	var partiesToKeep = [];
	for (var i = 0; i < parties.length; i ++) {
		if (parties[i].size > 1) {
			partiesToKeep.push(parties[i]);
		}
	}
	parties = partiesToKeep;
}

function updateAndDisplayParti() {
	for (var i = 0; i < parties.length; i ++) {
		parties[i].move();
		parties[i].display();
	}
}

function createParticle(birthX, birthY) {
	var parti = {x: birthX, y: birthY, size: round(random(5, 15)),
				speed: -5, move: partiMove, display: partiDisplay,
				color: myCaptureDevice.get(birthX, birthY)}
	return parti;
}

function partiMove() {
	// move particle and decrease size
	this.y += this.speed;
	this.size -= 1;
}

function partiDisplay() {
	strokeWeight(0);
	fill(this.color);
	push();
	translate(this.x, this.y);
	rotate(millis() / 1000 * 2 * PI);
	rectMode(CENTER);
	rect(0, 0, this.size, this.size);
	pop();
}

function censor() {
	// divides area below silhouette into 25 by 25 squares and extracts colors
	for (var i = 0; i < borderX.length; i += 5) {
		for (var j = borderY[i]; j < height; j += 25) {
			push();
			translate(borderX[i], j);
			rectMode(CENTER)
			fill(myCaptureDevice.get(borderX[i], j));
			rect(0, 0, 25, 25);
			pop();
		}
	}
}

Ammar Hassonjee and Lee Chu collaborated on this webcam machine. It consists of four scenes: a beachball headbutting game, a rainy day, snapped by Thanos, and an unnecessary censorship. Click the mouse to cycle through the scenes. Press the keys O and P to increase or decrease the darkness threshold depending on the lighting of your setting. Press the key R to automatically detect the darkness threshold, although this process may not be perfect.

lee chu – project 12 proposal

I plan to collaborate with Ammar Hassonjee on the final project. We are thinking about creating a webcam overlay that could detect faces and project a filter over it, much like snapchat or instagram filters. Ideally, by detecting the colors in the frame of the webcam, we can identify and track a face. While tracking the face, sounds may be triggered by a specific action, and the entire filter could be dynamic depending on the user’s movements and actions. We will most definitely need to look into how face tracking generally works, as well.

lee chu – looking outwards 11

phonelovesyoutoo: database by Kate Hollenbach displays the feed of both the front and rear-facing cameras. The recordings are displayed at the original time of day, depicting a glimpse of the relationship between human, device, time, and space. This collection of videos really highlights the information we essentially give for free just by owning a device with so many sensors and recording devices.

Kate Hollenbach is an artist, programmer, and educator based in Chicago and Los Angeles. She explores interactive systems and technologies involving body, gesture, and physical space.

lee chu – project 11

lrchu

// lee chu
// section c
// lrchu@andrew.cmu.edu
// project - 11


var islands = [];
var hills = [];
var x; // cloud x

// colors
var navajo = '#FFE4A3';
var lPink = '#FFAFBA';
var peachOrange = '#EDD397';
var mauve = '#E8A0A9';
var peach = '#FFEAB7';
var taupe = '#544244';
var puce = '#4C3C3E';
var middleYellow = '#F4B086';


function setup() {
    createCanvas(480, 300);
    // create an initial collection of islands
    for (var i = 0; i < 2; i++){
        var rx = random(40, width - 40);
        islands[i] = makeIsland(rx);
    }
    for (var i = 0; i < 3; i++){
        var rx2 = random(40, width - 40);
        hills[i] = makeHill(rx2);
    }
    frameRate(10);
    x = random(40, width - 40);
}


function draw() {
    sky();
    horizon();
    // sun
    fill(peachOrange);
    ellipse(width / 2, height / 2, 100);
    // islands
    updateAndDisplayIslands();
    removeIsland();
    addIsland();
    // cloud
    cloud(x, 80);
    x += 1;
    if (x - 50 > width - 40) {
        x = -10;
    }
    // hills
    updateHills();
    removeHill();
    addHill();
    // window
    fill(0, 0, 50);
    windowFrame(40, 60);
    fill(75, 75, 100);
    windowFrame(28, 55);
}


function cloud(x, y) {
    strokeWeight(0);
    fill(255);
    rect(x, y, 20, 10);
    rect(x + 10, y + 10, 20, 10);
    rect(x - 10, y + 10, 20, 10);
    rect(x + 20, y + 20, 20, 10);
    rect(x - 10, y + 20, 20, 10);
    rect(x - 30, y + 20, 20, 10);
    rect(x - 30, y + 30, 20, 10);
    rect(x - 50, y + 30, 20, 10);
    rect(x - 20, y + 30, 20, 10);
    rect(x + 10, y + 30, 20, 10);
    rect(x + 30, y + 30, 20, 10);
    rect(x - 20, y + 40, 30, 10);
}


function makeIsland(birthLocationX) {
    var island = {x: birthLocationX, breadth: round(random(25, 50)),
                    speed: -1,
                    height: round(random(80, 150)),
                    move: islandMove,
                    display: islandDisplay}
    return island;
}


function islandMove() {
    this.x += this.speed;
}


function islandDisplay() {
    // face
    fill(middleYellow);
    push();
    translate(this.x, height - 150);
    beginShape();
    vertex(0.4 * this.breadth, -this.height / 2);
    vertex(0.6 * this.breadth, -this.height / 2);
    vertex(this.breadth, this.height / 2);
    vertex(0.8 * this.breadth, this.height / 2 * 1.2);
    vertex(0.4 * this.breadth, this.height / 2 * 1.2);
    vertex(0.1 * this.breadth, this.height / 2);
    endShape(CLOSE);
    // shadow
    fill(taupe);
    beginShape();
    vertex(0.4 * this.breadth, -this.height / 2);
    vertex(0.1 * this.breadth, this.height / 2);
    vertex(0.4 * this.breadth, this.height / 2 * 1.2);
    vertex(0.35 * this.breadth, this.height / 2);
    endShape(CLOSE);
    pop();
}


function updateAndDisplayIslands() {
    for (var i = 0; i < islands.length; i ++) {
        islands[i].move();
        islands[i].display();
    }
}


function removeIsland() {
    var islandsToKeep = [];
    for (var j = 0; j < islands.length; j ++) {
        if (islands[j].x + islands[j].breadth > 40) {
            islandsToKeep.push(islands[j]);
        }
    }
    islands = islandsToKeep;
}


function addIsland() {
    var likelihood = 0.007;
    if (random(1) < likelihood) {
        islands.push(makeIsland(width));
    }
}


function makeHill(x) {
    h = round(random(150, 200));
    var spike = {x: x, breadth: round(random(25, 50)),
                speed: -3,
                height: round(random(200, 250)),
                move: hillMove,
                display: hillDisplay}
    return spike;
}


function hillMove() {
    this.x += this.speed;
}


function hillDisplay() {
    fill(peach);
    push();
    translate(this.x, height - 50);
    beginShape();
    vertex(0, 0);
    vertex(this.breadth, 0);
    vertex(0.9 * this.breadth, -this.height * 0.25);
    vertex(0.7 * this.breadth, -this.height / 2);
    vertex(0.6 * this.breadth, -this.height);
    vertex(0.4 * this.breadth, -this.height);
    vertex(0.2 * this.breadth, -this.height / 2);
    endShape(CLOSE);
    pop();
}


function updateHills() {
    for (var i = 0; i < hills.length; i ++) {
        hills[i].move();
        hills[i].display();
    }
}


function removeHill() {
    var hillsToKeep = [];
    for (var j = 0; j < hills.length; j ++) {
        if (hills[j].x + hills[j].breadth > 40) {
            hillsToKeep.push(hills[j]);
        }
    }
    hills = hillsToKeep;
}


function addHill() {
    var likelihood = 0.01;
    if (random(1) < likelihood) {
        hills.push(makeHill(width));
    }
}


function windowFrame(r, f) {
    strokeWeight(0);
    // top left
    beginShape();
    vertex(0, 0);
    vertex(100, 0);
    vertex(100, r);
    bezierVertex(f, r, r, f, r, 100);
    vertex(0, 100);
    endShape(CLOSE);
    rect(100, 0, width - 200, r);
    // top right
    beginShape();
    vertex(0, height);
    vertex(100, height);
    vertex(100, height - r);
    bezierVertex(f, height - r, r, height - f, r, height - 100);
    vertex(0, height - 100);
    endShape(CLOSE);
    rect(width - r, 100, r, height - 200);
    // bottom right
    beginShape();
    vertex(width, 0);
    vertex(width - 100, 0);
    vertex(width - 100, r);
    bezierVertex(width - f, r, width - r, f, width - r, 100);
    vertex(width, 100);
    endShape(CLOSE);
    rect(100, height - r, width - 200, r);
    // bottom left
    beginShape();
    vertex(width, height);
    vertex(width - 100, height);
    vertex(width - 100, height - r);
    bezierVertex(width - f, height - r, width - r, height - f, width - r, height - 100);
    vertex(width, height - 100);
    endShape(CLOSE);
    rect(0, 100, r, height - 200);

    // lines
    stroke(0);
    strokeWeight(0.5);
    line(0, height - 20, width, height - 20);
    strokeWeight(0.25);
}


function sky() {
    for (var y = 40; y < height - 69; y ++) {
        var interval = map(y, 40, height - 69, 0, 1);
        var c = lerpColor(color(mauve), color(peachOrange), interval);
        strokeWeight(1);
        stroke(c);
        line(0, y, width, y);
    }
}


function horizon(){
    strokeWeight(0);
    fill(navajo);
    beginShape();
    vertex(0, height - 70);
    vertex(width, height - 70);
    vertex(width, height);
    vertex(0, height);
    endShape(CLOSE);
}

I aimed to emulate the view from inside of a train outwards into a scene with floating islands.

lee chu – project 10

lrchu



// lee chu
// section c
// lrchu@andrew.cmu.edu
// project - 10


var ampTog;
var freqTog;
var recordSpinL;
var recordSpinR;
var prevR;
var play;

var playToggle;

var scratch;
var scratch2;
var buildup;
var drop;
var volume;

var visualImage;

var px = [];
var py = [];
var dx = [];
var dy = [];

function preload() {
	// load sounds
	scratch = loadSound('https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/11/scratch.wav');
	scratch.setVolume(0.08);
	scratch2 = loadSound('https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/11/scratch2.wav');
	scratch2.setVolume(0.2);
	buildup = loadSound('https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/11/buildup.wav');
	buildup.setVolume(0.5);
	drop = loadSound('https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/11/wheredyago.wav');
	drop.setVolume(0.3);

    // load visuals
    visualImage = loadImage('https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/11/visual.jpg');
}

function setup() {
    createCanvas(500, 500);
    recordSpinL = 0; recordSpinR = 0;
    volume = 0.2;
    playFill = 'red';
    playToggle = 0;

    visualImage.resize(width, height);
    visualImage.loadPixels();

    // visuals setup
    for (var i = 0; i < 150; i ++) {
    	px.push(random(width));
    	py.push(random(height));
    	dx.push(1);
    	dy.push(1);
    }
}

function draw() {
	if (drop.isPlaying() == false) {
		background(255, 255, 220);
	}
	else {
		for (var i = 0; i < px.length; i ++) {
	    	var ix = constrain(floor(px[i]), 0, width-1);
	        var iy = constrain(floor(py[i]), 0, height-1);
	        var theColorAtLocationXY = visualImage.get(ix, iy);

	        noStroke();
	        fill(theColorAtLocationXY);
	        rectMode(CENTER);
	        rect(px[i], py[i], 30, 30);

	        // random movement
	        px[i] += dx[i] * random(-2, 10);
	        py[i] += dy[i] * random(-2, 10);

	        // keeping painters on the canvas
	        if (px[i] > width) {
	            px[i] = 0;
	        }
	        else if (px[i] < 0) {
	            px[i] = width;
	        }
	        if (py[i] > height) {
	            py[i] = 0;
	        }  
	        else if (py[i] < 0) {
	            py[i] = height;
        	}
    	}
	}
    
	// sound board
	soundBoard();
	dial(width / 2 - 30, 2 * height / 3 - 5);
	dial(width / 2 + 30, 2 * height / 3 - 5);

    // play button
	fill(45);
	rect(width / 2 + 4, 3 * height / 4 + 4, 30, 20);
	fill(playFill);
	stroke('white');
	strokeWeight(0.5);
	rect(width / 2, 3 * height / 4, 30, 20);
	fill('white');
	triangle(width / 2 - 5, 3 * height / 4 - 4, width / 2 - 5, 3 * height / 4 + 4,
		width / 2 + 5, 3 * height / 4);

	// basssss
	fill(45);
	strokeWeight(0);
	ellipse(width / 2 + 4, 3 * height / 4 + 60 + 4, 40);
	strokeWeight(0.5);
	fill(100);
	if (buildup.isPlaying()) {
		fill('red');
	}
	ellipse(width / 2, 3 * height / 4 + 60, 40);
	strokeWeight(2);
	text('DROP', width / 2 - 17, 3 * height / 4 + 65);
	strokeWeight(0);

	// record
	disc(width / 4, 3 * width / 4 + 10, recordSpinL);
	disc(3 * width / 4, 3 * width / 4 + 10, recordSpinR);
	if (play == 1) {
		recordSpinL += PI / 100;
		recordSpinR += PI / 100;
	}
}

function disc(x, y, r) {
    // shadow
    push();
    translate(x + 5, y + 5);
    fill(25);
    ellipse(0, 0, 155);
    pop();

	// record
	push();
	translate(x, y);
	rotate(r);
	fill(25);
	stroke(120);
	strokeWeight(3);
	ellipse(0, 0, 155);
	fill(30, 40, 185);
	strokeWeight(1);
	ellipse(0, 0, 40);
	fill(100);
	strokeWeight(0);
	ellipse(0, 0, 6);

	// scratches
	noFill();
	strokeWeight(0.3);
	arc(0, 0, 140, 140, 0, PI / 4);
	arc(0, 0, 120, 120, PI / 2, 5 * PI / 4);
    strokeWeight(0.25);
    arc(0, 0, 75, 75, 0, PI / 2);
    arc(0, 0, 85, 85, PI, 7 * PI / 4);

    // more scratches
    strokeWeight(0.75);
    line(0, 30, 0, 66);
    line(-30, 0, -70, 0);
    strokeWeight(0.35);
    line(20, -20, 45, -45);
    line(22, 22, 48, 48);
	pop();
}
	

function dial(x, y, r) {
	// shadow
	push();
	translate(x + 5, y + 5);
	fill(50);
	strokeWeight(0);
	ellipse(0, 0, 35);
	pop();

	// knob
	push();
	translate(x, y);
	rotate(0);
	fill(100);
	stroke(180);
	strokeWeight(0.75);
	ellipse(0, 0, 35);

	// pointer
	stroke('white');
	strokeWeight(1);
	line(0, 0, 0, -35 / 2);
	pop();
}

function soundBoard() {
	rectMode(CENTER);
	strokeWeight(0);
	// shadow
	fill(75);
	rect(width / 2 + 10, 4 * height / 5 - 20 + 10, 7 * width / 8, height / 2.5, 20);
	// red
	fill(175, 50, 50);
	rect(width / 2, 4 * height / 5 - 20, 7 * width / 8, height / 2.5, 20);
}

function mousePressed() {
	// toggles spinning records
	play = 1;
	// left scratch sound
	if (dist(mouseX, mouseY, width / 4, 3 * width / 4 + 10) < 77.5) {
		recordSpinL -= PI / 4;
		if (scratch.isPlaying()) {
			scratch.stop();
		}
		scratch.play();
	}
	// right scratch sound
	if (dist(mouseX, mouseY, 3 * width / 4, 3 * width / 4 + 10) < 77.5) {
		recordSpinR-= PI / 4;
		if (scratch2.isPlaying()) {
			scratch2.stop();
		}
		scratch2.play();
	}
	// play button
	if (dist(mouseX, mouseY, width / 2, 3 * height / 4) < 20) {
		playFill = 'green';
		if (buildup.isPlaying()) {
			buildup.stop();
		}
		buildup.play();
	}
	// bass button
	if (dist(mouseX, mouseY, width / 2, 3 * height / 4 + 60) < 20 & buildup.isPlaying()) {
		if (buildup.isPlaying()) {
			buildup.stop();
		}
		if (drop.isPlaying()) {
			drop.stop();
		}
		drop.play();
	}
}

In this primitive virtual audio mixer, I included two dj scratch sounds on each disc, a play button to activate the build up, and a button which drops the bass c:

lee chu – looking outwards 10

visuals from Porter Robinson’s Worlds tour

Porter Robinson, renown producer of electronic music, is also well known for his visuals at his live performances. Not only do the visuals need to be visually stunning, but they also need to correlate with and move with the music logically.

Porter aimed to bring beautiful and immersive sounds and visions to a genre of music too often associated with heavy bass drops. Porter sought to transport his audiences into hyper-real, neon-toned worlds of anime and MMORPG’s. These videogame-esque worlds unfold before you to bring back that childhood escapism into fantasy and fictional worlds.

lee chu – looking outwards 09

An interesting post I saw was Mike’s looking outwards 09, about Tai’s post, about Refik Anadol, a media artist from Turkey, who currently lives and works in Los Angeles. He thoroughly explores the relationship between architecture and media arts, merging machine learning and visual/audio experiences. He creates installations, but has also projected his work onto existing architecture.

As Mike had mentioned, what’s most impressive is the rigorous amount of effort to perfectly map projections onto a piece of irregular architecture, such as the Disney Hall in Los Angeles. Look at some of his work here.

one of refik’s renderings – melting memories

lee chu – project 09

lrchu

// lee chu
// section c
// lrchu@andrew.cmu.edu
// project - 09

var underlyingImage;
var px = [];
var py = [];
var directionX = [];
var directionY = [];
var ix = [];
var iy = [];

function preload() {
    var myImageURL = "https://i.imgur.com/QpJ7uJf.jpg";
    underlyingImage = loadImage(myImageURL);
}

function setup() {
    createCanvas(600, 300);
    background(0);
    underlyingImage.loadPixels();
    frameRate(60);

    // starting arrays for painters
    px.push(random(width));
    py.push(random(height));
    directionX.push(1);
    directionY.push(1);
}

function draw() {
    underlyingImage.resize(width, height); 

    // looping for individual painters
    for (i = 0; i < px.length; i ++) {
        ix = constrain(floor(px[i]), 0, width-1);
        iy = constrain(floor(py[i]), 0, height-1);
        var theColorAtLocationXY = underlyingImage.get(ix, iy);

        noStroke();
        fill(theColorAtLocationXY);
        rectMode(CENTER);
        rect(px[i], py[i], 10, 10);

        // random movement
        px[i] += directionX[i] * random(-2, 10);
        py[i] += directionY[i] * random(-2, 10);

        // keeping painters on the canvas
        if (px[i] > width) {
            px[i] = 0;
        }
        else if (px[i] < 0) {
            px[i] = width;
        }
        if (py[i] > height) {
            py[i] = 0;
        }  
        else if (py[i] < 0) {
            py[i] = height;
        }
    }
}

// adding painters with click
function mousePressed() {
    px.push(random(width));
    py.push(random(height));
    directionX.push(1);
    directionY.push(random(-1, 1, 2));
}

I miss Heath Ledger. I had wanted to create a bunch of little rectangular painters which make their way across the canvas.

what the end result should look like – CLICK on the canvas to introduce more painters

lee chu – looking outwards 08

Nervous System is a design studio that works at the intersection of science, art, and tech. Founded in 2007 by Jesse Louis-Rosenburg and Jessica Rosenkrantz, Nervous System focuses on creating generative online applications in which users can co-create products resulting in infinite design possibilities for homeware, lighting, jewelry, and most fascinating, clothing. Some of their simpler 3-D designs can also be downloaded and printed at home. Check out their website here.

One of their more interesting designs is of a parametric dress which is fully 3-D printed using smaller modules that can fold and make the dress malleable and form-fitting. In addition, they implemented an application which accounts for body shape and size so that almost anyone can rock this dress.

dress in motion

Eyeo 2015 – Jesse Louis-Rosenburg and Jessica Rosenkrantz

lee chu – looking outwards 07

gun death visualization by Periscope for 2013

Kim Ree, co-founder of a data visualization firm Periscopic, is probably best known for her work in visualizing gun deaths in 2010 and 2013. The diagram above illustrates the overwhelming amount of deaths from U.S. owned guns. The orange strokes depict the actual lifespans of gun victims, and the gray projects an estimated lifespan according to the U.S. distribution of deaths and likely causes of death. By also counting the amount of hours that these victims were robbed of, the data is much more impactful than if it were simply a death count. Play with the visualizer here.

Not speaking in relation to this particular piece, but I think the most interesting aspect about data visualization is that it can be depicted in a way to sway or even change one’s impressions of anything.