Jenny Hu — Final Project

For my final project, I created a simple step sequencer with 4 sounds and background music! You can add sounds at different points by toggling the squares and circles (the squares indicate a note switched off, and a circle indicates a note switched on), and change the speed the music player loops through the sequencer on the bpm toggle.

I really enjoyed making this because it became about the curation of the sounds and music,  letting anyone generate a soothing atmosphere.

Jenny’s sketch

//Jenny Hu
//Section E
//jjh1@andrew.cmu.edu
//Final Project

//This final project is a simple step sequencer.
//Sounds are loaded into each row,
//as the music player passes each note (switched on), it plays the sound
//squares indicate switched off notes, and circles indicate the ones switched on.

var playing = false;
var notesArray = []; //all notes are pushed into this array

//sizing variables
var totalC = 15; 
var canvasWidth = window.innerWidth; //sizing of everything should overall be parametric to this width.
var margin = 0.02 * canvasWidth; //space between notes and everything else
var noteW = (canvasWidth - (totalC + 1) * margin)/ totalC; //calculated width of each square/circle
var canvasHeight = 5 * margin + 4 * noteW + (noteW*4); 

//variables key to moving and playing components
var playButtonW = margin ; //big play/pause button
var baseLineX = margin; 
var baseLineY = (canvasHeight / 5) * 3.5;
var movingPlayBar = 20; 
var movingPlayBarW = 10; 
var playX; //moving x tracked against the array's x's. 

//variables to make bpm changeable
var bpmEdit = 2;
var bpm;
var bpmX;
var bpmToggleX;
var bpmToggleLength;

var backgroundMusicToggle = false;
var interval;
var volume = 1;

function preload() {
    // load sound files 
    bird = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/182507__swiftoid__bird-chirp.wav");
    bird.setVolume(volume);
    bell = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/173000__keykrusher__bicycle-bell-2.wav");
    bell.setVolume(volume / 3 * 2);
    cello = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/398494__anthousai__wind-chimes-single-01.wav");
    cello.setVolume(volume);
    shaker = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/368607__samsterbirdies__shake.wav");
    shaker.setVolume(volume);
    backgroundMusic = loadSound("https://courses.ideate.cmu.edu/15-104/f2018/wp-content/uploads/2018/12/203099__lemoncreme__groove-music.wav");
    backgroundMusic.setVolume(volume);
}

function setup(){
	createCanvas(canvasWidth, canvasHeight);
	frameRate(1000);
	angleMode(DEGREES);

    bpmToggleLength = width / 8 * 5; 
	bpmX = bpmToggleLength + margin;
	//setting up rows with different sounds
    for (var c = 0; c < totalC; c++){
    	for (var r = 0; r < 4; r++){
    		var x = c * (noteW + margin) + margin;
    		var y = r * (noteW + margin) + margin; 
    		if ( r == 0){
    			snd = bird;
    		} else if (r == 1){
    			snd = bell;
    		} else if (r == 2){
    			snd = cello; 
    		} else {
    			snd = shaker;
    		}
    		var n = Note(x , y, snd, noteW);
            notesArray.push(n);
    	}
    }
}



function draw(){
	background(250, 230, 10);
	var wiggleRoom = 2;

	//intro text
	stroke(0, 0, 250);
	strokeWeight(1);
	fill(0, 0, 250);
	textSize(margin);
	textFont('Courier');
	text('press play to begin!', width / 2 - bpmToggleLength / 1.75, canvasHeight / 8 * 7); 

	//static line
	strokeWeight(5);
	line( baseLineX, baseLineY, canvasWidth - baseLineX, baseLineY);//playbar baseline

	//for loop rendering the array into notes
    for (var i = 0; i < notesArray.length; i++){
    	notesArray[i].render();

    	//play music when the play x and array of on notes collide
    	if (playX >= notesArray[i].x - wiggleRoom 
    		& playX <= notesArray[i].x + wiggleRoom 
    		&& notesArray[i].on == true){
    		notesArray[i].snd.play();
    	}
    }

	bpmToggle();
	playBackground();
	playButton();
    movePlayStream();
}

//function to move the bpm visual ui
function bpmToggle(){
	//shadow
	noStroke();
	fill(250, 250, 250, 150);
	rect( bpmToggleLength + margin, baseLineY + (margin * 2.5), width/5, noteW/5, 80); 
	//bpm text
	stroke(0, 0, 250, 250);
	fill(0, 0, 250);
	strokeWeight(1);
	text('bpm', bpmToggleLength + margin, canvasHeight / 8 * 7 + margin);
	//toggle
	fill(0, 0, 250);
	ellipse(bpmX, baseLineY + (margin * 2) + noteW / 3, noteW / 3, noteW / 3); 
}


//function to play background music
function playBackground(){
	if (backgroundMusicToggle == true){ //counter will be 1, or 2 (on rare occasion), immediate at play
		interval = 1250;
		if (counter  > 0 & counter < 3){
			backgroundMusic.play();
			playing = true;
		} else if ( counter % interval > 0 & counter % interval <= 2){ //interval is based on the timing of the background song
			playing = false;
			counter = 0; //reset counter if you've paused the music
		}
	 } 
}

//play/pause functionality + visuals
//playing is toggled in mousePressed
function playButton(){
	fill(0, 0, 250);

	if (playing == true) { 
		//pause button
		rect(width/2 - playButtonW, baseLineY + (margin * 2) , 
			playButtonW, playButtonW * 3);
		rect(width/2 + playButtonW, baseLineY + (margin * 2), 
			playButtonW, playButtonW * 3);
		//mapping of the bpm toggle to the speed of  playX
		bpmEdit = map(bpmX, 
				bpmToggleLength + noteW / 2, bpmToggleLength - noteW / 2 + width/5, 
				2, 4);
		bpm = bpmEdit; // the set BPM by the user
		backgroundMusicToggle = ! backgroundMusicToggle; //turning on the background music
		counter += 1;

	} else {
		//play button
		triangle ( width/2 - playButtonW, baseLineY + (margin * 2),
				  width/2 - playButtonW, baseLineY + (margin * 2) + noteW * .75 * 2,
				  width/2 - playButtonW + noteW * .75 * 2, baseLineY + (margin * 2) + noteW * .75 )
		bpm = 0; //stop the player if we've paused the switch
		counter = 0;
		backgroundMusic.stop();

	}
}

//draws the animated line
function movePlayStream(){
	playX = baseLineX + movingPlayBar - 20;
	stroke(0, 0, 250);
	fill(250);
	strokeWeight(4);
	//loops the line if the play button moves off the line
	if (playX > canvasWidth - baseLineX) {	
		movingPlayBar = baseLineX;
	} else {
		//the visual parts
		rect(playX, margin / 2, movingPlayBarW, canvasHeight / 5 * 3.5);
		ellipse( playX + movingPlayBarW / 2 , baseLineY, movingPlayBarW*2, movingPlayBarW*2);
	}
	//add whatever the bpm has been toggled to be
    movingPlayBar += bpm;
}



function mousePressed(){
	//turning on and off the individual notes by checking your mouse position
    for (var i = 0; i < notesArray.length; i++){
		if (mouseX > notesArray[i].x 
			& mouseX < notesArray[i].x + noteW 
			&& mouseY > notesArray[i].y
			&& mouseY < notesArray[i].y + noteW){
			var currentToggle = !notesArray[i].on;
			notesArray[i].on = currentToggle;
		}    	
    }
    //switch play/pause mode by clicking on the play/pause button
    if (mouseX > width / 2 - playButtonW 
    	& mouseX < width / 2 + margin + playButtonW 
    	&& mouseY > baseLineY + (margin * 2)
    	&& mouseY < baseLineY + (margin * 2) + playButtonW * 3){
    	playing = ! playing;
    }
}


function mouseDragged(){
	//dragging the bpm toggle
	if (mouseX > bpmToggleLength + margin
    	& mouseX < bpmToggleLength + width/5 + margin
    	&& mouseY > baseLineY + (margin * 2.5)
    	&& mouseY < baseLineY + (margin * 2.5) + noteW){
		bpmX = mouseX;
    }
}


//make the note real
function render(){
	fill(250);
	stroke(0, 0, 250);
	strokeWeight(5);
	//rendering cirlces if the note is on
	if (this.on){
		fill(250, 130, 180);
		noStroke();
		ellipse(this.x + noteW/2, this.y + noteW/2, this.w, this.w);
	} else { 
		//rendering squares if the notes is off
		rect(this.x, this.y, noteW, noteW); 
	}

	if(this.snd.isPlaying() & playX < this.x + noteW*1.5 && playX > this.x){
		var t = frameCount;
		this.w = noteW * sin(t*5);
	}else{
		this.w = noteW;
	}
}

//my note object maker function
function Note(x, y, snd, wide){
	var objNote = { //object literal format
		x : x,
		y : y,
		w: wide,
		snd : snd,
		on : false,
		playing: false
	}
	objNote.render = render;
	return objNote;
}


Jenny Hu — Looking Outwards 12

In looking for inspiration for my final project, which will be some form of an audio sequencer (tagged with visual animations)— I’ll write here about three projects I’ve found that range from simple to more difficult and experimental.

  1. The first project is Yamaha beats, by artist Louie Zong. Louie is a board artist for Cartoon Network’s We Bare Bears, but also maintains a really strong set of work online— including music production, illustration, and games. His project Yamaha Beats is a simple sequencer made from Unity— where you can make music using the sounds of koroks, from ghibli. I like the simplicity, and hope I can make something at the very least like this, that is well crafted and beautifully made.

    screenshot from Louie Zong’s sequencer game yamaha beats

  2.  The second project is Google’s Doodle for Oskar Fischinger’s 117th birthday. The project is pretty remarkable for all the nuances the program gives you— changing instruments, layering sequences, and crafting complex animation just to name a few. What I love is the little things the program adds in— like the fact I can click and drag, and the fade in and out of the actual sequence tool, which lets you appreciate the animations more.

    screenshot from the google doodle

  3. Finally, the third project, which is both audio + visual, but also physical. Golan Levin’s project Scrapple is an audio-visual sequencer that uses everyday objects as the markers for the musical composition. By placing things on a table, and having a projection, visitors can more physically reflect and be active in space— composing music in a different way.

    I think it’s also important to note the really contrasting audio and visual tones/aesthetics here. Professor Golan’s leans heavier into electronic beats, Google’s leans into traditional instruments and analog animations (like Oskars’), and Louie’s uses non-traditional instrument sounds to feel more like a forest. I’ll have to do a lot of thinking towards how I want mine to come out!

Jenny Hu — Project 12 : Proposal

For my final project, I want to make some form of a simple, but well-crafted audio sequencer— a tool where someone can compose simple music and have it played back to them. I want to utilize animations per moment of ‘collision’ for each part— in order to connect both the visual and auditory. So in some ways, this is like an audio-visualizer, but in this case, people will get to produce their own simple compositions!

sketch made on sketch app

Jenny Hu — Looking Outwards 11: Computer Music

For this week’s looking outwards post, I will discuss the artist Mileece, a  sound artist and environmental designer who is known for her methods of making music with plants.

Mileece creates generative music , one track which can be listened to above, and on this link. The way she generates this music is by essentially reading electrical currents given off by  plants via electrodes, and processing the current into binary code— which is then processed and animated into sound. Often, this creates sound that feels like music.

Vice created great documentation of her process at the following video:

What I love about Mileece’s music is the duality of the generative work. In some ways, the music is generated purely from the plant, it’s a radical change in our perception of what a plant is and is capable of, but it’s reprocessed by the computer. The computer is a key part of our ability to interpret the plant as music in the first place. Because of this, it feels impossible to define her music without the discussion of computation.

Will we start to breed plants to generate totally new sounds?

Jenny Hu — Project 11: Composition

jenny’s sketch

//Jenny Hu
//Section E
//jjh1@andrew.cmu.edu
//Project 11

var myTurtle;
var startFrame;
var newWeight;
var newColor;
var randWeight;

function setup() {
    createCanvas(400, 400);
    background(250);
    myTurtle = makeTurtle(width / 2, height / 2);
    myTurtle.setColor(color(20));
    myTurtle.penDown();

    resetCanvas();
    frameRate(10);
}

function draw() {
    var step = (frameCount - startFrame)/30.0;
    // timePassed();
    //change the grayness of the turtle
    newColor = random(0 , 70);
    myTurtle.setColor(color(newColor));

    //change the weight of the turtle
    newWeight = random(0,1);
    ink(myTurtle);

    //move my turtle
    myTurtle.forward(step);
    myTurtle.left(16.0);
  
    //stops, so you can admire the cute fingerprint composition
    if (myTurtle.y < 100) {
      myTurtle.penUp();
  }
}

//reset the turtle when you click
function mousePressed(){
     resetCanvas();
}

//reset the turtle
function resetCanvas() {
    background(250);
    startFrame = frameCount;
    myTurtle.penUp();
    myTurtle.goto(width / 2, height / 2);
    myTurtle.penDown();
}

//changes the weight of the turtle to resemble ink
function ink(myTurtle){
  randWeight = random(0 , 2);
  newWeight = newWeight + randWeight;
  myTurtle.setWeight(newWeight);
}

///API Below///


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 project, I wanted to do something akin to a ‘computer’s fingerprint’. I was interested to give it the feeling of ink, which makes each print feel unique. Unlike humans where each print is different, a computer is capable of making ‘perfect’ geometries— which makes the spiral feel fitting. Each generation of this spiral is unique though, due to the random generation of both width of each stroke and tone of gray.

Click the mouse anywhere to reset.

fingerprint image

 

 

Jenny Hu — Looking Outwards 10

For this week’s Looking Outwards writing, I want to discuss Lauren McCarthy‘s work and project LAUREN.

image from Lauren’s about page

Lauren McCarthy is an LA-based artist whose work centers on the tensions of automation, surveillance, and culture. She is also the creator of p5.js, the javascript library this whole class and all my work thus far rely on. What I love from her biography is the following quote: “I make art about what confuses me”.  Work that speaks to me most strongly is work that carefully explores, processes, and asks the unexpected questions.

*video of project LAUREN

Her work LAUREN describes how Lauren watched and remotely interacted with people in their homes through a variety of cameras, sensors, etc and aimed to be an even better personal AI assistant than the commercial assistants available today.  Her proposition is simple: A human can understand each person better, and anticipate their needs.

What I precisely love about this work is how it revises the ongoing distrust (and trust) of AI assistant products, and at large, the idea of sentience in artificial objects and materials. If we allow this sentience artificially, isn’t it much better to allow it naturally (an actual human being)? Our current paradigm of privacy is clearly placed in discourse in the LAUREN project.

Jenny Hu — Week 10: Generative Landscape

jenny’s sketch

//Jenny Hu
//Section E
//jjh1@andrew.cmu.edu
//Project 10

var moonx;
var moony;
var moonr;

var sunx;
var suny;
var sunr;

function setup() {
    createCanvas(480, 300);
    //frameRate(10);
    hillSpeedB = random(0.000002, 0.00003);
    hillSpeedFront = random(0.000005, 0.00009); 
    moonx = 350;
    moony = random(20,80);
    moonr = random(20,50);
    sunx = 420;
    suny = random(90,200);
    sunr = random(80,150);
    
}

function draw() {
    //background gradient
    var c1 = color(0);
    var c2 = color(200);

    for (var i = 0; i < height/2; i++){
        var gr = map (i, 0, height/2, 0, 1);
        var newC = lerpColor( c1, c2, gr);
        stroke(newC);
        line(0, i, width, i);
    }

    //make my moon and sun

    moonx = moonx - .01;
    if (moonx < -100) {
        moonx = 490;
        moonx = moonx - .01;
    };

    myMoon();

    sunx = sunx - .01;
    if (sunx < -100) {
        sunx = 490;
        sunx = sunx - .01;
    };
    
    mySun();

    //generate my hills
    drawHill(); 
}


function drawHill() {

    stroke(50);
    beginShape(); 
    for (var x = 0; x < width; x++) {
        var t = x * 0.002 + (millis() * hillSpeedB);
        var y = map(noise(t), 0,.9, 0, height/2);
        line(x, y, x, height);
    }
    endShape();

    stroke(200);
    beginShape(); 
    for (var x = 0; x < width; x++) {
        var t = x * 0.003 + (millis() * hillSpeedFront);
        var y = map(noise(t), 0, 0.7, 0, height);
        line(x, y, x, height);
    }
    endShape();
    
}

function myMoon(){
    noStroke();
    fill(200);
    ellipse(moonx, moony , moonr, moonr);
}
function mySun(){
    noStroke();
    fill(250);
    ellipse(sunx, suny , sunr, sunr);
}

For this project, I wanted to lean especially simple and elegant. Arguably, this might have been one of the most off-kilter process thus far in the class— I started with the intention to do simple, but subtle shapes. I wanted to impose 3D spheres onto the generative landscape, and apply a small gaussian blur to create a greater sense of focus.. but alas, I failed to find how to apply both the blur and the 3D implementation. I had explored and programmed using Perlin noise methods,  and considered multiple ways to apply blur (including generating from an array of images).

I think if I had more time, I would be able to find a method to implement these techniques on top of one another (the noise, with the landscape, with 3D on top), but in this case, I had to scale back.

In doing so, I think I learned a valuable lesson that I should have started small, and worked towards my bigger goal. Scaling back was mildly disappointing.

sketch of the landscape

Jenny Hu — Looking Outwards 09

For this week’s Looking Outwards, I will discuss Anthony Ra’s week 4 post.

I loved the piece that Anthony posted; Hush‘s interactive sound-art installation invites onlookers to rotate prism pieces of a board that refracts and changes color— and therefore sound.

My favorite statement from Anthony’s post is that the project ” allows for a calm duality in light projection and soundscape.” The elegance for this duality is exactly what makes the project so astoundingly sublime— and generates such a calm, multi-sensorial experience.

close up of board pieces

The pieces are elementary, and simple in a way that makes the piece inviting to touch and understand. What generates as a result, is something that feels seemingly complex, but aesthetically correct (and therefore simple). This duality is something that speaks to me as well, as someone who aims to create experiences that draw from inherently complex processes, this immediate and simple harmony is something I hope to achieve in my work in the future.

Jenny Hu — Project 09 Portrait

jenny’s sketch

//Jenny Hu
//Section E
//jjh1@andrew.cmu.edu
//Project 09

var underlyingImage;
var coffeeImage;

function preload() {
    var myImageURL = "https://i.imgur.com/raTslIA.jpg";
    var myCoffeeURL = "https://i.imgur.com/6kkGXgx.png";
    coffeeImage = loadImage(myCoffeeURL);
    underlyingImage = loadImage(myImageURL);
}

function setup() {
    createCanvas(400, 600);
    background(255);
    underlyingImage.loadPixels();
    frameRate(10);
}

function draw() {
    //variables needed for drawing the pixels over and over again
    var xa = random(width);
    var ya = random(height);
    var xb = random(width);
    var yb = random(height);
    var xc = random(width);
    var yc = random(height);

    var ixa = constrain(floor(xa), 0, width-1);
    var iya = constrain(floor(ya), 0, height-1);
    var ixb = constrain(floor(xb), 0, width-1);
    var iyb = constrain(floor(yb), 0, height-1);
    var ixc = constrain(floor(xc), 0, width-1);
    var iyc = constrain(floor(yc), 0, height-1);

    var theColorAtLocationXaYa = underlyingImage.get(ixa, iya);
    var theColorAtLocationXbYb = underlyingImage.get(ixb, iyb);
    var theColorAtLocationXcYc = underlyingImage.get(ixc, iyc);

    //open ellipses
    noFill();
    stroke(theColorAtLocationXaYa);
    strokeWeight(1);
    ellipse(xa, ya, 15, 15);

    //big rectangle
    fill (theColorAtLocationXbYb);
    noStroke();
    rect(xb, yb, 10, 10);

    //small rectangle
    fill (theColorAtLocationXcYc);
    noStroke();
    rect(xc, yc, 5, 5);

}

//call the coffee image when you click
function mousePressed() {
  image(coffeeImage, mouseX-75, mouseY-75, 150, 150);
}

I loved the textural quality individual shapes gave to this project— like in the brief, it was nice to see this transforming effect happen as I generated the pixels across the canvas.

My image is of my friend, Adella, who is pictured here opening a bottle of La Colombe. Rightfully so, I thought it would be interesting and fun to add a La Colombe image as the user clicks around. Doing so, makes the image feel even more artificial/generated, and even ad-like. In some ways, it contextualizes the image.

adella in the original photo
resulting image screenshot
beginning screenshot, with a few mouse presses

Jenny Hu — Looking Outwards 08

AnnMarie Thomas is a mechanical engineer and advocate for early engineering education. She is most well known as the founder and director of the Playful Learning Lab at University of St. Thomas, where she is centrally located, which encourages children to playfully learn.

The best way I can describe her work is fundamental, imaginative, and playful. AnnMarie strips away the complexity of learning engineering and simplifies it to tangible pieces, akin to toys. In the way I think legos are the smallest, most basic building blocks of analog toys, I feel that her work is similar to the most basic building blocks of interaction design across the physical and digital tools.

Her project, Squishy Circuits is a beautiful example of basic software and hardware interaction that feels like playdough. Its form is in a book, likely to make the course and tools accessible for parents and teachers to use. (something I also admire).

AnnMarie’s Website

A lecture of hers where she focuses on the importance of play and what that means.