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 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;
}
]]>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.
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!
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!
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
//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.
]]>
For this week’s Looking Outwards writing, I want to discuss Lauren McCarthy‘s work and project LAUREN.
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
//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.
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.
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
//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.
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).
A lecture of hers where she focuses on the importance of play and what that means.
]]>