All Posts

Week 1: Self Portrait

Starting this self portrait was easy. I started off with a rouned rectangle to create the top half of my face, then added an ellipse at the bottom, to closely resemble my face shape.

Moving on to the eyes, I added two ellipses in their respected positions, then added irises, pupils, and my personal favorite, small white circles for highlights. I made the eyes looking to the right to reduce the creepiness of the image, because I’m not sure someone could stomach having this portrait directly staring at them.

I had greater aspirations for the nose, but unfortunately had to settle for two lines that meet to form a pointed nose, as well as the mouth. It was a bit of a hassle trying to understand the arc enough to create a mouth shape, but I finally managed to get it. In my defense, I haven’t taken math in a long time, and was a bit rusty on how pi and radians worked.

Lastly, I had to figure out how to do my hair. After a lot of research, the most I could settle for was creating a sine curve four times. I found a template code online (Cited in the code) on how the sine code was translated to sketch and had to spend some time playing around with it so I could fix the curves and position. After doing this, I was very happy with my curls, but scared for my life, because this was how it looked:

I was determined to keep some curls in, so I decided to add a sort of background hair, and create some bangs. I fiddled with some arcs and pulled out a protractor and finally created some bangs. I also messed around with some rectangle shapes to create a more cohesive bang and background. This leaves the curly strands looking like highlights. It still looks strange, but less creepy, i’ll say.

void setup () {
  size (500, 500);
}
  

void draw (){
  
  
  // hair- background
    //hair, the background
  stroke(105,80,45);
  fill(105,80,45);
  rect(125, 100, 250, 275, 150, 150, 50, 50);
  
  //drawing the face
  //pushStyle();
  noStroke();
  fill(224, 172, 105);
  rect(150, 100, 200, 200, 150, 150, 50, 50);
  fill(224, 172, 105);
  ellipse( 250, 250, 200, 275);
  //noStroke();
  //popStyle();
  
  // drawing the eyes
    //  white part
  fill(255, 255, 255);
  ellipse(200, 225, 50, 30);
  ellipse(300, 225, 50, 30);
  // iris
  fill(50,30,20);
  ellipse(207, 225, 28, 28);
  ellipse(307, 225, 28, 28);
  //pupil
  fill(0,0,0);
  ellipse(207, 225, 18, 18);
  ellipse(307, 225, 18, 18);  
  //highlight
  fill(255,255,255);
  ellipse(202, 220, 5, 5);
  ellipse(302, 220, 5, 5);  
  
  //attempting the nose
  stroke(112,86,53);  
  strokeWeight(3);
  //arc(250, 250, 20, 30, 50, QUARTER_PI);
  line(255, 245, 270, 275);
  line(270, 275, 255, 277);
  
  //mouth
  noFill();
  arc(250, 310, 80, 60, 0, PI);
 
  // hair -- bangs
  fill(105,80,45);
  stroke(105,80,45);
  rect(160, 100, 180, 50, 600, 300, 0, 0);
  stroke(112,86,53);
  strokeWeight(30);
  noFill();
  arc(335, 100, 160, 120, HALF_PI, PI);
  arc(150, 100, 160, 120,radians(0), radians(90));
  
  // hair -- Trying to make it curly
  // Citation; got this from a online discussion, edited it to make it horizontal 
  //https://stackoverflow.com/questions/2395434/create-a-sin-wave-line-with-processing
  strokeWeight(30);
  float a = 0.0;
  float inc = TWO_PI/25.0;
  float prev_y = 150, prev_x = 130, y, x;

  for(int i=0; i<100; i=i+4) {
    y = i +150;
    x = 130 + sin(a) * 30.0;
    line(prev_x, prev_y, x, y);
    prev_x = x;
    prev_y = y;
    a = a + inc;
  }
    
      strokeWeight(30);
  float prev_yy = 250, prev_xx= 130;

  for(int i=0; i<100; i=i+4) {
    y = i +250;
    x = 130 + sin(a) * 30.0;
    line(prev_xx, prev_yy, x, y);
    prev_xx = x;
    prev_yy = y;
    a = a + inc;
  } 
  float prev_yyy = 160, prev_xxx = 380;

  for(int i=0; i<100; i=i+4) {
    y = i +160;
    x = 380 + sin(a) * 30.0;
    line(prev_xxx, prev_yyy, x, y);
    prev_xxx = x;
    prev_yyy = y;
    a = a + inc;
  }
    
      strokeWeight(30);
  float prev_yyyy = 260, prev_xxxx= 380;

  for(int i=0; i<100; i=i+4) {
    y = i +260;
    x = 380 + sin(a) * 30.0;
    line(prev_xxxx, prev_yyyy, x, y);
    prev_xxxx = x;
    prev_yyyy = y;
    a = a + inc;
}



  
  
}

 

 

 

Week 1: Self portrait

This is my first processing project, a self portrait, and being a rather crafty person it felt just like using different paper cutouts and somehow putting them together to create a portrait of myself – well somewhat. Getting to know how to create different 2D shapes in Processing, I thought about which shapes to use to create a face and how I will take advantage of the fact that I am not limited by paper cutouts but could actually add some movement and interaction. While experimenting in class, at some point the eyes I had created where not static anymore but moving and creating a pattern behind the head which I thought would be a cool repetition of a visual element in the portrait and add some fun. I kept the face, eyes and mouth simple while trying to remember radian values from high school to create a semi-circle for the mouth. When looking for a somewhat realistic blonde tone RGB value I realized that the color would not match with the skin tone I used. That is how my favorite part of the portrait was added, the color changing hair. To make the code work took another reflection because my approach was too narrow minded thinking about our class example where the color of a rectangle would only change when clicking on it. The simple solution was to define mouseX and mouseY within the whole area of my “artwork” so you can click anywhere within the window and the hair color will change. Hope you enjoy!

P.S. I  have not died my hair yet.

Here you can see two confused eyes, no smile, and a portrait in progress.
Here you see two happy eyes, an enthusiastic smile, and hair in NYU violet – the final portrait.
//portrait
//for animated reference
int x; 
int y; 
int xSpeed = 9;
int ySpeed = 9;

//for static reference
int a;
int b;

//to change hair color
float red = 255;
float green = 0;
float blue = 0;
int value = 0;

void setup () {
  size(640, 480);
  x = width/2;
  y = height/2;
  a = width/2;
  b = height/2;
}

void draw () {
  noStroke();

  //eyes background animation
  noFill();
  ellipseMode(CENTER);

  //white part/sclera
  fill(252);
  ellipse(x-35, y-10, 30, 30);
  ellipse(x+35, y-10, 30, 30);

  //black part/iris
  fill(0);
  ellipse(x-35, y-10, 10, 10);
  ellipse(x+35, y-10, 10, 10);

  if (x >= width || x <= 0) {
    xSpeed *= -1;
  }

  if (y >= height || y <= 0) {
    ySpeed *= -1;
  }

  x = x + xSpeed;
  y = y + ySpeed;

  //hair: behind body
  fill(red, green, blue); //hair changes color
  rectMode(CENTER);
  rect(a, b+40, 180, 0.7*b);

  //body shape behind face
  fill(0); //clothes color
  rectMode(CENTER);
  rect(a, 1.5*b, 80, b);

  //face shape
  fill(235, 221, 188);
  ellipse(a, b, 180, 220);

  //static eyes
  //white part/sclera
  fill(252);
  ellipse(a-35, b-10, 30, 30);
  ellipse(a+35, b-10, 30, 30);

  //black part/iris
  fill(0);
  ellipse(a-35, b-10, 10, 10);
  ellipse(a+35, b-10, 10, 10);

  //nose
  fill(209, 156, 50);
  ellipse(a-8, b+20, 6, 6);
  ellipse(a+8, b+20, 6, 6);

  //mouth
  arc(a, b+40, 80, 80, 0, PI, PIE);

  //hair
  //bangs
  fill(red, green, blue); //hair changes color
  arc(a, b-40, 180, 180, PI, 2*PI);
  //sides
  rectMode(CORNER);
  rect(a-90, b-50, 20, 0.7*b);
  rect(a+70, b-50, 20, 0.7*b);

  //arms
  stroke(0);
  strokeWeight(20);
  noFill();
  arc(a, b+100, 300, 180, 0, PI, OPEN);
}

void mousePressed () {
  if (mouseX > 0 && mouseX < width && mouseY > 0 && mouseY < height) {
    red = random(255);
    green = random(255);
    blue = random(255);
  }
}

 

Face assignment

Miko

In my first assignment I really wanted to incorporate some form of animation. I decided to focus on blinking eyes. I used arc() function to draw arcs and changed their start points as well as the endpoints with respect to other parts of the eye. I decided to split the animation into 3 parts: the eyelid, the iris and the pupil. The eyelid and the iris animate at the same intervals, but the iris needed a small adjustment, as the arc length of a circle did not correspond directly to that of an ellipse of the eyelid. The last part, the pupil, comes into play only when the eyelid is almost closed.

float eyelidPosition = 40;
float irisPosition = 40;
float pupilPosition = 5;
float speed = 0.3;
int skinColor = #FEB186;
int mouthColor = #C18E74;

void setup() {
size(640, 360);
background(255);
}

void draw() {
background(255);
drawFace(200, 200);
}

void drawEye(int x, int y) {
//EyeShape
ellipse(x, y, 60, 40);

//Eyelid
fill(skinColor);
arc(x, y, 60, 40, PI, 2*PI);
fill(255);

//Eyelid animation
arc(x, y, 60, eyelidPosition, PI, TWO_PI);
arc(x, y, 40, 40, -irisPosition*PI/80, PI+irisPosition*PI/80);

//Pupil
fill(0);
arc(x, y, 5, 5, -pupilPosition*PI/5, PI+pupilPosition*PI/5);
fill(255);

eyelidPosition = eyelidPosition+speed;
irisPosition = eyelidPosition-3*PI;

if (eyelidPosition < 5) {
pupilPosition = pupilPosition+speed;
}
if (eyelidPosition < 0) {
delay(int(random(7)*100));
eyelidPosition=0;
speed=random(0.1,0.5);
} else if (eyelidPosition > 40) {
delay(int(random(5)*1000));
eyelidPosition=40;
speed=-random(0.1,0.5);
}
if (irisPosition < 0) {
irisPosition=0;
}
if (pupilPosition < 0) {
pupilPosition=0;
}
}

void drawSmile(int x, int y) {
fill(mouthColor);
arc(x, y, 150, 60, 0, PI);
arc(x, y, 150, 20, 0, PI);
fill(255);
}

void drawFace(int x, int y) {
fill(skinColor);
ellipse(x, y, 200, 250);
fill(255);
drawSmile(x, y+50);
drawEye(x-50, y-30);
drawEye(x+50, y-30);
}

 

Nisala and Jana: Final Final Final

Well, this has been quite a journey. Obviously, working together with Nisala was quite a lot of desperate laughing, excited high fives and cries for help that both Jack and Aaron patiently listened to- so big big thank you!

The IM show itself was a lot of fun. We managed to fix most of the bugs/imperfections that we caught during the user testing – we fixed the descriptions, colors, adjusted the mapped values and got an iMac to manage the load of data. Eventually, we decided for two colors on the screen only (red for the shapes that are being adjusted and submitted, and white for the already submitted ones). This helped to increase clarity for the user.

We adjusted the mapped values for volume right at the start of the show, so it would take into account the amount of noise in the room – which was quite significant. The only issue was the light – because we changed our location last minute into the corner behind a curtain (where people could scream privately and shamelessly). Therefore, we wanted a light leading people into the corner, but we did not consider that it would interfere with the readability of the descriptions. Me and Nisala’s ambition to be sustainable and laser-cut cardboard waste for the descriptions was a good step for the environment, however, in a dark corner, with the wrong angle of light, it did not facilitate the best conditions for the desired interaction. So, if installing this next time, this would be the first thing to carefully consider. Together with the height of the stand, since we unintentionally discriminated short people.

People, however, had a lot of fun screaming into the microphone. When they left the space, they were smiling a lot – and giving them the private corner to do so was definitely a good choice as they could loosen up a little more. This was quite a success as our intention was not only to collect data on how people feel at this stressful time of the year but also to relief a little of the stress.

Though to some people, a further explanation had to be given about the meaning of what they have seen on the screen, most people understood the concept. The interaction for most people was smooth – just getting them to actually read the instructions first instead of pressing random buttons was a bit of a challenge (but this is where better light could help us if displayed next time).

Here is the video of the final data that we collected from the show.

Here is our final setup:


Close up of the interface:

Sadly, people did not feel THAT much better to support our efforts financially (of course this was a joke – or wasn’t it?). They reacted to this humorous touch really well.

The Arduino code:

const int ledWHITE = 12;
const int ledRED = 13;
int buttonConfirmArdi = 8;
int buttonHoldArdi = 9;

int buttonConfirmState = 0;
int buttonHoldState = 0;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println("0,0,0,0,0,0,0,0");
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  pinMode(6, INPUT);
  pinMode(8, INPUT);
  pinMode(9, INPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);

}

void loop() {
  if (Serial.available() > 0) {
    char inByte = Serial.read();
    delay(0);
    int sadButton = digitalRead(2);
    int fearButton = digitalRead(3);
    int joyButton = digitalRead(4);
    int angerButton = digitalRead(5);
    int disgustButton = digitalRead(6);

    int potentiometer = analogRead(A0);

    int buttonConfirm = digitalRead(8);
    int buttonHold = digitalRead(9);

    delay(0);
    Serial.print(sadButton);
    Serial.print(',');
    Serial.print(fearButton);
    Serial.print(',');
    Serial.print(joyButton);
    Serial.print(',');
    Serial.print(angerButton);
    Serial.print(',');
    Serial.print(disgustButton);
    Serial.print(',');

    Serial.print(potentiometer);
    Serial.print(',');
    Serial.print(buttonHold);
    Serial.print(',');
    Serial.println(buttonConfirm);


  }

  buttonConfirmState = digitalRead(buttonConfirmArdi);
  buttonHoldState = digitalRead(buttonHoldArdi);

  if (buttonConfirmState == HIGH) {
    // turn LED on:
    digitalWrite(ledRED, HIGH);
  } else {
    // turn LED off:
    digitalWrite(ledRED, LOW);
  }


  if (buttonHoldState == HIGH) {
    // turn LED on:
    digitalWrite(ledWHITE, HIGH);
  } else {
    // turn LED off:
    digitalWrite(ledWHITE, LOW);
  }
}

Processing Code:

//calling the class.

ArrayList<DrawCircle> circles;
ArrayList<DrawSquare> squares;
ArrayList<DrawTriangle> triangles;
ArrayList<DrawLine> lines;
ArrayList<DrawHexagon> hexagons;

//establishing the communication with arduino
import processing.serial.*;
Serial myPort;

//Emotion Buttons

int sadButton=0;
int prevSadButtonState=0;

int fearButton =0;
int prevFearButtonState =0;

int joyButton =0;
int prevJoyButtonState =0;

int angerButton =0;
int prevAngerButtonState =0;

int disgustButton =0;
int prevDisgustButtonState =0;


int potentiometer;


//For Button
int buttonState=0;
int prevButtonState = 0;

//for Held Button
int buttonStateHold = 0;

boolean confirmColor= false;

//Audio stuff, from mic.

import processing.sound.*;
Sound s;


AudioIn audioInput;
Amplitude analyzer;

float volume;

boolean readyToDraw;



void setup() {
  //size(640, 480);
  fullScreen();
  noFill();
  blendMode(ADD);
  //stuff for Arduino.
  printArray(Serial.list());
  String portname=Serial.list()[11];
  println(portname);

  myPort = new Serial(this, portname, 9600);
  myPort.clear();
  myPort.bufferUntil('\n');


  //AUDIO INITIALIZATION
  // Create an Audio input and grab the 1st channel
  audioInput = new AudioIn(this, 0);
  println("here is the input " + audioInput);
  // start the Audio Input
  audioInput.start();

  // create a new Amplitude analyzer
  analyzer = new Amplitude(this);
  // Patch the input to an volume analyzer
  analyzer.input(audioInput);


  //initialize the arrayList

  circles = new ArrayList <DrawCircle>();
  squares = new ArrayList <DrawSquare>();
  hexagons = new ArrayList <DrawHexagon>();
  lines = new ArrayList <DrawLine>();
  triangles = new ArrayList <DrawTriangle>();
}


void draw() {
  //if (confirmColor == false) {
  //    if (buttonState==1) {
  //        confirmColor=true;
  //        fill(255);
  //    } else {
  //      fill(0, 255, 0);
  //    } 
  //  }

  background(0);

  //  fill(20,30); 
  //rect(0,0, width,height);
  //pushStyle();
  //fill(100,50);
  //rect(0,0, height, width);
  //popStyle();

  // Get the overall volume (between 0 and 1.0)
  volume = analyzer.analyze();

  //update and display the circles, squares, etc.
  updateDisplayForLoop();

  //Only draw the shape that corresponds with the button.
  clickEmotionButton();
}




void serialEvent(Serial myPort) {
  String s =myPort.readStringUntil('\n');
  s=trim(s);
  if (s!=null) {
    int values[]=int(split(s, ','));
    sadButton = values[0];
    fearButton = values[1];
    joyButton = values[2];
    angerButton = values[3];
    disgustButton = values[4];
    potentiometer= values[5];
    buttonStateHold = values[6];
    buttonState= values[7];
  }
  println(sadButton, fearButton, joyButton, angerButton, disgustButton, potentiometer, buttonState, buttonStateHold);
  myPort.write(0);
}



void clickEmotionButton() {

  //ONLY DRAW THE CIRCLE IF THE SAD BUTTON PRESSED.
  if (prevSadButtonState==1 && sadButton==0) {

    circles.add(new DrawCircle());

    if (prevButtonState==1 && buttonState==0) {
      fill(0, 0, 255, 40);
      circles.add(new DrawCircle());
    }
  } 


  //ONLY DRAW THE SQUARE IF THE FEARBUTTON PRESSED

  if (prevFearButtonState==1 && fearButton==0) {

    squares.add(new DrawSquare());
    if (prevButtonState==1 && buttonState==0) {
      squares.add(new DrawSquare());
    }
  }

  //ONLY DRAW THE HEXAGON IF THE JOYBUTTON PRESSED
  if (prevJoyButtonState==1 && joyButton==0) {
    hexagons.add(new DrawHexagon());

    if (prevButtonState==1 && buttonState==0) {
      hexagons.add(new DrawHexagon());
    }
  }

  //ONLY DRAW THE LINE IF THE ANGERBUTTON PRESSED
  if (prevAngerButtonState==1 && angerButton==0) {
    lines.add(new DrawLine());

    if (prevButtonState==1 && buttonState==0) {
      lines.add(new DrawLine());
    }
  }


  //ONLY DRAW THE TRIANGLE IF THE DISGUSTBUTTON PRESSED
  if (prevDisgustButtonState==1 && disgustButton==0) {
    triangles.add(new DrawTriangle());

    if (prevButtonState==1 && buttonState==0) {
      triangles.add(new DrawTriangle());
    }
  }


  prevSadButtonState= sadButton;
  prevFearButtonState= fearButton;
  prevJoyButtonState = joyButton;
  prevAngerButtonState = angerButton;
  prevDisgustButtonState = disgustButton;
  prevButtonState = buttonState;
}


void updateDisplayForLoop() {


  for (DrawCircle circle : circles) {
    circle.update(potentiometer, buttonState, buttonStateHold);
    fill(255, 40);
    circle.display();
  }


  for (DrawLine line : lines) {
    line.update(potentiometer, buttonState, buttonStateHold);
    line.display();
  }

  for (DrawSquare square : squares) {
    square.update(potentiometer, buttonState, buttonStateHold);
    square.display();
  }

  for (DrawHexagon hexagon : hexagons) {
    hexagon.update(potentiometer, buttonState, buttonStateHold);
    hexagon.display();
  }


  for (DrawTriangle triangle : triangles) {
    triangle.update(potentiometer, buttonState, buttonStateHold);
    triangle.display();
  }
}

class DrawCircle {

  PVector center;
  float angle;
  float radius;// radius if rotation

  float circleSize;

  float x;
  float y;
  float lineWeight;

  float energyLevel;

  boolean confirm;


  float speedModifier;


  //the center oof the imaginary circle
  int centerX; 
  int centerY;

  int circleColor;
  int circleStrokeColor;


  DrawCircle() {


    centerX= width/2 + width/3; 
    centerY = height/2-height/16;

    center = new PVector(centerX, centerY);
    radius = 300;
    circleSize = 30;
    lineWeight=1;
    speedModifier=120;
    confirm=false;
    circleColor = color(216, 29, 68); 
    circleStrokeColor=color(216, 29, 68);
    ;
  }

  void update(int _potentiometer, int buttonState, int buttonStateHold) {


    //if (confirmVolume==true) {

    //  if(button2State==)
    //}



    if (confirm==false) {
      if (buttonState==1) {
        confirm=true;
        circleColor= color(255, 255, 255, 70);
        circleStrokeColor=color(255);
      }

      radius = map(_potentiometer, 0, 1023, 100, 50);//radiusModifier

      speedModifier = map(_potentiometer, 0, 1023, 150, 5);

      if (buttonStateHold==1) {   //If the button is held, then change these things.
        lineWeight = map(volume, 0, 1, 1, 20);
        circleSize = map(volume, 0, 1, 10, 1000);
      }
    }

    x = center.x + cos(angle)*radius;
    y = center.y + sin(angle)*radius;

    angle += PI/speedModifier; // speedModifier
  }


  void display() {
    fill(circleColor);
    stroke(circleStrokeColor);
    noStroke();
    //strokeWeight(3);
    ellipse(x, y, circleSize, circleSize);
  }
}

class DrawHexagon {

  PVector center;
  float angle;
  float radius;// radius if rotation

  float scaleSize;

  float x;
  float y;
  float lineWeight;

  float energyLevel;

  boolean confirm;


  float speedModifier;


  //the center oof the imaginary circle
  float centerX; 
  float centerY;
  
  int hexagonColor;
  int hexagonStrokeColor;

  DrawHexagon() {


    centerX= width/2 - width/6; 
    centerY = height/2 +height/3.5;

    center = new PVector(centerX, centerY);
    radius = 300;
    scaleSize = 10;
    lineWeight=1;
    speedModifier=120;
    confirm=false;
    hexagonColor = color(216, 29, 68);
    hexagonStrokeColor = color(216, 29, 68);
    
  }

  void update(int _potentiometer, int buttonState, int buttonStateHold) {


    if (confirm==false) {
      if (buttonState==1) {
        confirm=true;
        hexagonColor = color(255, 255, 255, 70);
        hexagonStrokeColor = color(255);
      }

      radius = map(_potentiometer, 0, 1023, 100, 50);//radiusModifier

      speedModifier = map(_potentiometer, 0, 1023, 150, 5);

      if (buttonStateHold==1) {   //If the button is held, then change these things.
        lineWeight = map(volume, 0, 1, 1, 20);
        scaleSize = map(volume, 0, 1, 10, 500);
      }
    }

    x = center.x + cos(angle)*radius;
    y = center.y + sin(angle)*radius;

    angle += PI/speedModifier; // speedModifier
  }


  void display() {
    fill(hexagonColor);
    stroke(hexagonStrokeColor);
    noStroke();
    //strokeWeight(3);


    pushMatrix();
    translate(x, y);
    scale(scaleSize/50);
    beginShape();
    vertex(0 - 44, 0 - sqrt(3) * 44);
    vertex(0 + 44, 0 - sqrt(3) * 44);
    vertex(0 + 2 * 44, 0);
    vertex(0 + 44, 0 + sqrt(3) * 44);
    vertex(0 - 44, 0 + sqrt(3) * 44);
    vertex(0 - 2 * 44, 0);
    endShape(CLOSE);
    popMatrix();
  }
}

class DrawLine {

  PVector center, point1, point2;
  float angle;
  float radius;// radius if rotation

  float circleSize;

  float x1, x2;
  float y1, y2;
  float lineWeight;

  float energyLevel;

  boolean confirm;


  float speedModifier;


  //the center oof the imaginary circle
  float centerX; 
  float centerY;
  
  int lineColor;



  DrawLine() {
    point1 = new PVector(x1, y1);
    point2 = new PVector(x2, y2);

    centerX= width/2 + width/6; 
    centerY = height/2 + height/3.5;

    center = new PVector(centerX, centerY);
    radius = 300;
    circleSize = 10;
    lineWeight=10;
    speedModifier=120;
    confirm=false;
    
    lineColor = color(216, 29, 68);
  }

  void update(int _potentiometer, int buttonState, int buttonStateHold) {

    if (confirm==false) {
      if (buttonState==1) {
        confirm=true;
        lineColor = color(255, 255, 255, 90);
      }

      radius = map(_potentiometer, 0, 1023, 100, 50);//radiusModifier

      speedModifier = map(_potentiometer, 0, 1023, 150, 5);

      if (buttonStateHold==1) {   //If the button is held, then change these things.
        lineWeight = map(volume, 0, 1, 3, 5);
        circleSize = map(volume, 0, 1, 10, 500);
      }
    }






    x1 = center.x + cos(angle)*radius;
    y1 = center.y + sin(angle)*radius;


    angle += PI/speedModifier; // speedModifier
  }


  void display() {
    //fill(lineColor);
    stroke(lineColor);
    //noStroke();
    strokeWeight(5);
    //strokeWeight(5);

    pushMatrix();
    translate(x1, y1);
    scale(circleSize/20);
    line(0, 0, 60, 0);
    popMatrix();
  }
}

class DrawSquare {

  PVector center;
  float angle;
  float radius;// radius if rotation

  float squareSize;

  float x;
  float y;
  float lineWeight;

  float energyLevel;

  boolean confirm;


  float speedModifier;
  
  //the center oof the imaginary circle
  int centerX; 
  int centerY;
  
  
  int squareColor;
  int squareStrokeColor;

  DrawSquare() {

    centerX=width/2- width/3; // the place where the object is drawn changes depending on what we input.
    centerY=height/2-height/16;
    
    center = new PVector(centerX, centerY);
    squareSize = 10;
    lineWeight=1;
    speedModifier=120;
    confirm=false;
    squareColor = color(216, 29, 68);
    squareStrokeColor = color(216, 29, 68);
  }

  void update(int _potentiometer, int buttonState, int buttonStateHold) {


    if (confirm==false) {
      if (buttonState==1) {
        confirm=true;
        squareColor = color(255, 255, 255, 70);
        squareStrokeColor = color(255);
      }

      radius = map(_potentiometer, 0, 1023, 100, 50);//radiusModifier
      speedModifier = map(_potentiometer, 0, 1023, 150, 5);
      
      
      
      if (buttonStateHold==1) {   //If the button is held, then change these things.
      lineWeight = map(volume, 0, 1, 1, 20);

      squareSize = map(volume, 0, 1, 10, 1000);
    }
    }


    x = center.x + cos(angle)*radius;
    y = center.y + sin(angle)*radius;

    angle += PI/speedModifier; // speedModifier
  }


  void display() {
    rectMode(CENTER);
    fill(squareColor);
    stroke(squareStrokeColor);
    noStroke();
    //strokeWeight(3);
    rect(x, y, squareSize, squareSize);
  }
}

class DrawTriangle{

  PVector center;
  float angle;
  float radius;// radius if rotation

  float circleSize;

  float x1, x2, x3;
  float y1, y2, y3;
  float lineWeight;

  float energyLevel;

  boolean confirm;


  float speedModifier;


  //the center oof the imaginary circle
  int centerX; 
  int centerY;
  
  int triangleColor;
  int triangleStrokeColor;

  DrawTriangle() {


    centerX= width/2; 
    centerY = height/2-height/3;

    center = new PVector(centerX, centerY);
    radius = 300;
    circleSize = 10;
    lineWeight=1;
    speedModifier=120;
    confirm=false;
    triangleColor = color(216, 29, 68);
    triangleStrokeColor = color(216, 29, 68);
  }

  void update(int _potentiometer, int buttonState, int buttonStateHold) {


    if (confirm==false) {
      if (buttonState==1) {
        confirm=true;
        triangleColor = color(255, 70);
        triangleStrokeColor = color(255);
      }

      radius = map(_potentiometer, 0, 1023, 100, 50);//radiusModifier

      speedModifier = map(_potentiometer, 0, 1023, 150, 5);

      if (buttonStateHold==1) {   //If the button is held, then change these things.
        lineWeight = map(volume, 0, 1, 1, 20);
        circleSize = map(volume, 0, 1, 10, 500);
      }
    }


    x1 = center.x + cos(angle)*radius;
    y1 = center.y + sin(angle)*radius;
    
    

    angle += PI/speedModifier; // speedModifier
  }


  void display() {
    fill(triangleColor);
    noStroke();
   // stroke(triangleStrokeColor);
    
    //strokeWeight(1);
    
    pushMatrix();
    translate(x1, y1);
    scale(circleSize/50);
    triangle(0, 0, 0, 90, 90, 0);
    popMatrix();
  }
}

 

Final Project

After all the mental breakdowns and the literal physical pain, this semester has come to a close. My final project wanted to raise some sort of awareness on the environment and on the dangers of consumption and greed. Although it did come out a bit more comical than I intended it to,  I think my final project was a success as it made people laugh, have fun and more importantly, think.

These are all the paintings for the interface, done with watercolors:

The box went from this:

To this:

I still think it would’ve looked nicer with wood, but for something DIY-ed I think it looks really good.

This is the code for my final project, never thought I’d say this in my life but I’m glad the code got deleted because I actually learned a lot while working and figuring things out. The funniest thing was attempting to add the sounds to the program last minute, and it actually worked!!!!

import processing.sound.*;
SoundFile beep;
SoundFile happy;
SoundFile scary;

import processing.video.*;
import jp.nyatla.nyar4psg.*;


PImage background;
PImage background2;
PImage tree;
PImage tree2;
PImage bush;
PImage mushroom;
PImage factory;
PImage waterBottle;
PImage TrashCan;
PImage bags;
PImage bear;
PImage dBear;
PImage lily;
PImage lily2;
PImage fish1;
PImage fish2;
PImage fish3;
PImage deadFish1;
PImage deadFish2;
PImage deadFish3;
PImage bird;
PImage bird2;
PImage bird3;
PImage bird4;
PImage deadBirdPic; 
PImage deadBirdPic2;


Movie Apocalypse;


int value = 0;


Capture cam;


MultiMarker nya;


int[] objects = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
int i = objects.length;


boolean b = true;
boolean backgroundB = false;
boolean factoryPic = false;
boolean trashCan = false;
boolean bag = false;
boolean bottle =false;
boolean apocalypse = false;
boolean deadBird = true; 
boolean treesGone = true; 
boolean deadBear = true;
boolean fish = true; 
boolean Badmusic = false;

void setup() {
  //size(1920, 1080, P3D);
  fullScreen(P3D, 2);
  colorMode(RGB, 100);
    String[] cameras = Capture.list();
  //printArray(cameras);
  cam = new Capture(this, cameras[0]); 
  //cam=new Capture(this, 1280,700);
  nya=new MultiMarker(this, 1920, 1080, "camera_para.dat", NyAR4PsgConfig.CONFIG_PSG);
  
  Apocalypse = new Movie(this, "fire.mov");
  
    beep = new SoundFile(this, "beep.wav");
    scary = new SoundFile(this, "thunder.wav");
    happy = new SoundFile(this, "nature.wav"); 
    
  background = loadImage("background.png");
  background2 = loadImage("background2.png");
  
  factory = loadImage("factory4.png");
  TrashCan = loadImage("trashcan.png");
  bags = loadImage("bags.png");
  waterBottle = loadImage("waterBottle.png");
  

  tree = loadImage("tree.png");
  bush = loadImage("bush.png");
  tree2 = loadImage("tree2.png");
  bear = loadImage("bear.png");
  dBear = loadImage("deadBear.png");
  
  bird = loadImage("bird.png");
  bird2 = loadImage("bird2.png");
  bird3 = loadImage("bird3.png");
  bird4 =loadImage ("bird4.png");
  deadBirdPic = loadImage("deadBird.png");
  deadBirdPic2 = loadImage("deadBird2.png");
  
  fish1 = loadImage("fish1.png");
  fish2 = loadImage("fish2.png");
  fish3 = loadImage("fish3.png");
  deadFish1 = loadImage("deadfish1.png");
  deadFish2 = loadImage("deadfish2.png");
  deadFish3 = loadImage("deadfish3.png");
  lily = loadImage("lily1.png");
  lily2 = loadImage("lily2.png");
  
  
  fish1.resize(80,100);
  fish2.resize(80,100);
  fish3.resize(100,80);
  deadFish1.resize(120,80);
  deadFish2.resize(120,80);
  deadFish3.resize(120,80);
  
  tree.resize(600, 750);
  tree2.resize(500,600);
  bush.resize(200,400);
  
  bear.resize(280, 400);
  dBear.resize(450,180);
  
  bird.resize(200, 200);
  bird2.resize(200, 200);
  bird3.resize(200,100);
  bird4.resize(200,200);
  deadBirdPic.resize(200,100);
  deadBirdPic2.resize(300,100);
  factory.resize(350, 550);
  TrashCan.resize(400,600);
  bags.resize(500,400);
  //dBear.resize(,)
  factory.resize(400, 600);
  for (int c = 0; c < i; c++) {
    nya.addNyIdMarker(c, 80);
  }

  cam.start();
  background(background);
  noCursor();
}

void draw() {
  if (cam.available()!=true) {
    return;
  }
  cam.read();
  nya.detect(cam);
  background(background);

  image(Apocalypse, 0, 0);

  //if (b) {
  //  pushStyle();
  //  imageMode(CORNER);
  //  pushMatrix();
  //  //translate(1280/2,700/2);
  //  nya.drawBackground(cam);
  //  popMatrix();
  //  popStyle();
  //}

  //Apocalypse.resize(displayWidth,displayHeight); how do i make the video fullscreen?
  //use some app and rezise and change the Apocalypse video before class on Monday please please please Dhabiaaaa
  
  for (int c = 0; c < i; c++) {
    if ((!nya.isExist(c))) {
      //println(nya.getNyId(c));
      
      //println("returning");
      continue;
    }

    if (c==1) {
      if (!beep.isPlaying() && deadBear==true){
        beep.play();
      }
      else{
        beep.stop();
      }
      //Bear turns to carpet code 
      deadBear = false;
      break;
    }
    
    if (c==2 && treesGone == true) {
       treesGone = false;
      if (!beep.isPlaying()){
        beep.play();
      }
      else{
        beep.stop();
      }   //tree falls code because of table or chair or maybe both????
    }
    
    if (c==3 && trashCan == false) {
      if (!beep.isPlaying()){
        beep.play();
      }
      else{
        beep.stop();
      }
      trashCan = true;
     }
          
    if (c==4&& factoryPic==false) {
      factoryPic = true;
      if (!beep.isPlaying()){
        beep.play();
      }
      else{
        beep.stop();
      }
    }

    if (c==5 && fish == true) {
      if (!beep.isPlaying()){
        beep.play();
      }
      else{
        beep.stop();
      }      //fish
      fish = false;
    }
    
    if (c==6 && deadBird == true) {
      if (!beep.isPlaying()){
        beep.play();
      }
      else{
        beep.stop();
      }      //bird dies 
      deadBird = false;
    }
    
    if (c==7 && bag == false) {
      if (!beep.isPlaying()){
        beep.play();
      }
      else{
        beep.stop();
      }      bag = true;
    }
    
    
    if (c==8 && backgroundB ==false) {
      if (!beep.isPlaying()){
        beep.play();
      }
      else{
        beep.stop();
      }      backgroundB = true;
    }
    
    if (c==9) {
      
    }
    
    if (c==0 && apocalypse == false) {
      if (!beep.isPlaying()){
        beep.play();
      }
      else{
        beep.stop();
      }      //don't forget to rezie the video + add a text that says, real life has no reset button
      Apocalypse.loop();
      apocalypse=true;
    }
    

  }


  
  if (backgroundB){
    background(background2);
    
  }
  
  if (bag && apocalypse == false){
    image(bags, 1100, 250); //fix
  }
  

  
  if (!deadBird && apocalypse==false){
    image(deadBirdPic, 80,780);
    image(deadBirdPic2, 600, 450);
  }
  if (deadBird && apocalypse==false){
  image(bird, 800, 100);
  image(bird2, 100, 50);
  image(bird3, 1000, 50);
  image(bird4, 500, 50);
  }
  if (fish && apocalypse == false){
    image(fish1,700,750);
    image(fish2,500,740);
    image(fish3,900,730);
    image(lily,1000,740);
    image(lily2,800,720);

  }
  if (!fish && apocalypse == false){
    image(deadFish1,700,750);
    image(deadFish2,1100,730);
    image(deadFish3,900,730);
  }
  if (treesGone && apocalypse == false){
  image(tree2,1000,100);    
  image(tree, 1300, 0);
  image(bush,1300,500);
  //Add more trees
  }
  if (deadBear && apocalypse == false){
   image(bear, 250, 600);  
  }

  
  if (trashCan && apocalypse == false){
    image(TrashCan, 1400, 550); //fix

  } 
  
    if (factoryPic) {
    image(factory, 0, 0);
  }
  
    if (!deadBear && apocalypse == false){
    image(dBear,100,500);
  }
  
  if (!apocalypse){
    if (!happy.isPlaying()){
      happy.loop();
    }
  }
  
  if (apocalypse) {
    if (happy.isPlaying()){
      scary.loop();
    if (scary.isPlaying()){
      happy.stop();
    }
      
    }
    image(Apocalypse, 0, 0);
    textSize(80);
    text("Real life has no reset button", 50, 80);
    factoryPic = false;
    treesGone = false; 
    deadBird=true; 

  }
}


void keyPressed() {
  if (keyPressed) {
    if (key == 'b' || key == 'B') {
      b = !b;
    }
  }
}


void movieEvent(Movie m) {
  m.read();
}

 

Final project documentation. Maxim Blinov

Here it goes… 🙂

My final project idea was to get something from an alternate reality. After some brainstorming, I came up with this idea to make this arcade claw game (everyone knows this when this game where it is almost impossible to pull out an item) and make it virtual (a.k.a. on the computer) and make the prize be real. So it turned out to be this:

Description of the software/hardware:
The game itself consists of a big script made in processing which drew the claw machine game (code will be attached in the end) and an arduino script which opened the door and gave the prize. Sounds simple, but it took a lot of effort to do it. The claw was controlled with a joystick and the goal of the game was to drop an item off screen.

Expectation/evaluation:
The expectation was to get an item from the alternate reality and it worked out pretty well 🙂 The prizes were some haribos and chocolate chip cookies

Code:
Arduino code:

#include <Servo.h>;
int left = 0;
int right = 0;
int servoPin1 = 3;
int servoPin2 = 4;
const int inX = A1;
const int pressed = 6;
int xValue = 0;
int notPressed = 0;
Servo Servo1;
Servo Servo2;
void setup() {
  pinMode(inX, INPUT); // setup y input
  pinMode(pressed, INPUT_PULLUP); // we use a pullup-resistor for the button functionality
  Servo1.attach(servoPin1);
  Servo2.attach(servoPin2);
  Serial.begin(9600);
  Serial.println("1,1");
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
}

void loop() {

  xValue = analogRead(inX);
  notPressed = digitalRead(pressed);
  Serial.print(xValue);
  Serial.print(",");
  Serial.println(notPressed);
  while (Serial.available()) {

    right = Serial.parseInt();
    left = Serial.parseInt();

    if (Serial.read() == '\n') {
      Servo1.write(right);
      Servo2.write(left);
    }


  }

}

    Processing code:
main:

import shiffman.box2d.*;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.joints.*;
import org.jbox2d.collision.shapes.*;
import org.jbox2d.collision.shapes.Shape;
import org.jbox2d.common.*;
import org.jbox2d.dynamics.*;
import org.jbox2d.dynamics.contacts.*;
import processing.serial.*;

Box2DProcessing box2d;
Claw claw;
bounds bounds;
movement move;
Serial myPort;
float moveX = 0;
float moveY = 0;
int directionX = 0;
int directionY = 0;
float gravity = 1;
float ellipseX = 200;
float ellipseX1 = 600;
float ellipseY = 400;
float ellipseY1 = 400;
int s;
int current = 0;
boolean time = false;
boolean pickup = false;
boolean attach = false;
boolean attach1 = false;
boolean ball1_out = true;
boolean ball2_out = true;
PImage img1,img2;

void setup() {
  //size(1280,1024);
  fullScreen();
  smooth();
  imageMode(CENTER);
  claw = new Claw();
  bounds = new bounds();
  move = new movement();
  printArray(Serial.list());
  img1 = loadImage("/home/neonovi/Desktop/cookie.png");
  img2 = loadImage("/home/neonovi/Desktop/snake.png");
  String portname=Serial.list()[32];
  println(portname);
  myPort = new Serial(this, portname, 9600);
  myPort.clear();
  myPort.bufferUntil('\n');
  myPort.write("1,1");
}


void keyPressed() {
  pickup=false;
  if (key == 'd' && moveY==0)
    directionX = 5;
  else if (key == 'a' && moveY==0)
    directionX = -5;
  else if (keyCode == 32) {
    attach = false;
    attach1 = false;
    if (moveY < height-300)
      directionY=3;
    claw.add = 0.01;
  }
}


void keyReleased() {
  directionY=-3;
  directionX=0;
  claw.add=-0.01;
}


void draw() {
  background(255);
  move.display();
  claw.display(moveX, 150+moveY); 
  fill(0);
  if (ellipseY<height-76 && !attach || ellipseX > width-150 && !attach)
    ellipseY+=gravity;
  if (ellipseY1<height-76 && !attach1 || ellipseX1 > width-150 && !attach1)
    ellipseY1+=gravity;
  image(img1,ellipseX, ellipseY, 100, 80);
  image(img2, ellipseX1, ellipseY1, 100,80);
  if (ellipseY >= height)
    ball1_out = false;
  if (ellipseY1 >= height)
    ball2_out = false;
  bounds.display();
  //image(img1,0,0,100,80);
  myPort.write(135*int(ball1_out)+","+135*int(!ball2_out)+"\n");
  println("ellipseY = "+ellipseY+" | moveY = "+moveY);//135*int(!ball1_out)+","+135*int(ball2_out)+"\n");
  println("ellipseX = "+ellipseX+" | moveX = "+moveX);
}

boundries:

class bounds{
 
void display(){
  rect(0, height-50, width-200, 50);
  rect(width-200, height-100, 50, 100); 
}
}

claw:

class Claw {
  float x=0;
  float y=0;
  float z=0;
  float add=0;
  Claw() {
  }

  void display(float x, float y) {
    fill(255);
    //ellipse(x+5, y+50, 100, 100);
    fill(0);
    rect(x, -1024+y, 10, 1024);
    pushMatrix();
    translate(x, y-5);
    rotate(z%TWO_PI);
    beginShape();
    vertex(0, 0);
    vertex(10, 0);
    vertex(-30, 50);
    vertex(0, 100);
    vertex(-50, 50);
    endShape();
    popMatrix();
    pushMatrix();
    translate(x+10, y-5);
    rotate(-z%TWO_PI);
    beginShape();
    vertex(0, 0);
    vertex(0-10, 0);
    vertex(30, 50);
    vertex(0, 100);
    vertex(50, 50);
    endShape();
    popMatrix();
    if (z<0.6 && add>0)
      z+=add;
    if (z>0 && add<0)
      z+=add;
  }
}

movement:

class movement {



  void display() {
    if (moveY<=height-301)
      moveY += directionY;
    moveX += directionX;
    if (moveY<0) {
      directionY=0;
      moveY=0;
    }
      if (moveY>height-300) {
    directionY=0;
    if (!time) {
      current = millis();
      time = true;
    }
    if (millis()-current>1500) {
      time=false;
      pickup=true;
    }
  }
  if (pickup) {

    if (moveY > 0) {
      moveY-=2;
      if (moveX >= ellipseX-25 && moveX<=ellipseX+25 && (ellipseY-moveY)<230) {
        attach = true;
        gravity=0;
        ellipseY = moveY+220;
      }
      else if (moveX >= ellipseX1-25 && moveX<=ellipseX1+25 && (ellipseY1-moveY)<230) {
        attach1 = true;
        gravity=0;
        ellipseY1 = moveY+220;
      }
    } else
      moveY=0;
  }
  gravity+=0.1;
  if (moveX>1280) {
    moveX = 0;
  }
  if (moveX<0) {
    moveX = 1280;
  }
  if (attach) {
    ellipseX=moveX+5;
    gravity=0;
    ellipseY=moveY+220;
  }
  else if (attach1) {
    ellipseX1=moveX+5;
    gravity=0;
    ellipseY1=moveY+220;
  }
  else
  gravity = 5;
  }
}

 

 

 

 

Final Project Showcase

For my final project, I wanted to create an interactive museum exhibit from the distant future, that describes and showcases our current technology in the 2010s. I specifically chose to talk about the iPhone and its uses as well as the culture around our smartphone devices and apps such as Snapchat.

To do this, I had two parts. The first was a pedestal with the only remaining artifact from Apple in the future, an  iPhone:

For this part, I used a servo motor attached with a finger made of cork and a cotton bud that is connected to electricity to show how people “used to” have to physically control their devices using their fingers. At first, I hoped that the motor would be triggered by people’s movement in front of an IR sensor, but seeing as the sensor could not always detect the range of distance accurately and there was a lot of movement in front of it, I chose to have the motor rotate continuously instead. Here’s the simple Arduino code I used for that:

#include "Servo.h"

float distance = 0;

int pos = 0;
 
Servo myservo;
 
void setup()
{
  Serial.begin (9600);

  myservo.attach(10);
 
}
 
void loop() {
  //read distance
  distance = analogRead(A0);
  Serial.println("someone there");
 
  if (distance > 600) {  //close distance
    for (pos = 0; pos <= 100; pos += 1) {
// goes from 0 degrees to 180 degrees // in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo to reach the position
}
for (pos = 100; pos >= 0; pos -= 1) {
// goes from 180 degrees to 0 degrees
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo to reach the position
}
  }
}

It was also connected to a Kinect device, which played a soundtrack where I gave a “tour” of the exhibition. The soundtrack played depending on how close the viewer is to the exhibit. However, this was one of the challenges that I faced during the showcase, because the space was very loud and even with speakers, the soundtrack was not loud enough. I replaced the speakers with headphones, but ideally, I would have liked them to play based on the viewer’s motion within the space. I had two separate soundtracks for each part of the exhibit, and using maximum and minimum depth thresholds as well as if statements to determine the distance, the code worked in a way that it controlled which soundtrack played based on where people were standing:

// Credits: Daniel Shiffman, Depth thresholding example

import org.openkinect.freenect.*;
import org.openkinect.processing.*;
import processing.sound.*;

Kinect kinect;

SoundFile soundfile;
SoundFile soundfile2;
// Depth image
PImage depthImg;

//soundtrack playing
boolean playing = false;

// pixels to be shown
//int minDepth =  100;
//int maxDepth = 900;


int maxDepth = 950;
int minDepth = 60;

// kinect's angle
float angle;
float L;

void setup() {
  size(1280, 480);

  kinect = new Kinect(this);
  kinect.initDepth();
  angle = kinect.getTilt();

  // blank image showing everything
  depthImg = new PImage(kinect.width, kinect.height);

  //load soundtrack
  soundfile = new SoundFile(this, "iPhone.aif");
  soundfile2 = new SoundFile (this, "Snapchat.aif"); 
  //println("Duration= " + soundfile.duration() + " seconds");
  //L=soundfile.duration();
  // for (int i = 0; i < files.length; i++) {
  //  files[i] = new SoundFile(this, (i+1) + ".aif");
  //}
}

void draw() {
  // draw the raw image
  image(kinect.getDepthImage(), 0, 0);

  // threshold the depth image
  int[] depth = kinect.getRawDepth();
  int counter =0;
  int left_counter =0, right_counter = 0;
  for (int x = 0; x < kinect.width; x++) {
    for (int y = 0; y < kinect.height; y++) {

      int offset =  x + y*kinect.width;
      // Grabbing the raw depth
      int rawDepth = depth[offset];

      if (rawDepth >= minDepth && rawDepth <= maxDepth && y<345  && y >170) {
        depthImg.pixels[offset] = color(255);
        
        if (x<240){

          right_counter++;
        }
        if (x>260 && x<500){

          left_counter++;
        }
      } else {
        depthImg.pixels[offset] = color(0);
      }
    }
  }
  //println(mouseX);
 
  //if (left_counter > 3600 || right_counter > 3600){

  //  if (!playing)
  //  {
  //    soundfile.play();
  //    playing = true;
  //  }
  //}

  //if (left_counter <= 3600 && right_counter <= 3600)
  //{
  //  if (playing)
  //  {
  //    soundfile.stop();
  //    playing = false;
  //  }
  //}
  if (left_counter > 3000)
  {
    if (!playing)
    {
      soundfile.play();
      playing = true;
    }
  } else
  {
    if (playing)
    {
      soundfile.stop();
      playing = false;
    }
  }
  
  //if (left_counter > 3000)
  //{
  //  if (!playing)
  //  {
  //    soundfile2.play();
  //    playing2 = true;
  //  }
  //} else
  //{
  //  if (playing)
  //  {
  //    soundfile.stop();
  //    playing = false;
  //  }
  //}

  // Draw the thresholded image
  depthImg.updatePixels();
  image(depthImg, kinect.width, 0);

  fill(0);
  text("TILT: " + angle, 10, 20);
  text("THRESHOLD: [" + minDepth + ", " + maxDepth + "]", 10, 36);
}

For the second part of the exhibit, I wanted to create some form of a Snapchat “simulator”, where people in the future could learn about one of the first ways in which facial recognition was used in a social media setting, as well as its influences in popular culture and so on. I had to try several examples and libraries on Processing, one of which used FaceOSC:

Here’s the initial code I tried working with on Processing:

// Face It
// Daniel Shiffman
// FaceOSC Example
// Adapted from Greg Borenstein: https://gist.github.com/atduskgreg/1603230

// Use with: https://github.com/downloads/kylemcdonald/ofxFaceTracker/FaceOSC.zip

import oscP5.*;
OscP5 oscP5;

import processing.video.*;

Capture cam;

PVector posePosition;
PVector poseOrientation;

boolean found;
float eyeLeftHeight;
float eyeRightHeight;
float mouthHeight;
float mouthWidth;
float nostrilHeight;
float leftEyebrowHeight;
float rightEyebrowHeight;

float poseScale;

PImage img;
PImage img2;


void setup() {
  size(640, 480);
  frameRate(30);

  img = loadImage("nose.png");
  img2 = loadImage("tongue.png");


  String[] cameras = Capture.list();

  if (cameras == null) {
    println("Failed to retrieve the list of available cameras, will try the default...");
    cam = new Capture(this, 640, 480);
  } 
  if (cameras.length == 0) {
    println("There are no cameras available for capture.");
    exit();
  } else {
    println("Available cameras:");
    printArray(cameras);

    // The camera can be initialized directly using an element
    // from the array returned by list():
    cam = new Capture(this, cameras[0]);
    // Or, the settings can be defined based on the text in the list
    //cam = new Capture(this, 640, 480, "Built-in iSight", 30);

    // Start capturing the images from the camera
    cam.start();
  }

  posePosition = new PVector();
  poseOrientation = new PVector();

  oscP5 = new OscP5(this, 8338);
  oscP5.plug(this, "mouthWidthReceived", "/gesture/mouth/width");
  oscP5.plug(this, "mouthHeightReceived", "/gesture/mouth/height");
  oscP5.plug(this, "eyebrowLeftReceived", "/gesture/eyebrow/left");
  oscP5.plug(this, "eyebrowRightReceived", "/gesture/eyebrow/right");
  oscP5.plug(this, "eyeLeftReceived", "/gesture/eye/left");
  oscP5.plug(this, "eyeRightReceived", "/gesture/eye/right");
  oscP5.plug(this, "jawReceived", "/gesture/jaw");
  oscP5.plug(this, "nostrilsReceived", "/gesture/nostrils");
  oscP5.plug(this, "found", "/found");
  oscP5.plug(this, "poseOrientation", "/pose/orientation");
  oscP5.plug(this, "posePosition", "/pose/position");
  oscP5.plug(this, "poseScale", "/pose/scale");
}


void draw() {
  background(0);
  stroke(0);
  if (cam.available() == true) {
    cam.read();
  }
  imageMode(CORNER);
  image(cam, 0, 0);
  println(poseScale);
  if (found) {
    translate(posePosition.x, posePosition.y);
    //translate(width/2, height/2);
    scale(poseScale*.5, poseScale*.5);



    noFill();

    //ellipse(0,0, 3,3);
    //stroke (250);
    //rect(-20, eyeLeftHeight * -9, 20, 7);
    //rect(20, eyeRightHeight * -9, 20, 7);

    imageMode(CENTER);
    //image(img, 0, nostrilHeight * -1, 7, 3);
    image(img, 0, nostrilHeight * -1, 40, 30);

    //image(img2, 0, 20, mouthWidth* 3, mouthHeight * 3);
    image(img2, 0, 20, 30, 30);
    //image (img, 5, nostrilHeight * -1, 7, 3);
    //rectMode(CENTER);
    ////fill(0);
    //rect(-20, leftEyebrowHeight * -5, 25, 5);
    //rect(20, rightEyebrowHeight * -5, 25, 5);
  }
}

public void mouthWidthReceived(float w) {
  //println("mouth Width: " + w);
  mouthWidth = w;
}

public void mouthHeightReceived(float h) {
  //println("mouth height: " + h);
  mouthHeight = h;
}

//public void eyebrowLeftReceived(float h) {
//  //println("eyebrow left: " + h);
//  leftEyebrowHeight = h;
//}

//public void eyebrowRightReceived(float h) {
//  //println("eyebrow right: " + h);
//  rightEyebrowHeight = h;
//}

//public void eyeLeftReceived(float h) {
//  //println("eye left: " + h);
//  eyeLeftHeight = h;
//}

//public void eyeRightReceived(float h) {
//  //println("eye right: " + h);
//  eyeRightHeight = h;
//}

public void jawReceived(float h) {
  //println("jaw: " + h);
}

//public void nostrilsReceived(float h) {
//  //println("nostrils: " + h);
//  nostrilHeight = h;
//}

public void found(int i) {
  //println("found: " + i); // 1 == found, 0 == not found
  found = i == 1;
}

public void posePosition(float x, float y) {
  //println("pose position\tX: " + x + " Y: " + y );
  posePosition.x = x;
  posePosition.y = y;
}

public void poseScale(float s) {
  //println("scale: " + s);
  poseScale = s;
}

public void poseOrientation(float x, float y, float z) {
  //println("pose orientation\tX: " + x + " Y: " + y + " Z: " + z);
  poseOrientation.x = x;
  poseOrientation.y = y;
  poseOrientation.z = z;
}


void oscEvent(OscMessage theOscMessage) {
  if (theOscMessage.isPlugged()==false) {
    //println("UNPLUGGED: " + theOscMessage);
  }
}

But seeing as face detection was really slow using FaceOSC and Processing, Aaron suggested I use one of the demos from a collection of code written using Jeeliz, a “Javascript/WebGL lightweight face tracking library designed for augmented reality webcam filters.”

Here’s the dog filter demo I used:

Here’s a link to the code I used:

https://github.com/jeeliz/jeelizFaceFilter/tree/master/demos/threejs/dog_face

 

 

Kyle + Tori | Final Blog

*victorious trumpet sounds* Wow! We made it. Because this was such a massive project, this is going to be a pretty long post, so buckle up and get ready to learn about how we created the Interactive Dartboard/Hangman Game…HangDarts!!!

During our brainstorm session we agreed on a couple of things right away.

  1. We want our finished product to incorporate both physical and digital elements (meaning we would somehow create an input using Arduino, then send it into Processing to make something happen).
  2. We want the interaction to be highly physical and almost visceral in nature. This would serve to draw participants into the interaction.
  3. We wanted to find a way to make it more meaningful to each individual user than something that just displayed shapes on a screen. In other words, we wanted to create some sort of game.

After some deliberation, we decided that the action of a person playing game of darts would be a great way to incorporate a physical interaction. After discussion about how to make a dartboard that was able to create a digital output, we decided that the most “genuine” way to create a dartboard was to incorporate several switches into the board itself. Each panel of the board would need to be covered in a permeable conductive material, so that when a conductive dart pierced it, it could connect the two layers, turning on the switch.  

Having the project roughly planned like this was very encouraging. We seemed to have a really clear idea of what we wanted to do and it seemed like we weren’t taking on too much. Actually, there was no point in the process did we think that we were taking on too much for our given two-week time frame.

The problem with making an interactive dartboard was trying to create something more meaningful than the board itself. We needed to add another layer of meaning to our product. After discussing what we thought the board could actually do, we settled on making each panel of our board represent a different letter, then using those letters to play a game of hangman. At first, we planned on making a panel for each of the 26 letters, but realized that each letter would not be used the same amount. We also wanted to minimize the risk of failure by limiting the number of switches we would need to create. Eventually, he design that we settled on was a 1 meter wide grid split into ten panels. The top five panels would be associated with the letters: a, e, i, o, u, as vowels are the first letters you guess in a game of hangman. On the bottom row, we had sets of several consonants (bcdf, ghjk, lmnp, qrst, and vwxyz) in order to both condense our board, and make the game play slightly different from a traditional game of hangman.

The grouping of the consonants also helped to make the game both shorter and slightly easier for the people playing, considering that one of the hard parts of the game would already be successfully hitting each target.  During user testing at the IM showcase our theory was supported as we found that participants began to use statistics to try to win the game (“If I hit the bcdf panel, I can make three words, and if I hit the lmnp panel I can only make one word so I’m going for the bcdf”). This was really interesting to see, not only because it was unexpected, but it also added an interesting cerebral aspect to the game. It made people think. This also made it fun for others to watch the participant play.

Once we had the design completed, we split up in order to actually make it (even though we worked at the same time in the same room). Tori began to work on coding the actual interface of the game, which would be displayed above the dartboard, while Kyle began working on the physical dartboard itself. Thus fed into our strengths and led to us having a stronger project overall. Because we were working next to each other, we could consult the other if we had a problem. After getting to a point where we could begin to incorporate our two parts together, we began working more closely on each other’s parts.

Our prototype was one of the main aspects of this project that gave us hope to create the full product. After testing out a dozen or so different combinations of materials, we decided that the most durable, reliable, and available solution would be to create the switches by gluing a piece of conductive fabric to each side of a piece of foam board. Each cell is individually connected to a breadboard where it serves as a switch: if there is a dart connecting the two sides, it would read as ON, and it it was removed, it would read as OFF. In addition to the actual switch, We had to design effective housing, to minimize the size and weight of the board, while also supporting the grid structure and effectively separate each switch, so that we wouldn’t get any errors. In addition, we needed to make it sturdy enough to withstand being hit by a stray dart.

In addition to the board prototype, We made a small button prototype with a set of 10 labeled buttons in order to start building the code that would send data over from arduino into the Processing program. Ideally, using this we would have been able to build the code and then just plug the board in once it was finished.

In order to bring our prototype to full scale, Kyle spent a lot of time at the laser cutter (booking 9 cutting sessions in total, because of errors with the cutter, and working around limited acrylic supplies). The base of the board was made out of two large pieces of 6 mm thick acrylic, which were bolted together with foam board and thinner acrylic to provide protection for the panels.

Once everything was cut, We took about two full tables in the IM lab with all of the fabric and foam spread out and just spent an hour or two drawing an outline, cutting, and wiring up all of the panels. Once everything was assembled, we went to activate everything, we found our first mistake: three of the wires connecting the panels to the arduino were too short. Luckily this was a quick fix. We just had to solder a little more wire onto what was already there. Last minute, we also added a small wooden board which we glued the arduino to, and a support arm, to make the entire system more maneuverable. We worked around this well, moving the angle of the arm so that Tori could solder the wires while Kyle worked on hand-drawing the letters onto the panels.

Now, back to the code! This bitch was a whole lot to write (about 300 lines in total, including classes and functions). Luckily though, most of it was logical. The basics of what we needed was: Recognize when the switch is turned on, check to see if the randomized word contains that letter. If it does, write the letter, if it doesn’t, draw the stick figure. Then there would be a reset button that clears the drawn stick figure and re-randomizes the word.

Up until then, Tori had handled the code rather well. However, there were a few things that just wouldn’t work. Luckily, Jack helped Tori figure that out. One such was the communication between Arduino and Processing.

Tori’s Comments on writing the code:

However, for the life of me, I could not figure out how to get my processing sketch to communicate with the arduino. And then Jack came in and fixed it. My issue? In a moment of complete student stress and exhaustion, we had began counting with 1 instead of 0. Cue an outraged cry of “THAT WAS IT???” as Jack pointed it out, we changed it, and it instantly worked. So. Frustrating.

He also helped me at the very end when I couldn’t figure out how to put in a reset button, which was another very easy fix. “Tori you’re calling this class to do all of the stuff. Just call the class again every time you push the button”. “Oooooohhhh”. Overall, Jack is a lifesaver and I thank him heavily for his help with this project.

At this point, I’m going to put the code.

Processing:

import processing.serial.*;    
Serial myPort;

int buttonA;
int buttonE;
int buttonI;
int buttonO;
int buttonU;
int buttonBCDF;
int buttonGHJK;
int buttonLMNP;
int buttonQRST;
int buttonVWXYZ;

//int numWords = 17;
//int number = int(random(0, numWords)); //to add the randomize word right into the reset button. 

Text text = new Text();

PFont f;
PImage paper;    //background for hangman
PImage pin;      //detail
PImage hangman;  //hangman drawing
PImage sticky;    //aesthetic sticky notes
PImage cork;     //cork background
PImage dart;    // for animation of the wrong letters
PImage sadBoi; //sadBoi hours
PImage torn; //gameover 

boolean reset = false;  //for reset button

int values[]= new int[10];

void setup() {
  fullScreen();
  f = createFont("Comic Sans MS Bold Italic", 50);

  printArray(Serial.list());          //setup for send to Ardy
  String portname=Serial.list()[0];
  println(portname);
  myPort = new Serial(this, portname, 9600);
  myPort.clear();
  myPort.bufferUntil('\n');

  cork = loadImage("cork.jpg");
  paper = loadImage("paper.png");
  pin = loadImage("pin.png");
  hangman = loadImage("hangman.gif");
  sticky = loadImage("sticky.png");

  textFont(f);
}

void draw() {
  if (reset == true) {  //setting the fail and pass counters back to 0 for reset button
    image(cork, 0, 0, width, height);    //draw the basic background
    image(paper, 0, 0, 1000, 1000);
    image(pin, 440, 100, 80, 70);
    image(hangman, 170, 200, 600, 600);
    image(sticky, 1300, 70, 600, 400);

    text.display();
    //text.fail = 0;
    //text.pass = 0;

    alphabet();
    text.run(values);
  }    //connected to line 64
}

void serialEvent(Serial myPort) {      //send to Ardy

  String s=myPort.readStringUntil('\n');
  s=trim(s);
  if (s!=null) {
    //println(s);    //to check the connections
    values = int (split(s, ','));    //I don't think we need this but whatever?
    if (values.length == 10) {
      buttonA = values[0];
      buttonE = values[1];
      buttonI = values[2];
      buttonO = values[3];
      buttonU = values[4];
      buttonBCDF = values[5];
      buttonGHJK = values[6];
      buttonLMNP = values[7];
      buttonQRST = values[8];
      buttonVWXYZ = values[9];
    }
  }

  myPort.write(buttonA);
}

void keyPressed() {   //assign the reset button to the space bar
  if (key == ' ') {
    reset = true;
     text = new Text();
  }
}
class Text {
  boolean textAppearA = false;    //so many booleans. there's gotta be a better way to do this. 
  boolean textAppearE = false;
  boolean textAppearI = false;
  boolean textAppearO = false;
  boolean textAppearU = false;
  boolean textAppearB = false;
  boolean textAppearG = false;
  boolean textAppearL = false;
  boolean textAppearQ = false;
  boolean textAppearV = false;

  int fail = 0;    //for drawing the stick figure based on errors
  int pass = 0;    //for "You win" 

  int numWords = 54;  //how many words in the doc, could use message.length but I don't feel like it. 
  int number = floor(random(0, numWords));    //randomize the words

  Text() {
  }

  void display() {
    for (int i = 0; i < 4; i++) {
      image(sticky, 850+(250*i), 600, 250, 250);
    }
  }

  void run(int[] _values) {
    sadBoi = loadImage("sad.png");
    dart = loadImage("dart.png"); 
    torn = loadImage("torn.png");

    String[] message = loadStrings("words.txt");
    fill(0);  //setting the font color to black


    if (_values[0] == 1) {
      image(dart, 1255, 30, 150, 150);
      if (textAppearA == false) {

        if ((message[number].charAt(0) != 'a') && (message[number].charAt(1) != 'a') && (message[number].charAt(2) != 'a') && (message[number].charAt(3) != 'a')) {
          fail++;
        }
        if ((message[number].charAt(0) == 'a') || (message[number].charAt(1) == 'a') || (message[number].charAt(2) == 'a') || (message[number].charAt(3) == 'a')) {
          pass++;
        }
        textAppearA = true;
      }
    }

    if (textAppearA == true) {
      for (int s = 0; s < 4; s++) {
        if (message[number].charAt(s) == 'a') {
          text(message[number].charAt(s), 955 + (250*s), 730);
        }
      }
    }

    if (_values[1] == 1) {
      image(dart, 1355, 30, 150, 150);

      if (textAppearE == false) {      //NOTE: MAKE ALL OF THIS INTO A FUNCTION SO THE CODE ISN'T AS LONG?


        if ((message[number].charAt(0) != 'e') && (message[number].charAt(1) != 'e') && (message[number].charAt(2) != 'e') && (message[number].charAt(3) != 'e')) {
          fail++;
        }
        if ((message[number].charAt(0) == 'e') || (message[number].charAt(1) == 'e') || (message[number].charAt(2) == 'e') || (message[number].charAt(3) == 'e')) {
          pass++;
        }

        textAppearE = true;
      }
    }

    if (textAppearE==true) {
      for (int s = 0; s < 4; s++) {
        if (message[number].charAt(s) == 'e') {
          text(message[number].charAt(s), 955 + (250*s), 730);
        }
      }
    }
    if ( _values[2] ==1) {
      image(dart, 1455, 30, 150, 150);

      if (textAppearI == false) {  

        if ((message[number].charAt(0) != 'i') && (message[number].charAt(1) != 'i') && (message[number].charAt(2) != 'i') && (message[number].charAt(3) != 'i')) {
          fail++;
        }
        if ((message[number].charAt(0) == 'i') && (message[number].charAt(1) == 'i') || (message[number].charAt(2) == 'i') || (message[number].charAt(3) == 'i')) {
          pass++;
        }

        textAppearI = true;
      }
    }

    if (textAppearI==true) {
      for (int s = 0; s < 4; s++) {
        if (message[number].charAt(s) == 'i') {
          text(message[number].charAt(s), 955 + (250*s), 730);
        }
      }
    }
    if (_values[3] == 1) {
      image(dart, 1555, 30, 150, 150);
      if (textAppearO == false) {     

        if ((message[number].charAt(0) != 'o') && (message[number].charAt(1) != 'o') && (message[number].charAt(2) != 'o') && (message[number].charAt(3) != 'o')) {
          fail++;
        }
        if ((message[number].charAt(0) == 'o') || (message[number].charAt(1) == 'o') || (message[number].charAt(2) == 'o') || (message[number].charAt(3) == 'o')) {
          pass++;
        }
        textAppearO = true;
      }
    }

    if (textAppearO==true) {
      for (int s = 0; s < 4; s++) {
        if (message[number].charAt(s) == 'o') {
          text(message[number].charAt(s), 955 + (250*s), 730);
        }
      }
    }

    if  (_values[4] == 1) {
      image(dart, 1655, 30, 150, 150);
      if (textAppearU == false) {  

        if ((message[number].charAt(0) != 'u') && (message[number].charAt(1) != 'u') && (message[number].charAt(2) != 'u') && (message[number].charAt(3) != 'u')) {
          fail++;
        }
        if ((message[number].charAt(0) == 'u') || (message[number].charAt(1) == 'u') || (message[number].charAt(2) == 'u') || (message[number].charAt(3) == 'u')) {
          pass++;
        }
        textAppearU = true;
      }
    }

    if (textAppearU ==true) {
      for (int s = 0; s < 4; s++) {
        if (message[number].charAt(s) == 'u') {
          text(message[number].charAt(s), 955 + (250*s), 730);
        }
      }
    }
    if (_values[5] == 1) {
      image(dart, 1335, 125, 150, 150);

      if (textAppearB == false) { 

        if ((message[number].charAt(0) != 'b') && (message[number].charAt(1) != 'b') && (message[number].charAt(2) != 'b') && (message[number].charAt(3) != 'b')
          && (message[number].charAt(0) != 'c') && (message[number].charAt(1) != 'c') && (message[number].charAt(2) != 'c') && (message[number].charAt(3) != 'c')
          && (message[number].charAt(0) != 'd') && (message[number].charAt(1) != 'd') && (message[number].charAt(2) != 'd') && (message[number].charAt(3) != 'd')
          && (message[number].charAt(0) != 'f') && (message[number].charAt(1) != 'f') && (message[number].charAt(2) != 'f') && (message[number].charAt(3) != 'f')) {
          fail++;
        }

        if ((message[number].charAt(0) == 'b') || (message[number].charAt(1) == 'b') || (message[number].charAt(2) == 'b') || (message[number].charAt(3) == 'b')
          || (message[number].charAt(0) == 'c') || (message[number].charAt(1) == 'c') || (message[number].charAt(2) == 'c') || (message[number].charAt(3) == 'c')
          || (message[number].charAt(0) == 'd') || (message[number].charAt(1) == 'd') || (message[number].charAt(2) == 'd') || (message[number].charAt(3) == 'd')
          || (message[number].charAt(0) == 'f') || (message[number].charAt(1) == 'f') || (message[number].charAt(2) == 'f') || (message[number].charAt(3) == 'f')) {
          pass++;
        }

        textAppearB = true;
      }
    }

    if (textAppearB == true) {
      for (int s = 0; s < 4; s++) {
        if ((message[number].charAt(s) == 'b') || (message[number].charAt(s) == 'c') || (message[number].charAt(s) == 'd') || (message[number].charAt(s) == 'f'))
          text(message[number].charAt(s), 955 + (250*s), 730);
      }
    }
    if (_values[6] == 1) {
      image(dart, 1555, 125, 150, 150);
      if (textAppearG == false) {  

        if ((message[number].charAt(0) != 'g') && (message[number].charAt(1) != 'j') && (message[number].charAt(2) != 'j') && (message[number].charAt(3) != 'j')
          && (message[number].charAt(0) != 'h') && (message[number].charAt(1) != 'h') && (message[number].charAt(2) != 'h') && (message[number].charAt(3) != 'h')
          && (message[number].charAt(0) != 'j') && (message[number].charAt(1) != 'j') && (message[number].charAt(2) != 'j') && (message[number].charAt(3) != 'j')
          && (message[number].charAt(0) != 'k') && (message[number].charAt(1) != 'k') && (message[number].charAt(2) != 'k') && (message[number].charAt(3) != 'k')) {
          fail++;
        }

        if ((message[number].charAt(0) == 'g') || (message[number].charAt(1) == 'j') || (message[number].charAt(2) == 'j') || (message[number].charAt(3) == 'j')
          || (message[number].charAt(0) == 'h') || (message[number].charAt(1) == 'h') || (message[number].charAt(2) == 'h') || (message[number].charAt(3) == 'h')
          || (message[number].charAt(0) == 'j') || (message[number].charAt(1) == 'j') || (message[number].charAt(2) == 'j') || (message[number].charAt(3) == 'j')
          || (message[number].charAt(0) == 'k') || (message[number].charAt(1) == 'k') || (message[number].charAt(2) == 'k') || (message[number].charAt(3) == 'k')) {
          pass++;
        }
        textAppearG = true;
      }
    }
    if (textAppearG == true) {
      for (int s = 0; s < 4; s++) {
        if ((message[number].charAt(s) == 'g') || (message[number].charAt(s) == 'h') || (message[number].charAt(s) == 'j') || (message[number].charAt(s) == 'k'))
          text(message[number].charAt(s), 955 + (250*s), 730);
      }
    }

    if ( _values[7] == 1) {
      image(dart, 1435, 180, 150, 150);
      if (textAppearL == false) {  


        if ((message[number].charAt(0) != 'l') && (message[number].charAt(1) != 'l') && (message[number].charAt(2) != 'l') && (message[number].charAt(3) != 'l')
          && (message[number].charAt(0) != 'm') && (message[number].charAt(1) != 'm') && (message[number].charAt(2) != 'm') && (message[number].charAt(3) != 'm')
          && (message[number].charAt(0) != 'n') && (message[number].charAt(1) != 'n') && (message[number].charAt(2) != 'n') && (message[number].charAt(3) != 'n')
          && (message[number].charAt(0) != 'p') && (message[number].charAt(1) != 'p') && (message[number].charAt(2) != 'p') && (message[number].charAt(3) != 'p')) {
          fail++;
        }

        if ((message[number].charAt(0) == 'l') || (message[number].charAt(1) == 'l') || (message[number].charAt(2) == 'l') || (message[number].charAt(3) == 'l')
          || (message[number].charAt(0) == 'm') || (message[number].charAt(1) == 'm') || (message[number].charAt(2) == 'm') || (message[number].charAt(3) == 'm')
          || (message[number].charAt(0) == 'n') || (message[number].charAt(1) == 'n') || (message[number].charAt(2) == 'n') || (message[number].charAt(3) == 'n')
          || (message[number].charAt(0) == 'p') || (message[number].charAt(1) == 'p') || (message[number].charAt(2) == 'p') || (message[number].charAt(3) == 'p')) {
          pass++;
        }

        textAppearL = true;
      }
    }

    if (textAppearL == true) {
      for (int s = 0; s < 4; s++) {
        if ((message[number].charAt(s) == 'l') || (message[number].charAt(s) == 'm') || (message[number].charAt(s) == 'n') || (message[number].charAt(s) == 'p'))
          text(message[number].charAt(s), 955 + (250*s), 730);
      }
    }

    if (_values[8] == 1) {
      image(dart, 1335, 250, 150, 150);
      if (textAppearQ == false) {  


        if ((message[number].charAt(0) != 'q') && (message[number].charAt(1) != 'q') && (message[number].charAt(2) != 'q') && (message[number].charAt(3) != 'q')
          && (message[number].charAt(0) != 'r') && (message[number].charAt(1) != 'r') && (message[number].charAt(2) != 'r') && (message[number].charAt(3) != 'r')
          && (message[number].charAt(0) != 's') && (message[number].charAt(1) != 's') && (message[number].charAt(2) != 's') && (message[number].charAt(3) != 's')
          && (message[number].charAt(0) != 't') && (message[number].charAt(1) != 't') && (message[number].charAt(2) != 't') && (message[number].charAt(3) != 't')) {
          fail++;
        }
        if ((message[number].charAt(0) == 'q') || (message[number].charAt(1) == 'q') || (message[number].charAt(2) == 'q') || (message[number].charAt(3) == 'q')
          || (message[number].charAt(0) == 'r') || (message[number].charAt(1) == 'r') || (message[number].charAt(2) == 'r') || (message[number].charAt(3) == 'r')
          || (message[number].charAt(0) == 's') || (message[number].charAt(1) == 's') || (message[number].charAt(2) == 's') || (message[number].charAt(3) == 's')
          || (message[number].charAt(0) == 't') || (message[number].charAt(1) == 't') || (message[number].charAt(2) == 't') || (message[number].charAt(3) == 't')) {
          pass++;
        }
        textAppearQ = true;
      }
    }

    if (textAppearQ == true) {
      for (int s = 0; s < 4; s++) {
        if ((message[number].charAt(s) == 'q') || (message[number].charAt(s) == 'r') || (message[number].charAt(s) == 's') || (message[number].charAt(s) == 't'))
          text(message[number].charAt(s), 955 + (250*s), 730);
      }
    }

    if (_values[9] ==1) {
      image(dart, 1590, 250, 150, 150);

      if (textAppearV == false) {  


        if ((message[number].charAt(0) != 'v') && (message[number].charAt(1) != 'v') && (message[number].charAt(2) != 'v') && (message[number].charAt(3) != 'v')
          && (message[number].charAt(0) != 'w') && (message[number].charAt(1) != 'w') && (message[number].charAt(2) != 'w') && (message[number].charAt(3) != 'w')
          && (message[number].charAt(0) != 'x') && (message[number].charAt(1) != 'x') && (message[number].charAt(2) != 'x') && (message[number].charAt(3) != 'x')
          && (message[number].charAt(0) != 'y') && (message[number].charAt(1) != 'y') && (message[number].charAt(2) != 'y') && (message[number].charAt(3) != 'y')
          && (message[number].charAt(0) != 'z') && (message[number].charAt(1) != 'z') && (message[number].charAt(2) != 'z') && (message[number].charAt(3) != 'z')) {
          fail++;
        }

        if ((message[number].charAt(0) == 'v') || (message[number].charAt(1) == 'v') || (message[number].charAt(2) == 'v') || (message[number].charAt(3) == 'v')
          || (message[number].charAt(0) == 'w') || (message[number].charAt(1) == 'w') || (message[number].charAt(2) == 'w') || (message[number].charAt(3) == 'w')
          || (message[number].charAt(0) == 'x') || (message[number].charAt(1) == 'x') || (message[number].charAt(2) == 'x') || (message[number].charAt(3) == 'x')
          || (message[number].charAt(0) == 'y') || (message[number].charAt(1) == 'y') || (message[number].charAt(2) == 'y') || (message[number].charAt(3) == 'y')
          || (message[number].charAt(0) == 'z') || (message[number].charAt(1) == 'z') || (message[number].charAt(2) == 'z') || (message[number].charAt(3) == 'z')) {
          pass++;
        }
        textAppearV = true;
      }
    }

    if (textAppearV == true) {
      for (int s = 0; s < 4; s++) {
        if ((message[number].charAt(s) == 'v') || (message[number].charAt(s) == 'w') || (message[number].charAt(s) == 'x') || (message[number].charAt(s) == 'y') || (message[number].charAt(s) == 'z')) {
          text(message[number].charAt(s), 955 + (250*s), 730);
        }
      }
    }

    pushStyle();
    noFill();    //make stick figure "transparent"
    strokeWeight(10);
    if (fail >= 1) {
      ellipse(580, 360, 100, 100);
    }

    if (fail >= 2) { 
      line(580, 420, 580, 580);
    }

    if (fail >= 3) { 
      line(580, 580, 550, 640); 
      line(580, 580, 610, 640);
    }
    if (fail >= 4) { 
      line(580, 440, 550, 520); 
      line(580, 440, 610, 520);
    }

    if (fail >= 4) {   //game over
      pushMatrix();
      pushStyle();
      textSize(90); 
      rotate(-.4);
      fill(255, 0, 0);

      image(torn, 175, height/2+100, 556*1.75, 215*1.75);
      text("GAME OVER", 420, 850);
      popStyle();
      popMatrix();
      for (int s = 0; s < 4; s++) {
        text(message[number].charAt(s), 955 + (250*s), 730);
      }
      image(sadBoi, 550, 327, 60, 60);
      //dartSize = 0;
    }

    if (pass >= 4) { 
      pushMatrix();
      pushStyle();
      textSize(90); 
      rotate(-.4);
      fill(0, 150, 0);
      image(torn, 175, height/2+100, 556*1.75, 215*1.75);
      text("YOU WIN!", 420, 850);
      popStyle();
      popMatrix();
    }

    //if (keyPressed && (key == 'k')) {      //work this into the reset button
    //  number = int(random(0, numWords));
    //}
  }
}
void alphabet() {
  pushStyle();
  fill(0);

  text("a", 1385, 175);
  text("e", 1485, 175);
  text("i", 1585, 175);
  text("o", 1685, 175);
  text("u", 1785, 175);

  text("bcdf", 1420, 255);
  text("ghjk", 1640, 255);
  text("lmnp", 1530, 325);
  text("qrst", 1420, 395);
  text("vwxyz", 1640, 395);
  popStyle();
}

Arduino:

int a = 11;     //pin numbers don't worry about this
int e = 2;
int i = 3;
int o = 4;
int u = 5;
int bcdf = 6;
int ghjk = 7;
int lmnp = 8;
int qrst = 9;
int vwxyz = 10;


void setup() {

  Serial.begin(9600);
  //  Serial.println("0,0,0,0");
  establishContact();
}

void loop() {
  if (Serial.available() > 0) {
  int  inByte = Serial.read();

    int aRead = digitalRead(a);
    int eRead = digitalRead(e);
    int iRead = digitalRead(i);
    int oRead = digitalRead(o);
    int uRead = digitalRead(u);
    int bcdfRead = digitalRead(bcdf);
    int ghjkRead = digitalRead(ghjk);
    int lmnpRead = digitalRead(lmnp);
    int qrstRead = digitalRead(qrst);
    int vwxyzRead = digitalRead(vwxyz);

    Serial.print(aRead);
    Serial.print(',');
    delay(1);
    Serial.print(eRead);
    Serial.print(',');
    delay(1);
    Serial.print(iRead);
    Serial.print(',');
    delay(1);
    Serial.print(oRead);
    Serial.print(',');
    delay(1);
    Serial.print(uRead);
    Serial.print(',');
    delay(1);
    Serial.print(bcdfRead);
    Serial.print(',');
    delay(1);
    Serial.print(ghjkRead);
    Serial.print(',');
    delay(1);
    Serial.print(lmnpRead);
    Serial.print(',');
    delay(1);
    Serial.print(qrstRead);
    Serial.print(',');
    delay(1);
    Serial.println(vwxyzRead);
    //  Serial.println(',');
  }
}

void establishContact() {
  while (Serial.available() <= 0) {
    Serial.println("0,0,0,0,0,0,0,0,0,0");   // send an initial string
    delay(300);
  }
}

Now, comes the time for the IM showcase. The big night. We were nervous, honestly. What if something went wrong? What if it didn’t work? What if the people who user tested it were just being nice? We had done user testing and had so much great feedback…oh right…User testing..

Before we had everything totally done, we asked some friends to participate in some User Testing. Here are the videos.

The users gave us a lot of really solid feedback. For example, one user said it would be cool to try and find a way to make the game multiplayer, have one person enter the word and the other person guess it. While we agreed that would be really cool, we thought it might get too crazy and be unfair if people could just add things that weren’t words.

Also, we learned that it is super fun and really satisfying to play. Watching the drawings come up right when the dart goes into the board really signifies that there is an interaction taking place between the user and the game. We received this feedback from multiple people. Every time people saw one person testing it, others would ask to play as well, even when it wasn’t completely finished.

When we moved into our final showcase space, we got some more user testing and feedback. It was suggested that we shine a spotlight on the board so people can see the letters better. we also decided on a set throwing distance as a way of controlling the space more and making it safer to use in the crowded Showcase space. We also did this because many users tried to throw from too close to the board, which would mean their dart wouldn’t have enough momentum to effectively pierce both layers of the board.

Overall, All of our user testers were really encouraging.

Okay back to the IM showcase. We were nervous as hell. But as soon as the show started, and the public began interacting with our game, it was impossible not to become overcome with a sense of pride. Throughout the entire two-hour-long event, there was nearly always a line of people waiting to play, while others gathered around watching. Bystanders would help to guess the word, and often take pictures and videos of the game as it was played. We believe this was the most delightful surprising discovery of the entire experience. Once the room was full, people began interacting with our board in an entirely new way. It was no longer a game of person vs. board, but of everybody trying to guess the word. People would even switch off who threw darts. The game became so multiplayer without us even trying. One person would throw the darts and four others would yell suggestions for letters. Or two friends would come and throw darts together while their other friends looked on. There was never just one person playing this game. It was incredible to watch so many people have fun with something that we made.

Another thing that we didn’t really think about was the replayability of the game. Throughout the showcase, the same people kept coming back to play again and, bringing their friends with them. Each time they played, they still had fun! Even we, the creators remained entertained the entire night, watching people throw darts over and over.

All in all, it was so much fun and so rewarding to see everyone have such a good experience with our project. Thank you for such a great semester!

Here are some pictures and videos of the IM showcase.