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.

Computer Vision for Artists

I feel like this is the breaking point for me. Computer vision is so cool and I want to use it in absolutely everything I do and that’s a horrible mindset. It’s one of those things that should only be used if it’s absolutely essential to the project. If it can be done in another way, do it in another way. But if it’s what makes the project unique or adds a key element, then do it. As I continue working with IM, it’ll be hard not to suppress the urge to incorporate computer vision into everything.

This reading was very technical, but I think it was a good basis for understanding how it works, what the right conditions are for it to work, and other things that we will need to know for going into the very beginnings of computer vision. For example, setting up the physical environment, especially in this sort of festival/installation vibe we have going for the final show. Making sure that the space is high contrast/low noise and is able to communicate properly with the computer will be an essential part of the process.

However, I really want to talk about an experience I had a little less than a year ago at the Museum of Contemporary Art in Montreal. They had recently installed Zoom Pavillion, an interactive art piece by Rafael Lozano-Hemmer and Krzysztof Krzysztof Wodiczko. In this, all of the walls as well as the ceiling were covered in video footage…of yourself. You would walk into this room and cameras would pick up on your face, gestures, gait, a group that you walked in on. Then, the cameras would start pairing you with people to mark your relationship with them. It was absolutely incredible. I walked in with a group of 18 year olds, all of us extremely good friends, so the cameras drew a large square around the entire group. However, as we started exploring the space, it kept us in communication with each other. It noticed that I kept looking at my good friend Adam, so it would draw a line between us and write “friendly”. I would move far away from my other friend and it would write “hostile”.

This was all fun and games, however, until another group of teenagers walked in. At that point, my friend group had gathered in the corner of the room and were making funny faces into the camera. The other teenagers walked in and almost immediately, we had two boxes, each around the groups and a line between us that read, “Potential Friendly”. How had this machine told us that a bunch of people we could easily connect with had entered the room just by observing a few seconds of their movement and interaction with the space? I will never forget this. It was absolutely incredible.

We’re done and it’s only Friday???`

Whaddup it’s ya boyz tori and kyle.

We did a few things this weekend.

Code: 

Tori made some words in a document and made them randomize on screen. She also figured out how to call specific characters which will be useful for later. Also, we got a dope hangman sketch. Here’s the code and a video:

Text text = new Text();

PImage hangman;
boolean one = false;
boolean two = false;
boolean three = false;
boolean four = false;



void setup() {
  fullScreen();
}


void draw() {
  background(255);
  hangman = loadImage("hangman.gif");
  image(hangman, 100, 100, 1000, 1000);
  noFill();
  strokeWeight(5);


  if (one == true) {
    ellipse(785, 355, 150, 150);
  }
  if (two == true) {
    line(785, 430, 785, 720);
  }
  if (three == true) {
    line(785, 720, 755, 780);
    line(785, 720, 815, 780);
  }
  if (four == true) {
    line(785, 480, 755, 580);
    line(785, 480, 815, 580);
    
    text("GAME OVER", width/2, height/2);
  }
  
  
    text.run();
  }

  void keyPressed() {


    if (key == '1') {
      one = true;
    }
    if (key == '2') {
      two = true;
    }
    if (key == '3') {
      three = true;
    }
    if (key == '4') {
      four = true;
    }
  }

 

class Text {

  float data[];
  int number = int(random(0, 6));
  int size = 60;

  Text() {
  }

  void run() {
 
    String[] message = loadStrings("words.txt");
 
 
    data = float(split(message[number], ","));
    fill(0);
    textSize(size);
    text(message[number], width/2 + 400, 700);
    text(message[number].charAt(3), width/2, height/2);    //now how do I get that to respond to an input...I don't think I can rn without connecting it to an input 
    if (keyPressed && (key == ' ')){
      number = int(random(0, 6));
    }
  }
}

I’m controlling the word randomization using the space bar. The hangman appears using the number keys 1-4 (based on # of errors in the future)

Physical Stuff: 

Kyle focused more on the physical stuff this weekend. He prototyped a panel using conductive copper fabric that we got from the Engineering Design Studio and some felt in between the two layers. He then got some poky pins and made a circuit using his arduino. He ran a bunch of trials to make sure that he could complete the circuit multiple times (upwards of 50-100) without the cicuit breaking or wearing down. We expect to complete more of these tests in the future, especially when we recieve the darts, which we ordered today and should be arriving on Monday or Tuesday.

Here is a picture of the failed attempt with conductive foam. We realized that this was not conductive enough to complete the circuit. 

Here’s a video and some pictures of the final prototype panel!

Cool Cat

Hey I did a thing. It’s pretty cool! Jack helped me discover box() instead of (rect) and how to rotate the image so you can really see the 3D aspect of it.

Here’s the original picture I used:

Here’s the code:

PImage img;
float increment = 5;
float incrementZ = 400;

void setup() {
  size(1200, 720, P3D);
  img = loadImage("pics.jpg");
  rectMode(CENTER);
}

void draw() {
  noStroke();
  background(0);
  rotateY(.2);

  for (int y = 0; y<img.height; y+= increment) {           
    for (int x = 0; x <img.width; x+= increment) { 
      img.loadPixels();
int index = (x + y * img.width);
      color pix = img.pixels[index];
      fill(pix); 
      float diam = map(brightness(pix), 0, 255, 2, incrementZ/2);
      pushMatrix();
      translate(x, y, diam);
      
      box(increment, increment, map(mouseX, 0, width, 0, incrementZ));
      popMatrix();
    
    }
  }
  
 img.updatePixels();
 
 increment = map(mouseY, 0, width, 5, 30);
}

Basically, the increment size changes with the Y axis and it “explodes” on the Z axis based on the mouseX value. Here’s a video to show it off:

Tori and Kyle – Even More Dart Stuff

What’s up y’all we’re back! You know who we is. Our little old interactive dartboard has grown up into a big dartboard who is making its parents (us) proud.

Well, it’s still a concept, so not yet. Hey! Speaking of concepts, let’s get right into it…

Concept

Dartboard Interactive Hangman

  • How will it work?
    • People will throw darts at a square dartboard (about the size of a whiteboard). On the dartboard will be 26 panels (4 rows of 6 with two letters straggling at the bottom).
    • If the letter that the user hits is in the word (that has been randomly generated by our list in Processing), it will appear on screen.
    • if the letter is not in the word, a sad but cool animation will happen to inform the dart-shooter that they have chosen incorrectly. The letter will then be placed in a “Letters Already Chosen” box and a body part of the Hangman will be drawn
    • The “game” is over once they get the word, or the man is fully drawn onscreen. In either case, a super fun animation will happen.
  • What is the Purpose behind this?
    • Kyle and I both grew up playing hangman in our schools. It often brought together unlikely people and strengthen the bonds between friends. Putting this into processing and adding darts into it just extends a childhood game into our “adult” interests and skills and invites people in to form new connections with each other.
    • also darts are cool.
      • they have a sort of physicality to them that will work nicely to support interactivity.

Technical Considerations

  • Durability
    • how can we make it withstand having darts thrown at it?
  • Consistency
    • using switches instead of sensors should help with this!
  • Proof of Concept
    • Will the game work?
  • Program
    • will it do what we want it to?
  • Dartboard Mobility

Equipment Requirements

  • Projector (to be supplied by Kyle)
  • Darts
  • Arduino (2)
  • Materials for Dartboard (Wiring, Conductive + Permeable Layers/Fabric/Foam)
  • Acrylic for Housing/Protection of Wires
  • Things to glue it all together!

Program

  • The darts going through the permeable layers will act as a switch, thus triggering a response of either 0 (low) or 1 (high). We can then send that from arduino to processing. Once processing reads that 0/1, it can trigger a response.
  • If it clicks the wrong letter, an animation will happen and it will draw a new body part. If it clicks the right letter in a randomly generated word, it’ll display the letter on a line to fill in the word.
  • Note that once we conceptualize it and know the specific steps, it’ll be a lot easier to build the program and make the moving pieces work together.

Electrical Construction

It’s really just switches. The hard circitry is making an interesting switch. Plus, we may need more pin imputs… 😀

Physical Construction 

  • Corkboard
    • Needs to be durable and reliable, as well as work with our system
  • We plan to design an acrylic housing to not only hold the switches, but also to protect the wiring.
  • We want to put it on a whiteboard like system so it is mobile.
  • Make sure it’s consistent and pretty
  • Make it physically satisfying- don’t lose the integrity of the dartboard effect.

Three Aspects that Terrify Us 

  • Designing the Housing
  • Making sure the Switch is Reliable
  • Calling the Word and Triggering Letters

Addition to Game

This week I struggled with coming up with an idea on which of my processing designs I could combine with arduino. I ended up going with my game, popping balloons. But which aspect would I change? I played around with a button to pop them, a potentiometer to change the speed in which they moved, but then settled on using a potentiometer to change how many circles would appear at the start of the game.

My biggest challenge with this was rearranging certain aspects of my code in order to fit how I had defined the sensor. Then, once I had done that, I struggled with the sensor reading being on a loop. Jack helped me add in a boolean to have it only read once at the start of the game.

Here’s some video:

https://vimeo.com/333083714

Here’s my code from arduino:

int sensor;

void setup() {
  Serial.begin(9600);
   
}

void loop() {
  sensor = analogRead(A0);
  Serial.write(sensor);

}

and from processing:

import processing.serial.*;

Serial myPort;

int sensor;
int totBall = 0;
float xPos;
float yPos;
float spd;
boolean ugh = false;

//int counter = 30;
//boolean timer = true;
//int x = 1;
//boolean time = false;




Ball[] ball; 


boolean timeIsUp=false;
float timeTillOver;

void setup() {
  size(640, 360);

  printArray(Serial.list());
  String portname=Serial.list()[0];
  println(portname);
  myPort = new Serial(this, portname, 9600);

  noStroke();
  timeTillOver=millis()+random(10000, 30000);
}


void draw() {


  background(0);
for (int i = 0; i < totBall; i++) {
    ball[i].run();
  }


  fill(0);
  rect(0, 0, width, 50); 
}

void serialEvent(Serial myPort) {


  sensor=myPort.read();




  if (ugh == false) {
    totBall = int(map(sensor, 0, 1023, 1, 40));
    ball = new Ball[totBall];
    ugh = true;

    for (int i = 0; i < totBall; i++) {
      ball[i] = new Ball(random(0, width), random(0, height), random(1, 5), random(1, 5));
    }
  }
}

(note that I also used a ball class, but that’s the same as my last upload)

Lessons from Computing

This class was my first introduction to anything related to computing. I had always thought of it as such a cool thing to be able to do, like if you learned it, so many opportunities would open for you. I always admired people who could code.

Now, having just dipped my toe into what it means and what I can do with computing, I can assure my past self that it is just as rewarding to be able to code. Seeing your idea come to life on your computer screen is such a rush of satisfaction. It’s immediate success, but it can also be immediate failure, and for me, since I rely heavily on small but fast-paced successes to keep me motivated, this is perfect.

Every class I have, I know I’m going to just be blown away by what I’m able to do on my computer. Before this semester, I would never have thought I would be able to do this or even be able to learn. Learning has really made me more confident in myself and helped me figure out where I may go in the future. If anything, it’s done exactly what I thought it would do: open up new possibilities.

One of the biggest lessons I learned in the moments of frustrations was to take a step back, take a break, and then just write out exactly what you want to happen in plain text, not in code. Then, once you have the steps down, find the logical solution in code. Most of the time, this works really well if I’m overwhelmed with a task in processing or if I can’t figure out how to logically make something happen.

But another lesson I’ve learned and am still learning is to ask for help, look things up, collaborate with classmates, and generally just admit when you aren’t sure of something. In class, it’s so important to go line by line and make sure we understand but we don’t always have time for this, so our classmates become one of our most valuable resources. Having each other, Aaron, Jack, and the other lab assistants to go to when we need help has made learning this new skill so much smoother and a much more positive experience than it would have been if I tried to do it alone.

Tori and Kyle: Dart Board!

Oh come on, you knew we were gonna work together.

Concept: 

A dartboard with two panels layered on top of each other, one more permeable (conductive fabric). When you throw the dart, it connects both panels and triggers an output in processing.

Based on where the dart landed, processing effect with your score. (if too simple in processing, make a game out of it?)

A speaker that plays “Congratulations”

Technical Requirements:

LEDS (of course), projector, computer, redboard, conductive fabric, conductive paint, various wires and other circuit stuff

Equipment Needs: 

Corkboard, darts, plastic protection (for edges so darts don’t harm wiring) 

Block Diagrams: 

Idk man it’s a thing with text and a pretty drawing

I really never know how to title these posts. Basically, I was up late at night after toying around with three other ideas for generative text output projects and I got an idea for something totally different which led to me making the google search for “cartoon kid reading”. I then got this totally unrelated picture of two people, in each other’s arms, looking at their phones, underneath the stars and constellations. I absolutely loved this photo. The aesthetic was beautiful and I immediately wanted to incorporate it into my sketch. Plus, I hadn’t yet experimented with loading a photo into processing, so I would get that skill too.

Now how would this incorporate text? There was an obvious element of astrology in this photo and when I followed the picture to its source, I found the article, “Why millenials are so into astrology?” I started taking words and phrases from that article and inputting them into a word document:

“The New Age of Astrology” “Millenial and Gen Z Quotient” “Horoscopes in the Backs of Magazines”

I imported that text into the screen and made it seem like it was coming out of the iPhone and into the guy’s head. And as they fade out of sight (and off screen), they loop back around, each phrase going at a different speed and at a different size, randomized by processing.

This work wasn’t really about, like, making something happen for me. It was more about the aesthetic and the atmosphere that the work of art instilled in me when I saw it. I had thought about making the text do something, with a mouse press or a release, or something, but I think that would have taken away from the art.

Here’s the video:

Here’s the main code:

PImage img;
PFont f;

float x = 70;
float y = 230;
int space = 30;
int fade = 255;



Text lines[];

void setup() {
  f = createFont("Courier", 44);

  size(1000, 680);
  img = loadImage ("astrology.jpg");
  String[] textLines = loadStrings("text.txt");
  lines = new Text[textLines.length];
  for (int i = 0; i < lines.length; i++) {
    lines[i] = new Text(textLines[i], x + i*space/2, y + i*space);
  }
}

void draw() {

  scale(.7);    // setting up the background image, note that scalge is fine for this project but not ideal for the future
  image(img, 0, 0);

  for (int i = 0; i < lines.length; i++) {
    lines[i].run();
  }
}

Here’s the class:

class Text {

  float x;
  float xHome = 85;
  float y;
  float speed;
  int fade = 255;
  float fadeAdjustment = 2.3;
  int space;
  int lines = 8;
  String text;
  int size = int(random(15, 25));


  Text(String t, float _x, float _y) {
    text = t;
    x = _x;
    y = _y;
    speed = random(1.6, 4.8);
  }


  void display() {

    pushMatrix();
    textAlign(LEFT);
    rotate(-.1);
    textSize(size);
    fill(255, fade);
    text(text, x, y);
    popMatrix();
  }


  void update() {
    x+= speed;
    fade -=  fadeAdjustment;
  }

  void checkEdges() {

    if (y>height) {
      y=0;
    }
    if (y<0) {
      y=height;
    }
    if (x>800) {
      x=xHome;
      fade=255;
    }
  }


  void run() {
    display();
    update();
    checkEdges();
  }
}

 

Here’s credit to the article and the artist is Maura Dwyer. Also, cheers to Jack and Aaron for meeting with me <3

Digitizing Everything`

Right from the start, this reading made some great points. For example, the idea that word of mouth is one of the best advertising strategies. You can advertise as much as you want and spend as much money as you want, but people trust their friends and family. If someone they trust recommends a service or experience, they’re more likely to buy into it. That’s something to keep in mind for IM, but also in whatever I do in the future, with my career, with SIG events, with everything, really.

It was funny reading about Waze, because I use Waze when I’m home and my mom absolutely hates it. I find it really useful. It keeps me really focused on the road, especially when I hear “object in the road ahead” or “heavy traffic in 2 miles”. But my mom finds it so distracting. She doesn’t trust what it’s saying so she tends to overthink what she should do. Instead of slowing down and paying more attention to what could be on the road, she panics and stops thinking. It’s interesting how the same app can cause such different reactions in people.

But in its function, one of the coolest things for me is that, yes, it does follow set traffic patterns, speed limits, and general data inputs, but then it also grows in efficiency as more people use it and add information in the moment. This is one of the most fascinting subjects to me, this user-generated content, because it really stimulates this need for interaction between humans that the reading points out. While the reading focuses on Wikipedia, I find its application in Waze to be even cooler. Frustrated drivers and passengers sitting in traffic, logging in that they’re stuck in bumper-to-bumper traffic. And then that sets off a little signal to all of the Waze users that there’s heavy traffic up a few miles, or the route redirects. It’s an incredible feeling of “well I’m suffering in this traffic, but I’m helping the people behind me”.

On another note, I found a connection with the idea that information used to be very costly to produce, but easy and cheap to reproduce. I feel like we’re starting to experience this with our processing sketches. We have to take the time to create the classes and functions, but once we do, we can easily reproduce them with almost no time.