5Square — Final Project, PComp + ICM

5Square

Make your own Music

Collaborators:

Amena Hayat

Max Horwich

Arnav Wagh

Please click the link to go to my previous blog post for the final project proposal:

GESTURAL GRAPHIC INTERFACE

 

 This project consists of
1. code in Tone.js library
2. animations for visuals
3. a glove that adds effects to music
———————————————————————————————–

Tone.js is a framework for creating interactive music in the browser. It provides advanced scheduling capabilities, synths and effects, and intuitive musical abstractions built on top of the Web Audio API.

We used Tone.js to create a musical interface that is easy, fun, interactive and engaging. The intended audience is a non-musician who wants to create music.

CODE

kickOn = false;
snareOn = false;
pianoOn = false;
bassOn = false;

//var upstate = false;
cChordPlaying = false;
fChordPlaying = false;
gChordPlaying = false;
amChordPlaying = false;

cBassPlaying = false;
fBassPlaying = false;
gBassPlaying = false;
amBassPlaying = false;

var serial;
var flex1 = 0,
flex2 = 0,
flex3 = 0;
flex4 = 0;

var pianoAnimation = false;

//KICK SOUND DEFINED
var kick = new Tone.MembraneSynth({
“envelope”: {
“sustain”: 0,
“attack”: 0.02,
“decay”: 0.8
},
“octaves”: 10
}).toMaster();

//KICK LOOP SET
var kickPart = new Tone.Loop(function(time) {
kick.triggerAttack(“C2”);
}, “2n”);

//SNARE FILTER
var snareFilter = new Tone.AutoFilter({
frequency: 1,
type: “sine”,
depth: 1,
baseFrequency: 400,
octaves: 2.6,
filter: {
type: “bandpass”,
rolloff: -12,
Q: 1
}
}).toMaster();

//SNARE SOUND DEFINED
var snare = new Tone.MetalSynth({
volume: -10,
frequency: 60,
envelope: {
attack: 0.001,
decay: 0.4,
release: 0.2
},
harmonicity: 5.1,
modulationIndex: 1,
resonance: 800,
octaves: 1.5
}).connect(snareFilter);

//SNARE LOOP SET
var snarePart = new Tone.Sequence(function(time, freq) {
snare.frequency.setValueAtTime(freq, time, Math.random() * 0.5 + 0.5);
snare.triggerAttack(time);
}, [null, 350, null, 350], “2n”);

//PIANO DELAY
var pianoDelay = new Tone.PingPongDelay({
“delayTime”: “4t”,
“maxDelayTime”: 2,
“wet”: .3,
“feedback”: .1
}).toMaster();

//PIANO TONE DEFINED
var piano = new Tone.MonoSynth(4, Tone.Synth, {
“volume”: -7,
“oscillator”: {
“partials”: [1, 2, 1],
},
“envelope”: {
“attack”: 0.001,
“decay”: 0.1,
“sustain”: 0.3,
“release”: 1
},
“portamento”: 0.001
}).connect(pianoDelay);

//PIANO CHORDS DEFINED
/*
var cChord = [“C4”, [“E4”, “G4”]];
var gChord = [[“B3”, “D4”], “G4”];
var amChord = [[“C4”, “E4”], “A4”];
var fChord = [[“C4”], “F4”, [“A4”]];
*/
var cChord = [“C4”, “E4”, “G4”];
var gChord = [“B3”, “D4”, “G4”];
var amChord = [“C4”, “E4”, “A4”];
var fChord = [“C4”, “F4”, “A4”];

var ellipseCoord = {};

//PIANO LOOP SET

var pianoPart = new Tone.Sequence(function(time, note) {
piano.triggerAttackRelease(note, “16n”, time);
//SEQUENCE OF CHORDS
}, [cChord]);
//pianoPart.probability = 0.5;

//Bass FFT & waveform
var fft = new Tone.FFT(32);
var spectrum = new Tone.Waveform(1024);

var bassDist = new Tone.Distortion({
“distortion”: 0.4,
“oversample”: ‘2x’
}).connect(spectrum).toMaster();

//BASS TONE DEFINED
var bass = new Tone.MonoSynth({
“volume”: -10,
“envelope”: {
“attack”: 0.1,
“decay”: 0.3,
“release”: 2,
},
“filterEnvelope”: {
“attack”: 0.001,
“decay”: 0.01,
“sustain”: 0.5,
“baseFrequency”: 200,
“octaves”: 2.6
}
}).connect(bassDist);

//BASS LOOP SET
var bassPart = new Tone.Sequence(function(time, note) {
bass.triggerAttackRelease(note, “16n”, time);
//SEQUENCE OF BASS NOTES
}, [“C2”]);
//bassPart.probability = 0.5;

//LEAD DELAY
var leadDelay = new Tone.PingPongDelay({
“delayTime”: “8n”,
“maxDelayTime”: 1,
“feedback”: 0.82,
“wet”: .40

}).toMaster();

//LEAD TONE DEFINED
var leadPaint = new Tone.PolySynth({
“volume”: -10,
“oscillator”: {
“type”: “square”
},
“envelope”: {
“attack”: 0.2
},
“portamento”: 0.05

}).connect(leadDelay);

//FX SENDS
var delayKickSend = kick.send(“delayKick”, -Infinity);
var delaySnareSend = snare.send(“delaySnare”, -Infinity);
var crushSend = piano.send(“crush”, -Infinity);
var chebySend = bass.send(“cheby”, -Infinity);

var delayKick = new Tone.FeedbackDelay(“4t”, 0.38)
.receive(“delayKick”)
.toMaster();
var delaySnare = new Tone.FeedbackDelay(“8t”, 0.25)
.receive(“delaySnare”)
.toMaster();
var crushPiano = new Tone.BitCrusher(4)
.receive(“crush”)
.toMaster();
var chebyBass = new Tone.Chebyshev(10)
.receive(“cheby”)
.toMaster();

//SLOWEST POSSIBLE TEMPO
//ALL OTHERS ARE SET AS MULTIPLE OF THIS
//
Tone.Transport.bpm.value = 60;

//HIT IT!!!
Tone.Transport.start();

//—————————————————————-
//BEGINNING OF SETUP

function setup() {
createCanvas(windowWidth, windowWidth/2);
background(230);

serial = new p5.SerialPort();
serial.on(‘list’, printList);
serial.on(‘data’, serialEvent);
serial.list();
serial.open(“/dev/cu.usbmodem1431”);
//ALWAYS CHECK IF USB PORT IS CORRECT FOR YOUR PERSONAL LAPTOP

sliderKick = createSlider(1, 6, 2);
sliderKick.position(5, height + 20);
sliderKick.size(width / 4 – 10, 10);
sliderBass = createSlider(1, 4, 1);
sliderBass.position(width / 4 + 5, height + 40);
sliderBass.size(width / 4 – 10, 10);
sliderPiano = createSlider(1, 4, 1);
sliderPiano.position(width / 4 + 5, height + 20);
sliderPiano.size(width / 4 – 10, 10);
sliderSnare = createSlider(1, 6, 2);
sliderSnare.position(5, height + 40);
sliderSnare.size(width / 4 – 10, 10);
//leadDelayPaint = createSlider(1,6,1);

sliderFX1 = createSlider(-100,0,-100);
sliderFX1.position(5, height+60);
sliderFX1.size(width/4-10, 10);
sliderFX2 = createSlider(-100,0,-100);
sliderFX2.position(5, height+80);
sliderFX2.size(width/4-10, 10);
sliderFX3 = createSlider(-100,0,-100);
sliderFX3.position(5, height+100);
sliderFX3.size(width/4-10, 10);
sliderFX4 = createSlider(-100,0,-100);
sliderFX4.position(5, height+120);
sliderFX4.size(width/4-10, 10);

ellipseCoord.c = [width/16, (height/2)*0.2, width/8, (height/2)*0.2, width/5.33, (height/2)*0.2];
ellipseCoord.g = [width/16, (height/2)*0.4, width/8, (height/2)*0.4, width/5.33, (height/2)*0.4];
ellipseCoord.am = [width/16, (height/2)*0.6, width/8, (height/2)*0.6, width/5.33, (height/2)*0.6];
ellipseCoord.f = [width/16, (height/2)*0.8, width/8, (height/2)*0.8, width/5.33, (height/2)*0.8];

// ellipseCoord.c = [80, 80, 160, 80, 240, 80];
// ellipseCoord.g = [80, 80 + 55, 160, 80 + 55, 240, 80 + 55];
// ellipseCoord.am = [80, 80 + 110, 160, 80 + 110, 240, 80 + 110];
// ellipseCoord.f = [80, 80 + 165, 160, 80 + 165, 240, 80 + 165];

}

//END OF SETUP
//—————————————————————-
//BEGIN OF DRAW
function draw() {

var kickPulse = kickPart.progress;
var snarePulse = snarePart.progress;
var pianoPulse = pianoPart.progress;
var bassPulse = bassPart.progress;
var loopstate = pianoPart.state;

var pp = map(pianoPulse, 0, 1, 0.5, 1.1);
var pt = map(snarePulse, 0, 0.3, 1, 1.2);
var pg = map(snarePulse, 0, 1, 1, 0.5);
var pf = map(snarePulse, 0, 1, 1, 0.2);

kickSwellOuter = map(kickPulse, 0, 1, 0, (width / 4.57)/35);
kickSwellMiddle = map(kickPulse, 0, 1, 0, (width / 5.33)/9);
kickSwellInner = map(kickPulse, 0, 1, 0, (width / 6.66)/3);

alphaOuter = map(kickSwellOuter, 0, 5, 50, 20);
alphaMiddle = map(kickSwellMiddle, 0, 16.7, 90, 30);

var wave = spectrum.getValue();
var fftwave = fft.getValue();

// fill(0);
// stroke(0);
// strokeWeight(2)
// //rect(width / 4, 0, width / 4, height / 2);
// rect(0,width,0,height);

//—————————–ANimations———————————
//KICK CIRCLE
push();
translate(0, 0);
fill(225);
noStroke();
rect(0, 0, width / 4, height / 2);
noFill();
ellipseMode(CENTER);

stroke(135, 206, 250, alphaOuter);
strokeWeight(2);
ellipse(width / 8, height / 4, width / 4.57 + (kickSwellOuter * -1));

stroke(135, 206, 250, alphaMiddle);
strokeWeight(2);
ellipse(width / 8, height / 4, width / 5.33 + (kickSwellMiddle * -1));

stroke(135, 206, 250);
strokeWeight(2);
ellipse(width / 8, height / 4, width / 6.66 + (kickSwellInner * -1));
//print(width/4-80);

pop();

//BASS SQUARE
beginShape();
push();
translate(width * 3 / 8, height * 3 / 4);
noStroke();
fill(225);
rect(0 – (width / 8), 0 – (height / 4), width / 4, height / 2)
//rect(0, 0, width / 4, height / 2)
beginShape();
noFill();
stroke(255, 100, 0); // waveform is red
strokeWeight(2);
rectMode(CENTER)
for (var i = 0; i < wave.length; i += 600) {
//var a = map(i, 0, wave.length, -3, 3);
var b = map(wave[i], -1, 1, -40, 40);
rect(0, 0, width / 6.15 + b, height / 3.07 + b);
}
pop();
endShape();

//PIANO TRIANGLE
push();
translate(width * 1 / 8, height/1.25);
print(height*1.25)
noStroke();
fill(225);
rect(0 – (width / 8), 0 – (height*0.25+height*0.05), width / 4, height / 2)
scale(pg);
stroke(255);
point(0, 0)
noFill();
strokeWeight(1);
triangle(width/-11.42, height/9.83, 0, height/-4.917, width/11.42, height/9.83);
//triangle(-70, 40.67, 0, -81.35, 70, 40.67); //for 800/400
pop();

//SLIDERS FOR TEMPO OF DIFFERENT INSTRUMENTS
//PLAYBACK RATE MULTIPLIES TEMPO FOR THAT PART
kickPart.playbackRate = sliderKick.value();
snarePart.playbackRate = sliderSnare.value();

// sliderKick.value(map(flex1, 0, 250, 1, 6));
// sliderSnare.value(map(flex1, 0, 250, 1, 6));
// sliderPiano.value(map(flex2, 0, 250, 1, 4));
// sliderBass.value(map(flex2, 0, 250, 1, 4));
// a = map(flex3, 0, 250, 0.1, 0.9);

push();
translate(width*0.25,0);
fill(225);
noStroke(0);
rect(0,0,width*0.25,height/2);
pop();

if (pianoAnimation == true) {
animateEllipseC(pianoPulse);
}
if (pianoAnimation == true) {
animateEllipseG(pianoPulse);
}
if (pianoAnimation == true) {
animateEllipseAM(pianoPulse);
}
if (pianoAnimation == true) {
animateEllipseF(pianoPulse);
}

//PIANO CHORD SLIDER
if (sliderPiano.value() == 1) {
if (cChordPlaying == false) {
//console.log(“inside value 1”);
pianoPart.removeAll();
pianoPart.add(0, cChord);
cChordPlaying = true;
fChordPlaying = false;
gChordPlaying = false;
amChordPlaying = false;
}
} else if (sliderPiano.value() == 2) {
if (fChordPlaying == false) {
pianoPart.removeAll();
pianoPart.add(0, fChord);
cChordPlaying = false;
fChordPlaying = true;
gChordPlaying = false;
amChordPlaying = false;
}
} else if (sliderPiano.value() == 3) {
if (gChordPlaying == false) {
pianoPart.removeAll();
pianoPart.add(0, gChord);
cChordPlaying = false;
fChordPlaying = false;
gChordPlaying = true;
amChordPlaying = false;
}
} else if (sliderPiano.value() == 4) {
if (amChordPlaying == false) {
pianoPart.removeAll();
pianoPart.add(0, amChord);
cChordPlaying = false;
fChordPlaying = false;
gChordPlaying = false;
amChordPlaying = true;
}
}

//BASS NOTE SLIDER
if (sliderBass.value() == 1) {
if (cBassPlaying == false) {
bassPart.remove(0);
bassPart.add(0, “C2”);
cBassPlaying = true;
fBassPlaying = false;
gBassPlaying = false;
amBassPlaying = false;
}
} else if (sliderBass.value() == 2) {
if (fBassPlaying == false) {
bassPart.remove(0);
bassPart.add(0, “F2”);
cBassBassPlaying = false;
fBassBassPlaying = true;
gBassBassPlaying = false;
amBassBassPlaying = false;
}
} else if (sliderBass.value() == 3) {
if (gBassPlaying == false) {
bassPart.remove(0);
bassPart.add(0, “G1”);
cBassPlaying = false;
fBassPlaying = false;
gBassPlaying = true;
amBassPlaying = false;
}
} else if (sliderBass.value() == 4) {
if (amBassPlaying == false) {
bassPart.remove(0);
bassPart.add(0, “A1”);
cBassPlaying = false;
fBassPlaying = false;
gBassPlaying = false;
amBassPlaying = true;
}
}

//FLEXER FX
delayKickSend.gain.value = sliderFX1.value();
delaySnareSend.gain.value = sliderFX2.value();
crushSend.gain.value = sliderFX3.value();
chebySend.gain.value = sliderFX4.value();

sliderFX1.value(map(flex1,310,400,-100,0));
sliderFX2.value(map(flex2,270,390,-100,0));
sliderFX3.value(map(flex3,230,340,-100,0));
sliderFX4.value(map(flex4,300,370,-100,0));

//RIGHT SIDE DRAWING
if (mouseX > width / 2 && mouseX < width &&
mouseY > 0 && mouseY < width) {
if (mouseIsPressed) {
for (i = 0; i < 15; i++) {
noStroke();
fill(255, 0, 0);
//fill(255, 100 + i * 3, 100 + i * 5, 255 / i);
ellipse(mouseX, mouseY, i, i);
}
}

//SLOW FADE
fill(230,15);
noStroke();
rect(width / 2, 0, width / 2, height);

//ERASE DRAWING AND KILL LEAD
if (keyIsPressed) {
fill(230);
rect(width / 2, 0, width / 2, height);
leadPaint.triggerRelease();
}

}
}

//END OF DRAW MODE
//—————————————-
//BEGINNING OF FUNCTIONS

function animateEllipseC(pianoPulse) {
//console.log(“inside chordAnimation”);
if (sliderPiano.value() == 1) {
var coord = ellipseCoord.c;
//console.log(coord);
var index = 0;
if (pianoPulse > 0 && pianoPulse < 0.3) {
index = 0;
} else if (pianoPulse > 0.3 && pianoPulse < 0.6) {
index = 2;
} else if (pianoPulse > 0.6) {
index = 4;
}
push();
translate(width / 4, 0)
point(0, 0)
fill(200, 50)
fill(124, 225, 0, 200);
noStroke();
var pp = map(pianoPulse, 0, 1, 0.5, 1.1);
ellipse(coord[index], coord[index + 1], pp*60, pp*60);
pop();
}
}

function animateEllipseF(pianoPulse) {
//console.log(“inside chordAnimation”);
if (sliderPiano.value() == 2) {
var coord = ellipseCoord.f;
//console.log(coord);
var index = 0;
if (pianoPulse > 0 && pianoPulse < 0.3) {
index = 0;
} else if (pianoPulse > 0.3 && pianoPulse < 0.6) {
index = 2;
} else if (pianoPulse > 0.6) {
index = 4;
}
push();
translate(width / 4, 0)
point(0, 0)
fill(200, 50)
fill(140, 180, 121, 200);
noStroke();
var pp = map(pianoPulse, 0, 1, 0.5, 1.1);
ellipse(coord[index], coord[index + 1], pp*80, pp*80);
pop();
}
}

function animateEllipseG(pianoPulse) {
//console.log(“inside chordAnimation”);
if (sliderPiano.value() == 3) {
var coord = ellipseCoord.g;
//console.log(coord);
var index = 0;
if (pianoPulse > 0 && pianoPulse < 0.3) {
index = 0;
} else if (pianoPulse > 0.3 && pianoPulse < 0.6) {
index = 2;
} else if (pianoPulse > 0.6) {
index = 4;
}
push();
translate(width / 4, 0)
point(0, 0)
fill(200, 50)
fill(100, 120, 190, 200);
noStroke();
var pp = map(pianoPulse, 0, 1, 0.5, 1.1);
ellipse(coord[index], coord[index + 1], pp*40, pp*40);
pop();
}
}

function animateEllipseAM(pianoPulse) {
//console.log(“inside chordAnimation”);
if (sliderPiano.value() == 4) {
var coord = ellipseCoord.am;
//console.log(coord);
var index = 0;
if (pianoPulse > 0 && pianoPulse < 0.3) {
index = 0;
} else if (pianoPulse > 0.3 && pianoPulse < 0.6) {
index = 2;
} else if (pianoPulse > 0.6) {
index = 4;
}
push();
translate(width / 4, 0)
point(0, 0)
fill(200, 50)
fill(90, 100, 60, 200);
noStroke();
var pp = map(pianoPulse, 0, 1, 0.5, 1.1);
ellipse(coord[index], coord[index + 1], pp*50, pp*50);
pop();
}
}

//DRAG TO PLAY FUNCTION
function touchMoved() {
var paintNote = [“C4”, “E4”, “G4”, “A4”, “C5”, “E5”, “G5”, “A5”, “C6”]

//right side of canvas
if (mouseX > width / 2 && mouseX < width &&
mouseY > 0 && mouseY < height) {

//————NOTE GRID!!!
//column1
if (mouseX > width / 2 && mouseX < width * 5 / 8 &&
mouseY < height && mouseY > height * 3 / 4) {
leadPaint.triggerAttackRelease(paintNote[0], “8n”);
} else if (mouseX > width / 2 && mouseX < width * 5 / 8 &&
mouseY < height * 3 / 4 && mouseY > height / 2) {
leadPaint.triggerAttackRelease(paintNote[1], “8n”);
} else if (mouseX > width / 2 && mouseX < width * 5 / 8 &&
mouseY < height / 2 && mouseY > height / 4) {
leadPaint.triggerAttackRelease(paintNote[2], “8n”);
} else if (mouseX > width / 2 && mouseX < width * 5 / 8 &&
mouseY < height / 2 && mouseY > 0) {
leadPaint.triggerAttackRelease(paintNote[3], “8n”);
//column2
} else if (mouseX > width * 5 / 8 && mouseX < width * 3 / 4 &&
mouseY < height && mouseY > height * 3 / 4) {
leadPaint.triggerAttackRelease(paintNote[1], “8n”);
} else if (mouseX > width * 5 / 8 && mouseX < width * 3 / 4 &&
mouseY < height * 3 / 4 && mouseY > height / 2) {
leadPaint.triggerAttackRelease(paintNote[2], “8n”);
} else if (mouseX > width * 5 / 8 && mouseX < width * 3 / 4 &&
mouseY < height / 2 && mouseY > height / 4) {
leadPaint.triggerAttackRelease(paintNote[3], “8n”);
} else if (mouseX > width * 5 / 8 && mouseX < width * 3 / 4 &&
mouseY < height / 4 && mouseY > 0) {
leadPaint.triggerAttackRelease(paintNote[4], “8n”);
//column3
} else if (mouseX > width * 3 / 4 && mouseX < width * 7 / 8 &&
mouseY < height && mouseY > height * 3 / 4) {
leadPaint.triggerAttackRelease(paintNote[2], “8n”);
} else if (mouseX > width * 3 / 4 && mouseX < width * 7 / 8 &&
mouseY < height * 3 / 4 && mouseY > height / 2) {
leadPaint.triggerAttackRelease(paintNote[3], “8n”);
} else if (mouseX > width * 3 / 4 && mouseX < width * 7 / 8 &&
mouseY < height / 2 && mouseY > height / 4) {
leadPaint.triggerAttackRelease(paintNote[4], “8n”);
} else if (mouseX > width * 3 / 4 && mouseX < width * 7 / 8 &&
mouseY < height / 4 && mouseY > 0) {
leadPaint.triggerAttackRelease(paintNote[5], “8n”);
//column4
} else if (mouseX > width * 7 / 8 && mouseX < width &&
mouseY < height && mouseY > height * 3 / 4) {
leadPaint.triggerAttackRelease(paintNote[3], “8n”);
} else if (mouseX > width * 7 / 8 && mouseX < width &&
mouseY < height * 3 / 4 && mouseY > height / 2) {
leadPaint.triggerAttackRelease(paintNote[4], “8n”);
} else if (mouseX > width * 7 / 8 && mouseX < width &&
mouseY < height / 2 && mouseY > height / 4) {
leadPaint.triggerAttackRelease(paintNote[5], “8n”);
} else if (mouseX > width * 7 / 8 && mouseX < width &&
mouseY < height / 4 && mouseY > 0) {
leadPaint.triggerAttackRelease(paintNote[6], “8n”);
}
}

// print(flex1);
}

//CLICK 2 PLAY FUNCTION
function mousePressed() {
if (mouseX > 0 && mouseX < width / 4 &&
mouseY > 0 && mouseY < height / 2) {
loopKick();
} else if (mouseX > width / 4 && mouseX < width / 2 &&
mouseY > 0 && mouseY < height / 2) {
loopPiano();
pianoAnimation = true;
} else if (mouseX > 0 && mouseX < width / 4 &&
mouseY > height / 2 && mouseY < height) {
loopSnare();
} else if (mouseX > width / 4 && mouseX < width / 2 &&
mouseY > height / 2 && mouseY < height) {
loopBass();
}
}

//LOOP FUNCTIONS
function loopKick() {
if (!kickOn) {
kickPart.start(0);
kickOn = !kickOn;
} else {
kickPart.stop();
kickOn = !kickOn;
}
}

function loopSnare() {
if (!snareOn) {
snarePart.start(0);
snarePart.loop = true;
snareOn = !snareOn;
} else {
snarePart.loop = false;
snareOn = !snareOn;
}
}

function loopPiano() {
if (!pianoOn) {
pianoPart.start(0);
pianoPart.loop = true;
pianoOn = !pianoOn;
} else {
pianoPart.loop = false;
pianoOn = !pianoOn;
}
}

function loopBass() {
if (!bassOn) {
bassPart.start(0);
bassPart.loop = true;
bassOn = !bassOn;
} else {
bassPart.loop = false;
bassOn = !bassOn;
}
}

function printList(portList) {
for (var i = 0; i < portList.length; i++) {
print(i + ” ” + portList[i]);
}
}

function serialEvent() {
var stringFromSerial = serial.readLine();
if (stringFromSerial.length > 0) {
var trimmedString = trim(stringFromSerial);
var myArray = split(trimmedString, “,”)
flex1 = Number(myArray[0]);
flex2 = Number(myArray[1]);
flex3 = Number(myArray[2]);
flex4 = Number(myArray[3]);
}
}

document.ontouchmove = function(event){
event.preventDefault();

}

ANIMATIONS

BEAT

BASS

PIANO

SNARE

LEAD MELODY

 

THE SKETCH

Click around and draw on the fifth square!

GLOVE

To make the glove, we used Adafruit Flora. FLORA is Adafruit’s fully-featured wearable electronics platform. It’s a round, sewable, Arduino-compatible microcontroller designed to empower  wearables.

Diagram 1: Sensor position on the glove

Arduino Code:

Diagram 2: Circuit for the glove

Initial testing

Serial communication from breadboard circuit:

Soldering:

Using potentiometers for controls for user testing

—————————————————————————

5square

performed by Max Horwich

enjoy

 

Gestural Graphic Interface

Partnering with two other people, my final project for ICM and PComp is the same: A graphic and musical interface that uses gloves  to enable inexperienced people to create, enhance and play music. After brainstorming for about a week and letting our simple ideas multiply and evolve into something that stays faithful to our project goals, and researching the scope and methods of other similar projects, we decided to take on a multi-faceted, holistic music creating interface that lets the user draw basic music notes in the form of doodles on a screen, and enhance it with the glove. The program will take inputs from the coordinates of the drawings to play and loop it, and from the sensors in the glove to add drastic effects, both musical and visual, to show the fun and the power in the art form itself.

Our inspiration through this research has been the Mi.Mu glove made by Imogen Heap, a glove that records, loops, plays and adds effects to sounds and music, while the user performs.

Inspirations..

Hand gestures — to make the music making process as intuitive as possible

To make this glove possible, we plan to build one using flex sensors, accelerometers, and probably even some tactile sensors, however that will greatly rely on the degrees of control we need.

Circuit diagram of one of the simplest glove designs

Our aim with the glove involves giving super powers to user when it comes to affecting change to the music. the first iteration might look similar to this one.

For the interface on the screen, our designs are still coming together, but the current target is around halfway between a traditional step sequencer and Kandinsky from the Chrome Music Lab Experiments. We researched a new library of functions for our project, Tone.js, and learned about its abilities and pitfalls.

Check it out:

https://musiclab.chromeexperiments.com/Kandinsky

The Progress:

We are currently researching the timeline, looping and how we are add and delete new user-added sounds to an existing loop. It is challenging but our progress is motivational.

Using Tone.js, we have so far created a sketch that uses this library to play beats at different frequencies and note lengths, and a sketch that creates markers to let the user draw, and maps sounds to each coordinate (and adds it to an array).

[Please refresh in case the sketches below do not work]

Or click the link:

TONE.JS

 

Or click the link:

MARKERS AND TONES

We are still understanding the workings of Tone.js and the next step is to serially communicate with the glove and add more changeable parameters to our sounds in code. We’re still in the process of acquiring materials for construction of the glove. This project has turned into an enormous undertaking, but it’s moving steadily forward, and I can’t wait to see how it develops.

 

Trash

Team Members

Amena Hayat

Yipeng Chen

Sam Chasan

For animation class, we were tasked with making a 30 second stop-motion animation with a character. Using Dragon Stop Motion, Adobe After Effects and Adobe Premier, we shot and compiled a short animation called “Trash”.

In it, we brought to life a piece of trash who comes to life when someone unsuccessfully throws it in to the trash basket, and takes upon itself the mission of going back home. After many attempts of jumping back in, getting super frustrated in the process of going “home”, dragging objects to use as high platforms, it gets lucky when someone else walks by and makes the task easy for it.

Here’s the final edit:

 

TRASH ANIMATION from Amena Hayat on Vimeo.

Faces on a slider

For my PComp midterm, I used a few frames of some hand made sketches that used a proximity sensor to change between them.

For the sake of learning and applying the HTML slider, I uploaded the sketches on to p5.js and used simple if conditions to traverse through the images using the slider and get the animation effect.

Here is my code:

function setup() {
createCanvas(420, 450);
one = loadImage(“faces/1.png”);
two = loadImage(“faces/2.png”);
three = loadImage(“faces/3.png”);
four = loadImage(“faces/4.png”);
five = loadImage(“faces/5.png”);
six = loadImage(“faces/6.png”);
seven = loadImage(“faces/7.png”);
eight = loadImage(“faces/8.png”);
nine = loadImage(“faces/9.png”);
ten = loadImage(“faces/10.png”);
slider = createSlider(0,10,0,0);
slider.position(10,height-40);
slider.size(width-20,20);
x = slider.value();
}

function draw() {
background(255);
console.log(slider.value());
if(slider.value() <= 1){
image(one,0,0,width,height);
}
else if(slider.value() > 1 && slider.value() <= 2){
image(two,0,0,width,height);
}
else if(slider.value() > 2 && slider.value() <= 3){
image(three,0,0,width,height);
}
else if(slider.value() > 3 && slider.value() <= 4){
image(four,0,0,width,height);
}
else if(slider.value() > 4 && slider.value() <= 5){
image(five,0,0,width,height);
}
else if(slider.value() > 5 && slider.value() <= 6){
image(six,0,0,width,height);
}
else if(slider.value() > 6 && slider.value() <= 7){
image(seven,0,0,width,height);
}
else if(slider.value() > 7 && slider.value() <= 8){
image(eight,0,0,width,height);
}
else if(slider.value() > 8 && slider.value() <= 9){
image(nine,0,0,width,height);
}
else {
image(ten,0,0,width,height);
}
}

Maternity and Race

This week for ICM I wanted to illustrate statistics for the problems women face, and I wanted to do it in a way that helps viewers understand the issues women of colour or lower education attainment face, and also make it easier to understand and visualise — using emojis of course.

I imported the data in a .csv file from https://mchb.hrsa.gov, HRSA for Maternal and Child Health, and read up on of their surveys done in 2015 for maternity leaves taken in the US, divided by race, educational attainment and reasons for not getting or taking the leave.

Maternity leave from employment after childbirth provides critical time for maternal-infant bonding and adjustment to life with a new baby. Longer length of maternity leave is associated with increased breastfeeding duration, as well as improved maternal mental health and child development. The WOMEN’S HEALTH USA 2011 stated that “the Family and Medical Leave Act (FMLA) guarantees both women and men up to 12 weeks of unpaid leave around the birth or adoption of a child as long as they work for larger employers (50+ employees) and meet certain tenure and working hour requirements.” However, many women cannot afford to take unpaid leave and usually use a combination of short-term disability, sick leave, vacation, and personal days in order to have some portion of their maternity leave paid. The U.S. is one of only 5 countries in the world that does not mandate paid maternity leave.

According to their study:

In 2006-2015, 66.0 percent of women reported being employed during their last pregnancy, of whom 69.7 percent reported taking maternity leave. Thus, nearly one-third of employed women did not report taking any maternity leave (30.3 percent). Women with at least a college degree were more likely to have taken leave than those who had attended college but not graduated (80.0 versus 71.6 percent, respectively) while less than half of women without a high school degree reported having taken leave. Hispanic and non-Hispanic Black women were less likely to report having taken maternity leave than non-Hispanic White women (62.5 and 64.3 percent, respectively, versus 72.2 percent). When taken, the average length of maternity leave was 10.0 weeks (data not shown). Among employed women who did not take maternity leave for their last pregnancy, 5.1 percent did not take it because it was not offered or allowed by their employer. Of non-Hispanic White women, 3.2 percent reported this reason, compared to 8.2 percent of Hispanic women and 10.2 percent of non-Hispanic Black women.

I graphed these using skin colour as a major visual tool to tell the difference between the treatment of women at work.

Below are the visualisations and my code:

 

The key: (will be added to the sketch later)

Women who took maternity leave based on race

Employer’s disagreement on maternity leave based on race

Women of (presented) colour who enrolled in college

Educational Attainment levels:

Less than High School

High School

Some College

Bachelors Degree and higher

CODE:

let dataRace;
let dataEducation;
function preload() {
dataRace = loadTable(“race.csv”,
“csv”,
“header”);
dataEducation = loadTable(“education.csv”,
“csv”,
“header”);
}
function setup() {
createCanvas(430, 400);
console.log(dataRace.getRowCount());
console.log(dataRace.getColumnCount());
white = loadImage(“maternity/white.png”);
black = loadImage(“maternity/black.png”);
hispanic = loadImage(“maternity/hispanic.png”);
mixed = loadImage(“maternity/mixed.png”);
e1 = loadImage(“maternity/e1.png”);
e2 = loadImage(“maternity/e2.png”);
e3 = loadImage(“maternity/e3.png”);
e4 = loadImage(“maternity/e4.png”);
whiteemp = loadImage(“maternity/whiteemp.png”);
blackemp = loadImage(“maternity/blackemp.png”);
hispanicemp = loadImage(“maternity/hispanicemp.png”);
multipleemp = loadImage(“maternity/mixeddemp.png”);
whitegraduate = loadImage(“maternity/whitegraduate.png”);
blackgraduate = loadImage(“maternity/blackgraduate.png”);
multiplegraduate = loadImage(“maternity/mixedgraduate.png”);
hispanicgraduate = loadImage(“maternity/hispanicgraduate.png”);
//slider1 = createSlider(0,100,0,1);
//slider1.position(10,height-70);
//slider1.size(width-20,20);
//x = slider1.value();
console.log(dataRace.getNum(3, “Took Maternity Leave”));
//console.log(dataEducation.getNum(3, “Percent of Women”));

}

function draw() {
background(255);

let whiteNum =dataRace.getNum(1, “Took Maternity Leave”);
let blackNum =dataRace.getNum(2, “Took Maternity Leave”);
let multipleNum =dataRace.getNum(3, “Took Maternity Leave”);
let hispanicNum =dataRace.getNum(4, “Took Maternity Leave”);
let whiteEMP =dataRace.getNum(1, “Did Not Take Maternity Leave – Not Offered or Allowed by Employer”);
let blackEMP =dataRace.getNum(2, “Did Not Take Maternity Leave – Not Offered or Allowed by Employer”);
let multipleEMP=dataRace.getNum(3, “Did Not Take Maternity Leave – Not Offered or Allowed by Employer”);
let hispanicEMP =dataRace.getNum(4, “Did Not Take Maternity Leave – Not Offered or Allowed by Employer”);
let whiteCol =dataRace.getNum(1, “Enrolled in College”);
let blackCol =dataRace.getNum(2, “Enrolled in College”);
let multipleCol =dataRace.getNum(3, “Enrolled in College”);
let hispanicCol =dataRace.getNum(4, “Enrolled in College”);
let lessHigh =dataRace.getNum(1, “Percent of Women”);
let highSchool =dataRace.getNum(2, “Percent of Women”);
let someCol =dataRace.getNum(3, “Percent of Women”);
let colHigher =dataRace.getNum(4, “Percent of Women”);

let y = 25;
let x = 5;
//if(slider1.value() < hispanicNum){
strokeWeight(30);
stroke(145, 86, 34);
line(x+30,y+80,x+30,y+80+hispanicNum);
image(hispanic,x,y,50,85);
//}
//if(slider1.value() < blackNum){
strokeWeight(30);
stroke(51, 30, 12);
line(x+80,y+80,x+80,y+80+blackNum);
image(black,x+50,y,50,85);
//}
//if(slider1.value() < whiteNum){
strokeWeight(30);
stroke(249, 222, 199);
line(x+130,y+80,x+130,y+80+whiteNum);
image(white,x+100,y,50,85);
//}
//if (slider1.value() < multipleNum){
strokeWeight(30);
stroke(183, 143, 110);
line(x+180,y+80,x+180,y+80+multipleNum);
image(mixed,x+150,y,50,85);
//}
//noStroke();
//textSize(30);
//text(slider1.value(),90,200);
//textSize(20);
//text(“% of women”,46,220);
//let edu1 =dataEducation.getNum(
strokeWeight(30);
stroke(100);
line(x+250,y+60,x+250,y+80+lessHigh);
image(e1,x+230,y,45,58);
strokeWeight(30);
stroke(75);
line(x+300,y+60,x+300,y+80+highSchool);
image(e2,x+280,y,45,58);
strokeWeight(30);
stroke(50);
line(x+350,y+60,x+350,y+80+someCol);
image(e3,x+330,y,45,58);
strokeWeight(30);
stroke(0);
line(x+400,y+60,x+400,y+80+colHigher);
image(e4,x+380,y,47,60);

strokeWeight(30);
stroke(145, 86, 34);
line(x+30,y+250,x+30,y+250+hispanicEMP*8);
image(hispanicemp,x,y+200,50,72);
strokeWeight(30);
stroke(33, 20, 9);
line(x+80,y+250,x+80,y+250+blackEMP*8);
image(blackemp,x+50,y+200,50,72);
strokeWeight(30);
stroke(249, 222, 199);
line(x+130,y+250,x+130,y+250+whiteEMP*8);
image(whiteemp,x+100,y+200,50,72);
strokeWeight(30);
stroke(183, 143, 110);
line(x+180,y+250,x+180,y+250+multipleEMP*8);
image(multipleemp,x+150,y+200,50,72);

strokeWeight(30);
stroke(145, 86, 34);
line(x+250,y+260,x+250,y+250+hispanicCol);
image(hispanicgraduate,x+230,y+200,45,58);
strokeWeight(30);
stroke(33, 20, 9);
line(x+300,y+260,x+300,y+260+blackCol);
image(blackgraduate,x+280,y+200,45,58);
strokeWeight(30);
stroke(249, 222, 199);
line(x+350,y+260,x+350,y+260+whiteCol);
image(whitegraduate,x+330,y+200,45,58);
strokeWeight(30);
stroke(183, 143, 110);
line(x+400,y+260,x+400,y+260+multipleCol);
image(multiplegraduate,x+380,y+200,45,58);

}

https://alpha.editor.p5js.org/amena91/sketches/BJpwh9TAb

FACES

I started this project for PComp due to my strong interest in manipulating faces. My aim with the project was to change facial features, expressions, or change the entire face from one person’s to another’s using different sensors. Since halloween is right around the corner, I decided to turn a beautiful woman into a witch using a proximity sensor.

This is the first sketch I made to use for the rest of the frames. It is my first shot at animation with any medium, but I have always been an artist at heart it came quite naturally:

Next, I built a little station to trace, and alter my frames in sketch form, and for that I used a lamp and a transparent platform to illuminate the sketches from below.

Necessity is the mother of invention right?

The FRAMES:

Here’s a GIF of the animation:

Sensor:

I used an ultrasonic sensor (HC-SR04) for this project, because it’s range was 3 cm to 4 m, which is perfect for a person walking towards the sensor and keeps the screen in visibility range.

The sensor works by using one pin as an output to emit a sound wave, and the other as an input to receive it back after it has bounced off the object in front of it. So my code, which was to use the sensor through the arduino and return a value for distance, had to include the simple distance/speed formula using the speed of sound.

 

Once my sensor started working efficiently to give the distance in centimeters, I noticed that the sensor was not working efficiently after about 1 meter, and it worked best when used which a smooth object, instead of a person.

The code for serial communication and the animation is below:

FACES from Amena Hayat on Vimeo.

 

http://alpha.editor.p5js.org/amena91/sketches/B1rKrlaTb

All Shapes and Sizes Barbie

Barbie, and the world in general, has been known to celebrate and look up to one particular kind of beauty, which is tall, skinny and white skinned. Not only is this standard highly difficult to achieve, but the non-inclusivity of the concepts of beauty that the general public has held for generations has been making women all over the world feel less comfortable in their own skin. Women are body shamed when they don’t fit a certain mould or skin color.

Generic old iconic-ass Barbie

Doll companies, alongside the people who control famous actresses and models, and popular media from advertisements to fashion magazines promote unattainable and restrictive beauty standards and cause a lot of pain, body dysphoria, and low self esteem among women and girls. Capitalism uses low self esteem as fuel to sell women products, and sometimes it all starts with a little girls first Barbie doll. Fortunately, recently women are now taking charge of what these beauty standards may be. A movement of acceptance has started, aiming towards more body positivity and diversity.

Body Positivity + Diversity

This has not only led to the Photoshopping of fashion magazines to be revealed more often, to models speaking about eating disorders, belly rolls, and stretch marks, but also doll companies to invest in dolls that more people can relate to. Ideals are being shifted, and capitalism is tweaking itself.

These are the dolls of the All Shapes and Sizes Barbie campaign:

In this assignment, I decided to make a function that takes as arguments weight, height and ethnicity, and outputs a doll.

function allShapesBarbie(x,y,heavy,tall,ethnicity)

(x,y): Coordinates on canvas

Heavy: Weight in pounds, ranging from 90 to 200 lbs

Tall: Height in inches, ranging from 5 foot 2 inches to 6 feet

Ethnicity: 1 for White, 2 for South Asian, 3 for African American — limited to three in my code to keep it simple but more can be added.

My code:

function setup() {
createCanvas(650, 500);
}

function draw() {
allShapesBarbie(50,100,90,67,1);
allShapesBarbie(150,126,130,63,2);
allShapesBarbie(250,78,105,70,3);
allShapesBarbie(350,145,90,60,1);
allShapesBarbie(465,146,190,60,3);
allShapesBarbie(580,68,90,72,2);
}

function allShapesBarbie(x,y,heavy,tall,ethnicity){

let skinColor;
let white = color(252, 226, 199);
let black = color(140, 95, 49);
let southasian = color(229, 166, 114);

if (ethnicity == 1){
skinColor = white;
}
else if (ethnicity == 2){
skinColor = southasian;
}
else if (ethnicity == 3){
skinColor = black;
}
else {print(“Invalid Ethinicty Number, Enter 1, 2 or 3 please.”);}

//Face
let faceWidth = map(heavy,90,200,40,54);
ellipseMode(CENTER);
rectMode(CENTER);
noStroke();
fill(112, 60, 4);
arc(x+7,y-22,42,26,PI,0);
arc(x-14,y-22,24,22,PI,0);
rect(x+1,y+5,54,59);
fill(skinColor);
ellipse(x,y,faceWidth,54);//faceshape
strokeWeight(1);
stroke(0);
fill(255);
ellipse(x-10,y-5,8,10);//eyes
ellipse(x+10,y-5,8,10);
fill(0);
ellipse(x-10,y-4,6,6);
ellipse(x+10,y-4,6,6);
noStroke();
fill(168, 24, 11);
ellipse(x-3,y+10,9,9);//lips
ellipse(x+3,y+10,9,9);
fill(242, 53, 36);
arc(x,y+10,15,12,0,PI);
fill(112, 60, 4);
arc(x+7,y-28.5,30,30,0,PI);//fringe

//Dress

let shoulder = map(heavy,90,200,30,40);
let waistx1 = map(heavy,90,200,20,40);
let waisty1 = map(tall,60,72,78,98);
let waistx2 = map(heavy,90,200,40,70);
let waisty2 = map(tall,60,72,140,170);
fill(0);
beginShape();
vertex(x-(shoulder),y+34);
vertex(x-10,y+34);
vertex(x-10,y+24);
vertex(x+10,y+24);
vertex(x+10,y+34);
vertex(x+(shoulder),y+34);
vertex(x+(waistx1),y+(waisty1));
vertex(x+(waistx2),y+(waisty2));
vertex(x-(waistx2),y+(waisty2));
vertex(x-(waistx1),y+(waisty1));
vertex(x-(shoulder),y+34);
endShape(CLOSE);

//Arms //put behind dress?

noFill();
let armFat = map(heavy,90,200,13,26);
let armLength = map(tall,60,72,80,122);
strokeWeight(armFat);
stroke(skinColor);
arc(x-(waistx1),y+(waisty1),36,armLength,HALF_PI,(3/2)*PI);
arc(x+(waistx1),y+(waisty1),36,armLength,(3/2)*PI,HALF_PI);

//Thighs

fill(skinColor);
let thighx1 = map(heavy,90,200,28,31);
let thighx2 = map(heavy,90,200,10,4);
let thighy = map(tall,60,72,140,170);
let kneex1 = map(heavy,90,200,18,26);
let kneex2 = map(heavy,90,200,10,16);
let kneey = map(tall,60,72,190,250);

beginShape();
vertex(x-(thighx1),y+(thighy));
vertex(x-(kneex1),y+(kneey));
vertex(x-(kneex2),y+(kneey));
vertex(x-(thighx2),y+(thighy));
endShape(CLOSE);
beginShape();
vertex(x+(thighx1),y+(thighy));
vertex(x+(kneex1),y+(kneey));
vertex(x+(kneex2),y+(kneey));
vertex(x+(thighx2),y+(thighy));
endShape(CLOSE);

//Boots
fill(0);
noStroke();
let bootx1 = map(heavy,90,200,32,40);
let bootx2 = map(heavy,90,200,0,2);
let booty = map(tall,60,72,195,250);
let bootheelx = (bootx1 + bootx2)/2
let bootheely = map(tall,60,72,270,350);

triangle(x-(bootx1),y+(booty),x-(bootheelx),y+(bootheely)+10,x-(bootx2),y+(booty));
ellipse(x-(bootheelx),y+(bootheely),25,20);
rect(x-(bootheelx),y+(bootheely)+7.5,25,5);

triangle(x+(bootx1),y+(booty),x+(bootheelx),y+(bootheely)+10,x+(bootx2),y+(booty));
ellipse(x+(bootheelx),y+(bootheely),25,20);
rect(x+(bootheelx),y+(bootheely)+7.5,25,5);

}

The output:

Play with more dolls here:

http://alpha.editor.p5js.org/amena91/sketches/HkEdfl53-