I created a smart watch interface for someone with anxiety and panic disorder. It reads someone’s pulse and breath rate and can detect when someone is having a panic attack. I used real-time biometric data from a pulse sensor and a wind sensor and hooked it up to an ardiuno, then I used serial communication to send the data and trigger & display different calming graphics using p5.js. I will go through the project interface and design first, then I will show you how I went about doing it.
Depending on the kind of panic attack you are having, there are 3 modes/techniques. The good thing is that any of these techniques would work during a panic attack. I weighted certain techniques to be triggered based on someone’s sensor data. But for the purpose of the scope of this project, and because this requires more knowledge, time and research, I used keyboard keys to to demonstrate the different interfaces.
These techniques and methods were based on my research on medical articles here, here, and here.
“Recently I found that trying to focus on something else such as counting numbers… works, because it requires concentration.”
Melanie Luxenberg
I used the step by step guide provided by NYU’s physical computing department here to learn how to use ardiuno and p5.js together. If you follow that guide, you can know how to download and run the project. You will need to download the P5.js complete library, the P5.serialserver, and the ardiuno software.
You can download all the files you need to run the project here:
galsanea_FinalProject_allFiles
If you just want to see the p5.js interaction, I modified the project to run on WordPress using mouse X location to simulate breathing pattern, and mouse Y location to simulate pulse patterns.
Use the following to replace the sensor data interaction:
keys:
b: breathing blobs interface
c: counting interface
g: game interface
any other key: regular clock interface
mouse:
x: move mouse in x directions to simulate breathing
y: move mouse in y directions to simulate heart beat
/*
Ghalya Alsanea
Section B
galsanea@andrew.cmu.edu
Final Project
normal mode:clock
date
sensor data: heart rate with beating heart
panic mode: deep breathing --- moving blob objects reacting to your breathing
distraction technique --- use pingpong-like game to distract you
counting technique --- count till a number to calm down
*/
// ----------------------------------------------------------------------------
// overall global variables
var proportion = 500; //canvas proportion
var MX, MY; //mouse x and y locations
// ----------------------------------------------------------------------------
//SENSOR global variables
var pulseSensor; //global variable for pulse sensor data
var breathSensor; //global variable for breath sensor data
var toggle = false; //toggle to help sort the incoming sensor data from the serial
var pulseData = []; //array container for incoming pulse data
// ----------------------------------------------------------------------------
// SERIAL global variables
var serial; // variable to hold an instance of the serialport library
var portName = 'COM11'; // fill in your serial port name here
var inData; // for incoming serial data. NOTE: it comes a byte at a time
var options = {baudrate: 9600}; // change the data rate to whatever you wish
// ----------------------------------------------------------------------------
// CLOCK global variables
var diameter = proportion * 0.75; //clock diameter
//configure clock hands based on clock diameter
var secHand = diameter * 0.45;
var minHand = diameter * 0.35;
var hourHand = diameter * 0.2;
// ----------------------------------------------------------------------------
// BLOBS global variables
var blobs = []; //used to store instances of blob objects
var change = 0; //stores the rate of rotation and the y coordinate for noise later
var colorsPalette; //color palette for blob objects
var numBlobs = 120; //number of blobs to make
// ----------------------------------------------------------------------------
// GAME global variables
var ballX; //ball x position
var ballY; //ball y position
var ballSize = 40; //diameter of ball
var xvel; //horizontal velocity
var yvel; //vertical velocity
var d; //distance between mouse and circle center
var dangerX; //danger zone
var ballColor; //what to color the ball
var score; //keep score of hits
var scores = []; //keep a list of all final scores to get the highest score
// ----------------------------------------------------------------------------
// CLICKER global variables
var gClickCount = 0; //mouse click counter
var inc = 20; //x increments
var yInc = 100; //y increments
var yPos1 = 25 //top y position
var yPos2 = yPos1 + 50; //bottom y position
// ----------------------------------------------------------------------------
function setup() {
createCanvas(proportion, proportion);
// setupSerial(); ..comment out to disable serial
setupBlobs();
setupGame();
}
// ----------------------------------------------------------------------------
function draw() {
//constrain mouse x and y to within the canvas
MX = constrain(mouseX, 0, width);
MY = constrain(mouseY, 0, height);
if (key === "b") {
//draw the blobs
displayBlobs();
} else if (key === "g") {
//draw the game
drawGame();
} else if (key === "c") {
//draw clicker
displayClicker();
} else {
clockDisplay();
}
}
////////////////////////////////////////////////////////////////////////
//**************************** SERIAL ********************************//
////////////////////////////////////////////////////////////////////////
function setupSerial() {
serial = new p5.SerialPort(); // make a new instance of the serialport library
serial.on('data', serialEvent); // callback for when new data arrives
serial.open(portName); // open the serial port
}
function serialEvent() {
// read a byte from the serial port, convert it to a number
// NOTE: the inData comes in a byte at a time (so range is 0-255)
inData = serial.read();
// sort the data incoming by distributing every other byte to their respective variable
// serial data comes in 2 at a time, so using a toggle you can store the data correctly
if (toggle) {
pulseSensor = inData;
} else {
breathSensor = inData;
}
toggle = !toggle;
}
////////////////////////////////////////////////////////////////////////
//*********************** base/CLOCK Display *************************//
////////////////////////////////////////////////////////////////////////
function clockDisplay() {
background(255, 228, 225); //mistyRose
noStroke();
//show the date
dateDisplay();
//for testing: using mouse data instead of sensor data
var size = map(MY, 0, height, 20, 40);
var BPM = 88;
// // for serial: map heart size to pulse sensor data
// var size = map(pulseSensor, 0, 255, 20, 40);
//
// //calculate beats per min (BPM) based on average pulse data
// pulseData.push(pulseSensor);
// //total pulse values read
// var total = 0;
// //find the total of all the readings
// for(var i = 0; i < pulseData.length; i++) {
// total += pulseData[i];
// }
// //divide by the length of the data to get the average
// var avg = total / pulseData.length;
// //map the average number onto a range of BPM hear rates
// var BPM = map(avg, 0, 255, 70, 130);
//
// //only read the last 100 readings
// if (pulseData.length > 100) {
// pulseData.shift();
// }
//show the heart rate data
heartDisplay(size, BPM);
//draw from the center of the canvas
push();
translate(width / 2, height / 2);
//draw clock base
fill(255);
circle(0, 0, diameter);
//convert time to angles
//subtract half a circle so the clock starts at the top, rather than 6 o'clock
var s = map(second(), 0, 60, 0, TWO_PI) - PI;
var m = map(minute(), 0, 60, 0, TWO_PI) - PI;
//the % takes the remainder of the hours/12, which makes the dif between AM/PM
var h = map((hour() % 12), 0, 12, 0, TWO_PI) - PI;
//draw the hands
drawClockHands(s, secHand, 1); //seconds hand
drawClockHands(m, minHand, 3); //minutes hand
drawClockHands(h, hourHand, 5); //hour hand
//draw the ticks every 6 degrees (6 * 60 = 360)
for (i = 0; i < 360; i += 6) {
//make every 5th tick thicker
if(i % 30 == 0) {
strokeWeight(2);
} else {
strokeWeight(1);
}
push();
rotate(radians(i));
line(0, diameter / 2, 0, secHand + 5);
pop();
}
pop()
}
//draw the clock hands
function drawClockHands(angle, hand, weight) {
stroke("PaleVioletRed");
push();
rotate(angle);
strokeWeight(weight);
line(0, 0, 0, hand);
pop();
}
//display today's date
function dateDisplay() {
var d = day();
var m = month();
var y = year();
fill("PaleVioletRed");
textAlign(LEFT);
textSize(18);
text(m + "." + d + "." + y, 10, height - 15);
}
//display heart rate with heart graphic
function heartDisplay(size, BPM) {
textSize(12);
textAlign(CENTER);
text(floor(BPM), width - 50, height - 60);
drawHeart(width - 50, height - 50, size);
}
function drawHeart(x, y, size) {
beginShape();
vertex(x, y);
bezierVertex(x - size / 2, y - size / 2, x - size, y + size / 3, x, y + size);
bezierVertex(x + size, y + size / 3, x + size / 2, y - size / 2, x, y);
endShape(CLOSE);
}
////////////////////////////////////////////////////////////////////////
//******************** breathing BLOB Display ************************//
////////////////////////////////////////////////////////////////////////
function setupBlobs() {
//create color paletter for the blob objects
colorsPalette = [color(146, 167, 202,30),
color(186, 196, 219,30),
color(118, 135, 172,30),
color(76, 41, 81,30),
color(144, 62, 92,30),
color(178, 93, 119,30),
color(215, 118, 136,30),
color(246, 156, 164,30)];
// create new blob object for every num of blobs
for (var i = 0; i < numBlobs; i++){
var temp = makeBlob(i + 0.1, i, i * random(90),
colorsPalette[floor(random(colorsPalette.length))]);
blobs.push(temp);
}
}
function displayBlobs() {
background(240, 200, 240, 30);
//for testing: using mouse data instead of sensor data
var addToRadius = map(MX, 0, width, 0, 100);
// //for serial: map the breathing sensor data to blob radius
// //it's inversely mapped because when you breath out it decreases and vice versa
// var addToRadius = map(breathSensor, 255, 0, 0, 100);
for(var i = 0; i < blobs.length; i++){
blobs[i].r = i + addToRadius;
blobs[i].draw(change);
}
//create rotation change and noise change
change+=0.01;
}
function makeBlob(radius, roughness, angle, color){
var blob = {r: radius, // radius of blob
x: width / 2, // x position of blob
y: height / 2, // y position of blob
rough: roughness, // magnitude of how much the circle is distorted
ang: angle, // how much to rotate the circle by
c: color, // color of the blob
draw: drawBlob, //draw the blobs
}
return blob;
}
function drawBlob(change) {
noStroke();
fill(this.c); //color to fill the blob
push();
translate(this.x, this.y); //move to xpos, ypos
rotate(this.ang + change); //rotate by this.angle+change
beginShape(); //begin a shape based on the vertex points below
//create vertex points
var off = 0;
for (var i = 0; i < TWO_PI; i += 0.1) {
var offset = map(noise(off, change), 0, 1, -this.rough, this.rough);
var R = this.r + offset;
var x = R * cos(i);
var y = R * sin(i);
vertex(x, y);
off += 0.1;
}
endShape();
pop();
}
////////////////////////////////////////////////////////////////////////
//************************ GAME display ******************************//
////////////////////////////////////////////////////////////////////////
function setupGame() {
noStroke();
//ball starts at the center of the canvas
ballX = width / 2;
ballY = height / 2;
//define danger zone
dangerX = width / 4;
//set x and y velocity
xvel = 5;
yvel = 0;
//set score
score = 0;
}
function drawGame() {
background (100);
//draw the hitting paddle
strokeWeight(5);
stroke(255);
line(mouseX, mouseY + 10, mouseX, mouseY - 10);
//instructions
noStroke();
fill(255);
textAlign(LEFT);
writeText(20, "Let's play a game!", 20, 25);
writeText(12, "Don't let the ball go left of the screen", 20, 50);
//define the playing area
fill(0);
rect(width / 2, 0, width / 2, height);
//warn if ball is too close to edge
if(ballX - ballSize / 2 <= dangerX & xvel < 0){
ballColor = "yellow";
} else {
ballColor = "white";
}
//draw the circle
fill (ballColor);
circle(ballX, ballY, ballSize);
//makes the ball move in x and y directions
ballX = ballX + xvel;
ballY = ballY + yvel;
//When it hits the right edge of the canvas,
//it reverses horizontal velocity
if (ballX >= width - ballSize / 2) {
xvel = -1 * xvel;
}
//when it hits the bottom or top edge of the canvas,
//it reverses vertical velocity
if (ballY <= ballSize / 2) {
yvel = -1 * yvel;
} else if (ballY >= height - ballSize / 2) {
yvel = -1 * yvel;
}
//find the distance between ball center and mouse
d = dist(mouseX, mouseY, ballX, ballY);
//if (1) the mouse hits the edge of the ball
//and (2) the mouse is on the left half of the canvas
//and (3) the x velocity is negative
//then switch directions and get a random yvel.
if (d <= ballSize / 2 & mouseX <= width / 2 && xvel < 0){
yvel = random(-3, 3);
xvel = -1 * xvel;
yvel = -1 * yvel;
score = score + 1;
}
//keep current score
fill("white");
writeText(30, score, width - 50, height - 50);
//reset game if th eball went to far out the canvas
if (ballX < -150) {
resetGame();
}
}
//reset game
function resetGame() {
scores.push(score) //push final score to scores
background (0, 51, 102);
fill(255);
textAlign(LEFT);
writeText(80, "IT'S OK!", 10, height / 4);
writeText(30, "click to try again", 20, height / 2);
//cureent score
writeText(20, "score: " + score, 20, height / 2 + 50);
//highest score
writeText(20, "best score: " + max(scores), 20, height / 2 + 100);
if (mouseIsPressed){
setupGame();
}
}
function writeText(size, words, x, y) {
textSize(size);
text(words, x, y);
}
////////////////////////////////////////////////////////////////////////
//*********************** COUNT display ******************************//
////////////////////////////////////////////////////////////////////////
function displayClicker() {
background("pink");
if (gClickCount === 0) {
noStroke();
fill(0);
textAlign(LEFT);
textSize(20);
text("tap me and count to fives...", 20, 50);
}
stroke (0);
strokeWeight(3);
//everytime you click, draw a line
for(var i = 0; i < gClickCount; i++){
//variable to show which line the tickmarks are on
var a = floor((i * inc + inc - 1) / width);
//x and y locations depending on which line
var x = i * inc + inc - width * a;
var y1 = yPos1 + a * yInc;
var y2 = yPos2 + a * yInc;
if ( (i + 1) % 5 === 0) {
//draw diagonal line every fifth tick
line(x, y1, x - 5 * inc, y2);
} else {
//otherwise draw a straight line
line(x, y1, x, y2);
}
}
}
function mousePressed() {
//everytime you click, you increase the amount of times the loop runs
gClickCount++;
}
function keyPressed() {
//reset gClickCount to zero whenever a keyboard key is pressed
gClickCount = 0;
}
]]>The Mill+ joined forces with Lush to create a 2-minute film created via biometric data visualization – the aim being to visualize the physical response someone has to a Lush spa treatment. Essentially, turning biometric data into art.
Mill+ Creative Director Carl Addy comments, “The data captured was fascinating. It shows a clear correlation between the treatment and the subject’s biometric response. You can actually see the moments when a sound or touch elicited a shift in brain wave which then triggers a reaction in breath and heart rate.”
Find more here(credits) & here(behind the scenes).
This post shows 7 visualization techniques to help ease your anxiety. For my final project I am looking at how visual queues can trigger emotional calming responses, and I was using this project as a study point. Here are some examples:
I love the how coherent and consistent the graphics are. Something that I wouldn’t necessary do is put people in the visualization, unless they’re vague enough figures, because I worry when something is as personal as your mind, you wouldn’t want someone to feel like they can’t relate to the visualization because “it doesn’t look like them”.
Read more here.
When comparing the two, I admire both the visual coherency that exists within each project. The first one is interesting because it is a visualization the takes inputs and does not necessarily have a specific output in mind. The second one is the opposite in the sense that it has a highly specific output it is trying to achieve through the visualization. For my final project, I want to find a way to do both and play with the two balances based on what’s needed.
]]>I want to create a smart watch interface for someone with anxiety and panic disorder. I plan on using real-time biometric data from sensors and using the data to trigger and display things using p5.js. I have done projects before the use p5 serial communication, but my plan b is that is it does not work, then I will simply use button interactions in p5.js to trigger the different modes.
Normal Mode includes a watch interface that displays the time and date, in addition to the sensor data in an artistic, data-visualization way (I am thinking something similar to a mood visualizer type of thing). The panic mode can be triggered through two ways: a panic button the user presses or sensor data that indicates the user is having a panic attack. In Panic Mode, the canvas cycles through the following anxiety relieving techniques:
If all of the following techniques do not work, then this triggers a “call emergency contact” state, which calls someone you designated as a person to reach out to. For example, “calling your mom…”
The biometric sensors I am thinking of using are: a heart rate (PPG) sensor, a GSR sensor, and a respiratory rate sensor. The last one, I might not need, I am waiting to confirm with a specialist…
PROJECT: PULSE
ARTIST: Janet Echelman
Janet Echelman is an artist who defies categorization. She creates experiential sculpture at the scale of buildings that transform with wind and light.
This is the first part of a permanent installation which opened on September 12, 2018 in the heart of Philadelphia’s thriving downtown. It is integrated with Kieran Timerlake’s Dilworth Park Plaza (opened to public in 2014), on the west side of City Hall, where many transit lines run below. The artist was commissioned by the Center City District to incorporate the site’s historic associations with water and transportation. The trains passing below will activate four-foot-tall curtains of colorful atomized mist that shows the path of a specific transit line. “These rail lines bring over 70,000 passengers to the site each day.”
This piece is really interesting because the specialized pumps create an ephemeral fog-like mist made of filtered, softened water onto which lighting is projected so it is completely safe for children to play in. I learned that this design and plaza integration took 9 years from the start of construction to the first reveal and launch of the green line phase and they’re fundraising to open the blue and orange lines, each will correspond to different SEPTA transit lines. I think this project starts to show how Philly is really harnessing the strength of their Center City District, a non profit org, to show how public efforts can successfully be directed towards creating better lives and experiences for people in the city. As much as this art celebrates history (first water pumping station in this area and also across from Pennsylvania Railroad Station) it also embraces the future by bringing technology into public space for not only spectatorship, but also interaction.
Artist: Janet Echelman
Studio Echelman Team: Melissa Henry, Daniel Zeese, Cameron Chateauneuf, Drew Raines, Melanie Rose Peterson, Rachel Kaede Newsom, Becky Borlan, Daniel Lear
The Olin Studio: Susan Weiler, Richard Roark, Ben Monette, Greg Burrell, Kasey Toomey
ARUP Lighting: Brain Stacy, Christoph Gisel
Urban Engineers: Andrew Scott
Kieran Timberlake: Steve Kieran, Marilia Rodrigues
CMS: Nadine Nemec, Chris Cook, Roy Kaplan
Center City District: Paul Levy
Images: Sean O’Neill, Melvin Epps, Sahar Coston-Hardy
For this project, I wanted to maximize randomization of the space objects, but still have some sort of coherency. Mostly, I was trying to become more comfortable with creating different classes of objects.
//Ghalya Alsanea
//galsanea@andrew.cmu.edu
//Section B
//Project 11
var lines;
var totalLines = 0;
var j; //jiggle
var f; //flower
var p1, p2, p3; //planets
var sp; //spinning planet
//stars
var star;
var star2;
function preload() {
//
}
function setup() {
createCanvas(600, 600);
background(0);
lines = new Array(500);
//initiate the dif class ojects
j = new Jiggle();
f = new Flower();
p1 = new Planet();
p2 = new Planet2();
p3 = new Planet3();
sp = new SpinPlanet();
star = new Star();
star2 = new Star();
}
function draw() {
background(0);
//create the shooting starts lines
lines[totalLines] = new Line();
totalLines++;
for (var i = 0; i < totalLines; i++) {
lines[i].move();
lines[i].display();
}
//show and move the objects
//jiggle circle
j.move();
j.show();
//flower
f.move();
f.show();
// planets
p1.move();
p1.show();
p2.move();
p2.show();
p3.move();
p3.show();
//spinplanet
sp.move();
sp.show();
//star 1
star.move();
star.show();
//star 2
star2.move();
star2.show();
}
//////////////////////////////////////////////////////////////////////
// define the classes //
//////////////////////////////////////////////////////////////////////
class Jiggle {
constructor() {
this.x = random(0, width); //start at a random xpos
this.y = height + 100; //make sure to start off the canvas
this.size = random(20, 50); //create a new size
this.col = color("orange"); //define the color
}
move() {
this.y-=2; //move up
this.x = this.x + random(-5, 5); //make the "jiggle" movement
//if it moves too far up, reset the object
if (this.y < -this.size) {
this.y = height;
this.x = random(0, width);
this.size = random(20, 50);
}
}
show() {
//draw the object
noStroke();
strokeWeight(1);
fill(this.col);
circle(this.x, this.y, this.size);
}
}
//////////////////////////////////////////////////////////////////////
class Flower {
constructor() {
this.x = random(0, width); //start at a random xpos
this.y = height + 100; //start y loc off the canvas
this.size = random(40, 80); //randomize size
}
move() {
this.y--; //move up
this.x = this.x + random(-1, 1);//cause the jiggle
//reset if it goes too far up
if (this.y < -this.size) {
this.y = height;
this.x = random(0, width);
this.size = random(40, 80);
}
}
show() {
// draw the object
noStroke();
fill(204, 101, 192, 180);
//rotate
push();
translate(this.x, this.y);
for (var i = 0; i < 10; i++) {
ellipse(0, 30, this.size / 6, this.size);
rotate(PI / 5);
}
pop();
}
}
//////////////////////////////////////////////////////////////////////
class Star {
constructor() {
this.x = random(0, width); //start at random xpos
this.y = height - 50; //start y loc off canvas
this.col = color("yellow");
this.npoints = random(5, 12); //points in star
this.angle = TWO_PI / this.npoints;
this.halfAngle = this.angle / 2.0;
this.r1 = random(5, 20); //inner radius
this.r2 = random(20, 40); //outer radius
}
move() {
this.x;
this.y = this.y - random(0.8, 1.2); //move in a random speed
// reset if it goes off cnavas
if (this.y < -100) {
this.y = height;
this.y -= random(0.8, 1.2);
this.x = random(0, width);
this.npoints = random(5, 12);
this.angle = TWO_PI / this.npoints;
this.halfAngle = this.angle / 2.0;
this.r1 = random(10, 30);
this.r2 = random(60, 100);
}
}
show() {
//draw the object
noStroke();
fill(this.col);
//rotate it
push();
translate(this.x, this.y);
rotate(radians(frameCount)/2);
beginShape();
//draw a star based on angles and npoints
for (var a = 0; a < TWO_PI; a += this.angle) {
var sx = cos(a) * this.r2;
var sy = sin(a) * this.r2;
vertex(sx, sy);
sx = cos(a + this.halfAngle) * this.r1;
sy = sin(a + this.halfAngle) * this.r1;
vertex(sx, sy);
}
endShape();
pop();
}
}
//////////////////////////////////////////////////////////////////////
class Planet {
constructor() {
this.x = random(0, width);
this.size = random(10, 50);
this.y = height - this.size;
this.col = color("red");
}
move() {
this.x;
//Moving up at a constant speed
this.y -= 0.75;
// Reset to the bottom
if (this.y < -100) {
this.y = height + 250;
this.size = random(10, 50);
}
if (this.x < 0 || this.x > width) {
this.x = (random(10, 500)) - 25;
}
}
show() {
//draw planet with ring
fill(this.col);
noStroke();
circle(this.x, this.y, this.size);
noFill();
stroke(this.col);
strokeWeight(1);
ellipse(this.x, this.y, this.size * 2, this.size / 2);
}
}
//////////////////////////////////////////////////////////////////////
class Planet2 {
constructor() {
this.x = random(0, width);
this.y = height + 100;
this.size = random(50, 80);
this.col = color("pink");
}
move() {
this.x;
//Moving up at a constant speed
this.y-=1.1;
// Reset to the bottom
if (this.y < -50) {
this.y = height;
this.size = random(50, 80);
}
if (this.x < 0 || this.x > width) {
this.x = (random(10, 500)) - 25;
}
}
show() {
//draw planet with rotated ring
fill(this.col);
noStroke();
circle(this.x, this.y, this.size);
stroke(this.col);
strokeWeight(2);
noFill();
push();
translate(this.x, this.y);
rotate(PI/4);
ellipse(0, 0, this.size * 1.5, this.size / 3);
pop();
}
}
//////////////////////////////////////////////////////////////////////
class Planet3 {
constructor() {
this.x = random(0, width);
this.y = height + 100; //start below window
this.size = random(50, 80);
this.col = color("coral");
}
move() {
this.x = this.x + random(-1, 1);
//Moving up at a constant speed
this.y -= 0.85;
// Reset to the bottom
if (this.y < -100) {
this.y = height;
this.size = random(50, 80);
}
if (this.x < 0 || this.x > width) {
this.x = (random(10, 500)) - 25;
}
}
show() {
//draw planet with rotating ring
fill(this.col);
noStroke();
circle(this.x, this.y, this.size);
noFill();
stroke(this.col);
push();
translate(this.x, this.y);
rotate(radians(frameCount)/2);
strokeWeight(1);
ellipse(0, 0, this.size / 2, this.size * 2);
pop();
}
}
//////////////////////////////////////////////////////////////////////
class SpinPlanet {
constructor() {
this.x = random(0, width);
this.y = height + 100; //start below window
this.size = random(50, 90);
this.col = color("green");
}
move() {
this.x = this.x + random(-1, 1);
//Moving up at a constant speed
this.y -= 0.65;
// Reset to the bottom
if (this.y < -100) {
this.y = height;
this.size = random(50, 90);
}
if (this.x < 0 || this.x > width) {
this.x = (random(10, 500))-40;
}
}
show() {
//draw a spinning planet
fill(this.col);
noStroke();
circle(this.x, this.y, this.size);
noFill();
stroke(this.col);
strokeWeight(2);
push();
translate(this.x, this.y);
rotate(frameCount);
ellipse(random(-1,1), 0, this.size / 2, this.size * 2);
pop();
}
}
//////////////////////////////////////////////////////////////////////
class Line {
constructor() {
this.r = height + 200; // starting pt + 200 so to make sure it starts off canvas
this.x = random(width); // Start with a random x location
this.y = this.r; // Start pt below the window
this.speed = random(2, 5); // Pick a random speed
this.l = random(100, 200); //randomize length of line
}
move() {
// move y upwards by speed
this.y -= this.speed;
}
display() {
//only draw if it's in the canvas
//draw the shooting stars lines
if (this.y > 0) {
strokeWeight(1);
stroke(random(100, 255), random(100, 255), random(100, 255), 200);
line(this.x, this.y, this.x, this.y - this.l);
}
}
}
]]>The Reverse Collection (2013 – 2016)
Creator: Tarek Atoui is a Beirut based artist and composer. What I admire about his work is that they’re grounded in extensive knowledge about music history and tradition.
The Reverse Collection is an ensemble of newly created instruments that were conceived in a few steps. The first stage was born out of the storage facilities of Berlin’s Dahlem museum, which houses the city’s ethnographic and anthropological collections. Atoui found that in the museum’s stash of historical instruments, there were many instruments with hardly any information/indication of their sociocultural provenance or any instructions on how to play them. So, he invited established musicians and improvisers to play with them, and recorded what happened, even though they had no idea how to use them.
The second stage was in collaboration with instrument makers. He asked them to listen to those recordings – layered and edited – and create new instruments on which someone could play something that sounds similar. Around 8 new instruments where created, and were used for several performances and new compositions, called the Reverse Sessions.
Finally, the third stage, which is The Reverse Collection, had a new generation of ten instruments to work with. Atoui recorded several performances to create a collection. This was done by a multi-channel sound work in an exhibition space, that was evolving over the whole duration of a show and allowing multiple associations between object, sound, space and performance. More on the performance and what the instruments are here.
I chose this project because I really admire the translation of historical artifacts into a modern instrument. Even though the result was a physical manifestation, the process of creating and understand the instruments was computational.
]]>To play or stop a sound, simply click inside the corresponding circle.
//Ghalya Alsanea
//galsanea@andrew.cmu.edu
//Section B
//Project 10
var amplitude; //global amp variable
var defaultSize = 50; //size of circles if nothing is playing
//compile titles into array for easy access
var titles =
["alien", "bass", "chirp", "snare",
"marimba", "piano", "synth", "hop"];
//have a callerID for the dif sounds for easy access
var callIDs = [];
//X location of all the circles
var xLoc = [];
//set starting booleans for dif sounds
let alienBool = false;
let bassBool = false;
let chirpBool = false;
let kickSnareBool = false;
let marimbaBool = false;
let pianoBool = false;
let synthBool = false;
let hopBool = false;
//store all booleans in an array for easy access
var bools = [alienBool, bassBool, chirpBool, kickSnareBool,
marimbaBool, pianoBool, synthBool, hopBool]
function preload() {
//load all sound files
alienBass = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/11/alien-bass-loop.wav");
bassLoop = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/11/bass-loop.wav");
chirp = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/11/electricbirdchirp.wav");
kickSnare = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/11/kick-snare-loop.wav");
marimba = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/11/marimba-loop.wav");
piano = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/11/piano-loop.mp3");
synth = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/11/synth-142.wav");
hop = loadSound("https://courses.ideate.cmu.edu/15-104/f2019/wp-content/uploads/2019/11/trip-hop.wav");
}
function setup() {
createCanvas(600, 150);
useSound();
amplitude = new p5.Amplitude();
//assign IDs to each sound
callIDs = [alienBass, bassLoop, chirp, kickSnare, marimba, piano, synth, hop];
for (var i = 0; i < callIDs.length; i++) {
callIDs[i].stop(); //start with no sound
callIDs[i].setLoop(true); //make sure it plays a continous loop
xLoc.push(i * 75 + 37.5); //assign x location based on ID index
}
}
function draw() {
//function call for rendering the circles
renderCircs();
}
function renderCircs () {
background(0);
for (var i = 0; i < callIDs.length; i++) {
//if the sound is playing, draw as red
//otherwise render as white
if (bools[i] === true) {
fill('red');
} else {
fill(255);
}
//render circle and text
circle(xLoc[i], height / 2, figureSize(bools[i]));
textSize(15);
textFont("Arial");
textAlign(CENTER);
text(titles[i], xLoc[i], height - 30);
}
}
//configure diameter of circle based on volume
function figureSize(a) {
//if the sound is playing, assign a size respectively
if (a === true) {
let level = amplitude.getLevel();
let size = floor(map(level, 0, 1, 50, 70));
return size;
}
//if no sound is playing then have the default size
else {
return defaultSize;
}
}
//configure whether to play the sound or stop based on boolean
function soundState(ind) {
if (bools[ind] === true) {
callIDs[ind].play();
} else if (bools[ind] === false) {
callIDs[ind].stop();
}
}
//state change when mouse is clicked
function mousePressed() {
for (var i = 0; i < xLoc.length; i++) {
//check where the mouse was clicked
var d = dist(mouseX, mouseY, xLoc[i], height / 2);
//if it's within one of the circles, then toggle sound state
if (d < figureSize(bools[i]) / 2) {
bools[i] = !bools[i];
soundState(i);
}
}
}
function soundSetup() {
//nothing here
}
For this project, I wanted to create something similar to a looping machine where you have different sounds and you can play them together and start and stop certain loops based on creative desire. I wanted the user to be in control of what’s being played and express themselves through sound. The 8 sounds I chose have the ability to be looped continuously and smoothly. The sounds sometimes work together and sometimes don’t, and the idea behind that is to see what music you can come up with using an unlikely pairing of sounds. Have fun and see what you come up with!
]]>Since I don’t know many people in our class, I decided to go through one specific Looking Outwards week, rather than through a specific person’s assignments. I chose LO-02, because I personally loving generative art and really enjoyed researching that topic that week.
I chose to write about Ellan Suder’s post about Alfred Hoehn’s Drawing Machine mostly because I have never seen a project like this and find it really intriguing.
Building off of Ellan’s thoughts, I also am very intrigued by this project. I appreciate the simplicity, yet complexity of the machine. It is somehow shocking how just changing a bit of the diameter/length of the spinning circles can create completely different works of art. This reminds me of the Spirograph project we did.
Something that I would add to the conversation is that why make a machine that does this, when we know we can program computers to do them better? I personally like pen-and-paper, but it gets me to wonder the wast of energy and paper of creating such beautiful artworks.
Finally, I agree with Ellan, that it is very mesmerizing and calming to watch the drawing happen.
]]>//Ghalya Alsanea
//Section B
//galsanea@andrew.cmu.edu
//project-09
var img;
var WIDTH;
var HEIGHT;
var brightnessValues = []; //stores the brightness values
var toggle = true; //add a toggle for the for loop to help the code run faster
//x and y coordinate arrays for 4 shades
var curve1x = [];
var curve2x = [];
var curve3x = [];
var curve4x = [];
var curve1y = [];
var curve2y = [];
var curve3y = [];
var curve4y = [];
function preload() {
img = loadImage("https://i.imgur.com/Aqg5q8V.jpg");
}
// getPixel: fast access to pixel at location x, y from image
function getPixel(image, x, y) {
var i = 4 * (y * image.width + x);
return color(image.pixels[i], image.pixels[i + 1], image.pixels[i + 2]);
}
function setup() {
//image is 480 x 480
WIDTH = img.width;
HEIGHT = img.height;
createCanvas(WIDTH, HEIGHT);
noFill();
img.loadPixels();
}
function draw() {
background(255);
// Search and store brightness of each pixel.
//toggle: you only really need to load the brightness once, toggle can be re-toggled later
if (toggle) {
for (var i = 0; i < img.width; i++) {
for (var j = 0; j < img.height; j++) {
var b = brightness(getPixel(img, i, j));
//sort vertices depending on brightness levels
if (b < 0.04) {
curve1x.push(i);
curve1y.push(j);
}
if (b >= 0.04 & b < 10) {
curve2x.push(i);
curve2y.push(j);
}
if (b>= 10 & b < 40) {
curve3x.push(i);
curve3y.push(j);
}
if (b >= 40 & b < 100) {
curve4x.push(i);
curve4y.push(j);
}
}
}
toggle = false;
}
//LIGHTEST - light blue
stroke(200, 220, 255, 50);
drawCurve (curve4x, curve4y);
//LIGHT -medium blue
stroke(100, 120, 255, 50);
drawCurve (curve3x, curve3y);
//DARK - blue
stroke(0, 0, 255, 50);
drawCurve (curve2x, curve2y);
//DARKEST - dark gray
stroke(50, 100);
drawCurve (curve1x, curve1y);
}
function drawCurve (x, y) {
//originally I wanted to create a continous line through vertices,
//but it was getting overcrowded, so to ket the sketch-like feeling
//I used random circle sizes
// beginShape();
for (i = 0; i < x.length; i+=4) {
circle(x[i], y[i], random(10));
// curveVertex(x[i] + random(15), y[i] + random(15));
}
// endShape();
}
For this assignment, I used a portrait I took of my sister. I wanted to somehow show her personality through the portrait, hence using blue, her favorite color. Originally, I was inspired by the one-line contour drawings and set out to create those with 4 different continuous lines, each for 4 different shades of brightness.
I couldn’t figure out how to create a non-messy looking portrait, so instead I slowly shifted to using circles. I still wanted to retain the drawing-like feel, which can be shown in the final right now. Going back to showing her personality through the piece, I thought the varying sizes of the circles and varying capacities also shows her kind and loving personality.
They first looked at how much space there is in a city for moving around (ie the allocation/distribution ratios between cars, bikes, etc). The tool, “What the Street!?” is labelled as “The Mobility Space Report”, where the designers created an open-source public tool for exploring urban mobility spaces. They created a fun way to look at inequality of mobility spaces systematically AND interactively.
Play with the tool here!
They use the data visualization tools of unrolling, packing, and ordering irregular shapes. Therefore, they packed and rolled all mobility spaces into rectangular bins to visualize the areas they take up, then the tool allows you to see the same shape on a regular satellite map.