Dream Box

 

This final project was inspired by La Monte Young’s Dream House (hence the name), however, not only I wanted to play with how sound travels through space like La Monte Young did, I also want whoever enters the space of my project to be able to control sound in the whole space. The only way I could think of for making this project come true was building a room (Dream Box), sound isolating it as much as I could, or at least blocking as many of the external sounds as was possible in the given circumstances, and then finding a way for the visitor of the box to be able to move sound through space. The main purpose of sound isolating the room was to make sure it was easy to hear how the sound played in the Dream Box moves within the space by swiping the wall. Swiping the wall is not quite real though, so I was faking it by having an ultrasonic rangefinder in one part of the wall, so that depending of different values(distance) the sound would travel from speaker to speaker.

Making this final come to life was a long journey that consisted of two parts:

1) Making a wooden box (a.k.a. the Dream Box)

2) Programming processing so that it knows what speaker to send the mp3 file to depending on the value from the ultrasonic rangefinder

While making the room (or the box) of wood was time consuming and did not required a lot of planning and calculating for the right dimensions of the foam and food I had to cut, it did not cause me much of a problem in a sense that it all went as planned.

The initial idea for making the walls was taking 25mm plywood and simply attaching a couple of pieces together, however, this proved to be very unsafe and extremely heavy. Knowing that, the plan then was to do the walls in the similar way theater flats are done. I took 1×4 inch stick lumber and made three frames  (I used the Arts Center wall as the 4th wall) for the walls that I later simply skinned with 6mm luan plywood. The dimensions of the room, which turned out to be a cube, were 244x244x244cm, which seemed quite small on paper; however, it felt really large once I saw it in life. At some point I even doubted myself whether I need to keep working on it, or whether making something that big would be a waste of time. But, as the saying goes, go big or go home, and I’m not home yet J.

Those are the flats I’ve built.

I thought that the coding part of this project would be a lot simpler and would not cause me any problems. So first I made a working prototype with Arduino. I have soldered four speakers and had the Arduino tell which speaker to make noise depending on the value I was getting from the analog sensor. However, after spending hours making this prototype to work, I was pointed out that it is nearly worthless to have a nice big room be filled with annoying buzz. And that was totally right, why would I want the visitor of the Dream Box experience any annoying sound when one is supposed to enjoy their time in the room playing with the directionality of sound, rather than get tired of a random tone that is being played?

Unfortunately for me, I could not play anything but tone using Arduino only, unless I was to use an MP3 shield, but I could not do that since I had 4 outputs (speakers), and an MP3 shield is only capable of playing through 2.

So I had to use processing for this, which was fine, I never expected anything to go wrong. However, it felt like everything that could go wrong went wrong. The first problem was that I did not have enough outputs for four speakers in my computer either, so I had to find an audio interface with 4 or more outputs. Once I had that, I did not know how to tell processing to play it through this exact device. And once there was a way, processing thought that the audio interface was merely a speaker. I did not know a way to tell processing that this device that it thinks is a speaker actually has 14 different outputs, so I had to teach it how to see all of those separate outputs. If not Aaron, I would never figure this out. Aaron provided me with the code that would let processing know which specific output to play the sound from using the audio interface. I thought the struggle would end, however, it has just begun, because the Beads library was a little confusing, and once I figured out how to make it all work the way I want to with one sound, I did not stop there. I though one sound might be too boring, so I decided to add more sounds so that the visitor of the Dream Box had more freedom when choosing the direction and the type of sound. Making it work with four different sounds was really hard, but eventually the battle was won.

There was another problem along the way as I was going to use infra-red rangefinder at first, but the values there were too inaccurate and unpredictable art times, so I had to change to the ultrasonic rangefinder, which is a bit harder to program.

This is the Arduino code

#define trigPin 13
#define echoPin 12
#define led 11
#define led2 10
int buttonPressPin = 8;
int buttonPress = 0;

boolean isAvailable = true;
void setup() {
Serial.begin (9600);
Serial.println('0');
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode(buttonPressPin, INPUT);
pinMode(led, OUTPUT);
pinMode(led2, OUTPUT);

}

void loop() {
buttonPress = digitalRead(buttonPressPin);
long duration, distance;

digitalWrite(trigPin, LOW); // Added this line
delayMicroseconds(2); // Added this line
digitalWrite(trigPin, HIGH);
// delayMicroseconds(1000); - Removed this line
delayMicroseconds(10); // Added this line
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
distance = (duration / 2) / 29.1;
if (distance < 4) { // This is where the LED On/Off happens
digitalWrite(led, HIGH); // When the Red condition is met, the Green LED should turn off
digitalWrite(led2, LOW);
}
else {
digitalWrite(led, LOW);
digitalWrite(led2, HIGH);
}
if (distance >= 200 || distance <= 0) {
//Serial.println("Out of range");
}
else {
Serial.print(distance);
Serial.print(",");
Serial.println(buttonPress);
//Serial.println(" cm");
}
delay(10);
if (digitalRead(buttonPressPin) == 1) {
if (isAvailable == true) {
buttonPress = 1;
isAvailable = false;
}
} else {
isAvailable = true;
}

}

Processing code

  • import beads.*;
    import org.jaudiolibs.beads.AudioServerIO;
    import java.util.Arrays; 
    
    AudioContext audioContext;
    IOAudioFormat audioFormat;
    float sampleRate = 44100;
    int buffer = 512;
    int bitDepth = 16;
    int inputs = 2;
    int outputs = 14; //set for soundflower now
    float speaker1Gain, speaker2Gain, speaker3Gain, speaker4Gain;
    float soundPos;
    int distance, previousDistance;
    int buttonPress;
    import processing.serial.*;
    String sourceFile1;
    String sourceFile2;
    String sourceFile3;
    String sourceFile4;
    boolean isPressed;
    int spCounter;
    SamplePlayer sp1;
    SamplePlayer sp2;
    SamplePlayer sp3;
    SamplePlayer sp4;
    Serial myPort;
    
    WavePlayer wp;
    
    Gain g1;
    Gain g2;
    Gain g3;
    Gain g4;
    
    Glide gainGlide1;
    Glide gainGlide2;
    Glide gainGlide3;
    Glide gainGlide4;
    
    Glide rateValue1;
    Glide rateValue2;
    
    boolean initializeSound = true;
    
    void setup() {
    size(640, 640);
    
    buttonPress = 0;
    spCounter = 0;
    isPressed = false;
    
    sourceFile1 = sketchPath("") + "fire.wav";
    sourceFile2 = sketchPath("") + "waves.mp3";
    sourceFile3 = sketchPath("") + "thunder.mp3";
    sourceFile4 = sketchPath("") + "birds.wav";
    audioFormat = new IOAudioFormat(sampleRate, bitDepth, inputs, outputs);
    audioContext = new AudioContext(new AudioServerIO.JavaSound(), buffer, audioFormat);
    println("no. of inputs: " + audioContext.getAudioInput().getOuts()); 
    println("no of outputs: " + audioContext.out.getIns()); 
    try {
    // initialize our SamplePlayer, loading the file
    // indicated by the sourceFile string
    sp1 = new SamplePlayer(audioContext, new Sample(sourceFile1));
    sp2 = new SamplePlayer(audioContext, new Sample(sourceFile2));
    sp3 = new SamplePlayer(audioContext, new Sample(sourceFile3));
    sp4 = new SamplePlayer(audioContext, new Sample(sourceFile4));
    }
    catch(Exception e)
    {
    // If there is an error, show an error message
    // at the bottom of the processing window.
    println("Exception while attempting to load sample!");
    e.printStackTrace(); // print description of the error
    exit(); // and exit the program
    }
    
    rateValue1 = new Glide(audioContext, 1, 50);
    rateValue2 = new Glide(audioContext, 1, 50);
    
    wp = new WavePlayer(audioContext, 400, Buffer.SINE);
    
    gainGlide1 = new Glide(audioContext, 0.0, 50);
    gainGlide2 = new Glide(audioContext, 0.0, 50);
    gainGlide3= new Glide(audioContext, 0.0, 50);
    gainGlide4= new Glide(audioContext, 0.0, 50);
    
    g1 = new Gain(audioContext, 2, gainGlide1);
    g2 = new Gain(audioContext, 2, gainGlide2);
    g3 = new Gain(audioContext, 2, gainGlide3);
    g4 = new Gain(audioContext, 2, gainGlide4);
    
    g1.addInput(sp1);
    g2.addInput(sp1);
    g3.addInput(sp1);
    g4.addInput(sp1);
    g1.addInput(sp2);
    g2.addInput(sp2);
    g3.addInput(sp2);
    g4.addInput(sp2);
    g1.addInput(sp3);
    g2.addInput(sp3);
    g3.addInput(sp3);
    g4.addInput(sp3);
    g1.addInput(sp4);
    g2.addInput(sp4);
    g3.addInput(sp4);
    g4.addInput(sp4);
    
    audioContext.out.addInput(0, g1, 0); // OUT 1
    audioContext.out.addInput(1, g2, 0); // OUT 2
    audioContext.out.addInput(2, g3, 0); // OUT 3
    audioContext.out.addInput(3, g4, 0); // OUT 4
    
    audioContext.start();
    
    // for IR rangefinder
    printArray(Serial.list());
    String portname=Serial.list()[2];
    println(portname);
    myPort = new Serial(this, portname, 9600);
    myPort.clear();
    myPort.bufferUntil('\n');
    
    color fore = color(255);
    color back = color(0);
    
    
    
    // SamplePlayer can be set to be destroyed when
    // it is done playing
    // this is useful when you want to load a number of
    // different samples, but only play each one once
    // in this case, we would like to play the sample multiple
    // times, so we set KillOnEnd to false
    sp1.setKillOnEnd(false);
    sp1.setToLoopStart();
    sp2.setKillOnEnd(false);
    sp2.setToLoopStart();
    sp3.setKillOnEnd(false);
    sp3.setToLoopStart();
    sp4.setKillOnEnd(false);
    sp4.setToLoopStart();
    sp1.start(); // play the audio file
    }
    void draw() {
    
    if (buttonPress==1) {
    spCounter++;
    
    if (spCounter==5) {
    spCounter=1;
    }
    
    if (spCounter==1) {
    sp1.setToEnd();
    sp2.setToEnd();
    sp3.setToEnd();
    sp4.setToEnd();
  •  
    
    
    sp1.setToLoopStart();
    sp1.start();
    //println(“————————–ending——————“);
    } else if (spCounter==2) {
    sp1.setToEnd();
    sp2.setToEnd();
    sp3.setToEnd();
    sp4.setToEnd();
    sp2.setToLoopStart();
    sp2.start();
    } else if (spCounter==3) {
    sp1.setToEnd();
    sp2.setToEnd();
    sp3.setToEnd();
    sp4.setToEnd();
    sp3.setToLoopStart();
    sp3.start();
    } else if (spCounter==4) {
    sp1.setToEnd();
    sp2.setToEnd();
    sp3.setToEnd();
    sp4.setToEnd();
    sp4.setToLoopStart();
    sp4.start();
    }
    }

    //if (initializeSound == true) {
    // spCounter = 1;
    // sp1.start();
    // initializeSound = false;
    //}

    //if (buttonPress==1) {
    // spCounter++;

    // if (spCounter==5) {
    // spCounter=1;
    // }

    // if (spCounter==1) {
    // sp1.setToEnd();
    // sp2.setToEnd();
    // sp3.setToEnd();
    // sp4.setToEnd();
    // sp1.setToLoopStart();
    // sp1.start();
    // //println(“————————–ending——————“);
    // } else if (spCounter==2) {
    // sp1.setToEnd();
    // sp2.setToEnd();
    // sp3.setToEnd();
    // sp4.setToEnd();
    // sp2.setToLoopStart();
    // sp2.start();
    // } else if (spCounter==3) {
    // sp1.setToEnd();
    // sp2.setToEnd();
    // sp3.setToEnd();
    // sp4.setToEnd();
    // sp3.setToLoopStart();
    // sp3.start();
    // } else if (spCounter==4) {
    // sp1.setToEnd();
    // sp2.setToEnd();
    // sp3.setToEnd();
    // sp4.setToEnd();
    // sp4.setToLoopStart();
    // sp4.start();
    // }
    //}

    if (distance>200 || distance<=5) {
    distance = previousDistance;
    } else if (distance>6 && distance<50) {
    soundPos=map(distance, 1, 50, 0, 1);
    speaker1Gain= sin((1-soundPos) * PI/2);
    speaker2Gain= sin(soundPos * PI/2);
    speaker3Gain = 0;
    speaker4Gain = 0;
    } else if (distance>51 && distance<100) {
    soundPos =map(distance, 51, 100, 0, 1);
    speaker2Gain= sin((1-soundPos) * PI/2);
    speaker3Gain= sin(soundPos * PI/2);
    speaker4Gain = 0;
    speaker1Gain = 0;
    } else if (distance>101 && distance<150) {
    soundPos = map(distance, 101, 150, 0, 1);
    speaker3Gain= sin((1-soundPos) * PI/2);
    speaker4Gain= sin(soundPos * PI/2);
    speaker1Gain = 0;
    speaker2Gain = 0;
    } else if (distance>151 && distance<200) {
    soundPos = map(distance, 151, 200, 0, 1);
    speaker4Gain= sin((1-soundPos) * PI/2);
    speaker1Gain= sin(soundPos * PI/2);
    speaker2Gain = 0;
    speaker3Gain = 0;
    }

    println(speaker1Gain, speaker2Gain, speaker3Gain, speaker4Gain);
    println(“spCounter: ” + spCounter);
    //if(distance>0||distance<640){
    //soundPos = map(distance,0,640,0,1);
    // speaker4Gain = 1;
    // speaker1Gain = 1;
    // speaker2Gain = 1;
    // speaker3Gain = 1;
    //}

    //gainGlide1.setValue(distance / (float)width);
    //gainGlide2.setValue(distance / (float)width);
    //gainGlide3.setValue(distance / (float)width);
    //gainGlide4.setValue(mouseY / (float)height);

    //println(distance,mouseY);

    gainGlide1.setValue(speaker1Gain);
    gainGlide2.setValue(speaker2Gain);
    gainGlide3.setValue(speaker3Gain);
    gainGlide4.setValue(speaker4Gain);

    //println(mouseX);
    //loadPixels();
    ////set the background
    //Arrays.fill(pixels, back);
    ////scan across the pixels
    //for (int j = 0; j<4; j++) {
    // for (int i = 0; i < width; i++) {
    // //for each pixel work out where in the current audio buffer we are
    // int buffIndex = i * audioContext.getBufferSize() / width;
    // //then work out the pixel height of the audio data at that point
    // int vOffset = (int)((1 + audioContext.out.getValue(j, buffIndex)) * (height/2 ));
    // //draw into Processing’s convenient 1-D array of pixels
    // vOffset = min(vOffset, height);
    // vOffset+=(int)map(j, 0, 3, -250, 250);
    // pixels[vOffset* height + i] = fore;
    // }
    //}
    //updatePixels();

    previousDistance = distance;
    }

    void serialEvent(Serial myPort) {
    String s=myPort.readStringUntil(‘\n’);
    s=trim(s);
    if (s!=null) {
    int values[]=int(split(s, ‘,’));
    if (values.length==2) {
    distance=(int)values[0];
    buttonPress = (int)values[1];
    }
    println(“dist: ” + distance + ” button: ” + buttonPress);
    myPort.write(‘0’);
    }
    }

    void mousePressed() {
    //if

  • 
    
    (buttonPress==1) {
    // spCounter++;
    
    // if (spCounter==5) {
    // spCounter=1;
    // }
    
    // if (spCounter==1) {
    // sp1.setToEnd();
    // sp2.setToEnd();
    // sp3.setToEnd();
    // sp4.setToEnd();
    // sp1.setToLoopStart();
    // sp1.start();
    // //println("--------------------------ending------------------");
    // } else if (spCounter==2) {
    // sp1.setToEnd();
    // sp2.setToEnd();
    // sp3.setToEnd();
    // sp4.setToEnd();
    // sp2.setToLoopStart();
    // sp2.start();
    // } else if (spCounter==3) {
    // sp1.setToEnd();
    // sp2.setToEnd();
    // sp3.setToEnd();
    // sp4.setToEnd();
    // sp3.setToLoopStart();
    // sp3.start();
    // } else if (spCounter==4) {
    // sp1.setToEnd();
    // sp2.setToEnd();
    // sp3.setToEnd();
    // sp4.setToEnd();
    // sp4.setToLoopStart();
    // sp4.start();
    // } 
    //} 
    
    
    
    //spCounter++;
    //println("--------------------------------------------" + spCounter);
    //if (spCounter==1) {
    // turn on 1
    //sp1.start();
    //// turn off 2
    //sp1.setToEnd();
    //sp2.setToEnd();
    //sp3.setToEnd();
    //sp4.setToEnd();
    //sp1.setToLoopStart();
    //sp1.start();
    
    //} else if (spCounter==2) {
    // // turn on 2
    // rateValue2.setValue(1);
    // sp2.setRate(rateValue2);
    // // turn off 1
    // //rateValue1.setValue(0);
    // //sp1.setRate(rateValue1);
    // //sp1.setToEnd();
    //}
    /*else if (spCounter==3) {
    sp3.start();
    } else if (spCounter==4) {
    sp4.start();
    } */
    //if (spCounter==5) {
    //if (spCounter==3) { // was 5
    // spCounter=1;
    //}
    }

Another thing why I decided to do the framing this way is so that I could place the rangefinder flat on the inside of the wall, and still have some space between the 6mm luan and the layer of Styrofoam I am going to place in on top of it.

Last step was to sound-isolate the room, after I made sure everything worked the way it should. I used 10cm thick Styrofoam to go along all walls to provide sound isolation. The top two pieces of Styrofoam were covered with a layer of 15mm plywood to keep the whole Dream Box in place. The plywood also had 4 round holes on every corner of the roof, which was to let me put the speakers through it. To make the sound come from the ceiling once one is inside the box, I carved out slots for the speakers to go to, so that the speakers would face downwards. I must say that cutting Styrofoam with a handsaw is not as easy as it looks.

This is the top view of the way the speakers were stuffed into the ceiling

And this is half of the roof top view to see how they are connected. I had to solder the extensions for the speakers as well.

So that the Styrofoam does not look ugly, and also to create a feeling of a Dream Box, I coated all of the interior of the box with red fabric, which made it even more similar to the Dream House.

This is how it looks on the inside:

The only comment I had from the people who went through the experience in the Dream Box was that the button was more attractive to them than the arrow, probably the swipe indication was not that clear, or maybe it is in the human nature that pressing buttons feels so pleasing. I think working a little more on the button design and the swipe signifier design would fix this issue. Other than that, I’ve seen and heard only positive feedback and I ended up being really proud of what I’ve built in a relatively short period of time as well as very happy with how this piece was accepted by the people who walked into it.

Here is the exterior of the Dream Box

Webcam Live Drawing project

I have decided to work on live video for this project, which was mostly inspired by the Computer Vision article as well as I just thought that I can do more things with live video rather than a still image.

 

My initial idea was to track a certain color, say the color of the lips, and then once those pixels were detected, I would change the color of those pixels so that they affect live image. So instead of having pinkish lips, I would be able to change the color to orange, green, or whatever color I would choose in the live video.

Doing this, I faced one problem with the pixel color detection. Because the lips are pink and somewhat similar to the skin tone and the rest of our body, it is very tricky to get just the pixels of the lip color selected.

For example, if I pick just one certain color of the lips and leave the threshold for searching for the similar color in the image as very small, lets say the threshold is 5, then it would select just a tiny bit of the lips:

 

And if I increase the threshold to 25, it would select a lot more than just the lips:

So this made me give up on this idea just because I realized I would not be able to reach the level of accuracy I am looking for. Almost instantly then another idea came to mind, which also involved tracking color, but now I was using tracked color for a different purpose.

 

So the idea is that the program tracks light green color, which in my case is a cap for a pen, and draws a point on every pixel that it finds close to the green color within a threshold of 20.

 

Then, on button press, the program saves the coordinates of those circles, which allows the person who is in the video to draw shapes or whatever (s)he wants while the live video is recording. You can change the colors as well!

This is one the drawings I’ve made, which was fun, but I forgot I had to record a video, so I tried replicating it in the video once again and just having a little fun with it.

 

 

 

I would say that the biggest challenge I faced was figuring out the arrays for colors in a way that when the color is changed, it is changed only for something that is about to be drawn, rather than changing the color of everything that has already been drawn.

 

Here is the code:

import processing.video.*;
Capture video;
color trackColor;
float threshold;
int[] xvalues= {};
int[] yvalues= {};

color currentColorRed;
color currentColorGreen;
color currentColorBlue;
int[] colorsArrayRed= {};
int[] colorsArrayGreen= {};
int[] colorsArrayBlue= {};

boolean saveNow;


void setup() {
 size(640, 480);
 video = new Capture(this, 640, 480, 30);
 video.start();
 trackColor = color(30, 175, 94);
 currentColorRed = 255;
 currentColorGreen = 255;
 currentColorBlue = 255;
}

void draw() {

if (video.available()) {
 video.read();
 }
 video.loadPixels();
 //image(video, 0, 0);

//threshold = map(mouseX, 0, width, 0, 100);
 threshold=20;
 float avgX = 0;
 float avgY = 0;

int count=0;

for (int y=0; y<height; y++) {
 for (int x=0; x<width; x++) {
 int loc = (width-1-x)+(y*width);

//what is the current color
 color currentColor = video.pixels[loc];
 float r1=red(currentColor);
 float g1=green(currentColor);
 float b1=blue(currentColor);
 float r2=red(trackColor);
 float g2=green(trackColor);
 float b2=blue(trackColor);

float d = dist(r1, g1, b1, r2, g2, b2);

if (d<threshold) {
 noStroke();
 strokeWeight(1);
 ellipse(x, y, 10, 10);
 avgX+=x;
 avgY+=y;

if (saveNow ==true) {
 xvalues = append(xvalues, x);
 yvalues = append(yvalues, y);
 colorsArrayRed = append(colorsArrayRed, currentColorRed);
 colorsArrayGreen = append(colorsArrayGreen, currentColorGreen);
 colorsArrayBlue = append(colorsArrayBlue, currentColorBlue);
 }
 count++;
 }
 }
 }
 println(xvalues.length);

if (xvalues.length > 1) {
 for (int i = 0; i < (xvalues.length - 1); i++) {
 fill(colorsArrayRed[i], colorsArrayGreen[i], colorsArrayBlue[i]);
 ellipse(xvalues[i], yvalues[i], 10, 10);
 }
 }
 video.updatePixels();

pushMatrix();
 translate(width, 0);
 scale(-1, 1);
 tint(255, 50);
 image(video, 0, 0);
 tint(255, 255);
 popMatrix();

if (count>0) {
 avgX = avgX/count;
 avgX = avgX/count;
 //fill(trackColor);
 //strokeWeight(4.0);
 //stroke(0);
 //ellipse(avgX, avgY, 8, 8);
 }
}

void keyPressed() {
 if (keyCode == ENTER) {
 saveNow = !saveNow;
 }
 if (key == 'r') {
 currentColorRed = 255; 
 currentColorGreen = 0; 
 currentColorBlue = 0;
 }
 if (key == 'g') { 
 currentColorRed = 0; 
 currentColorGreen = 255; 
 currentColorBlue = 0;
 }
 if (key == 'b') {
 currentColorRed = 0; 
 currentColorGreen = 0; 
 currentColorBlue = 255;
 }
 if (key == 'y') {
 currentColorRed = 255; 
 currentColorGreen = 255; 
 currentColorBlue = 0;
 }
 if (key == 'l') {
 currentColorRed = 0; 
 currentColorGreen = 255; 
 currentColorBlue = 255;
 }
 if (key == 'p') {
 currentColorRed = 255; 
 currentColorGreen = 0; 
 currentColorBlue = 255;
 }
 if (key == 'B') {
 currentColorRed = 0; 
 currentColorGreen = 0; 
 currentColorBlue = 0;
 }
 //if(keyCode == LEFT){
 //int[] colorsArrayRed= {};
 //int[] colorsArrayGreen= {};
 //int[] colorsArrayBlue= {};
 //int[] xvalues= {};
 //int[] yvalues= {};}
 //
}

On Computer Vision article

This reading was very inspirational as it gave me a lot of ideas to choose from when it came to deciding what I wanted to make for this project. After reading Computer Vision in Interactive Art chapter I knew I wanted to work with live video and tracking, be it tracking color or brightness or movement or whatever I could possibly imagine tracking.

The elementary computer vision techniques mentioned in the article, which are detecting motion, detecting presence, detection through brightness thresholding, simple object taking and basic interactions, gave me a better idea of what I could use in my project. All of these techniques were well explained which gave me enough understanding of what I should be aiming for in my work.

The one project I was impressed by the most was the Suicide Box by the Bureau of Inverse Technology installed in 1996. I did not think that a computer vision project, with the help of machine-vision based surveillance, could have such a big social impact and cause ethical controversy. At the same time, this project was a proof (if the data of the amount of recorded suicides was real) that machines can record data with the accuracy that humans cannot.

When working on my project and color detection, I was suggested to watch a YouTube video of Daniel Shiffman explaining the algorithms of color tracking. And he also had this article on Computer Vision open throughout the most part of the video, and even referenced it a couple of times, so this definitely helped me understand what Daniel was talking about!

Processing meets Arduino project

For the Processing meets Arduino assignment I’ve decided to expand on my jumping ball game project and add a joystick to it. Thus, instead of being controlled by arrows on keyboard, the ball is now controlled by the joystick. This was fairly simple, since the ball’s movement is only controlled on the x axis, however, it took me a little to get used to figure out the adjustments I had to do in terms of screen width/canvas width so that the ball moves just like I want it to.

Another thing that was a sort of an easy fix but took a little while to figure out was how to restart the game on button press rather than restarting the whole processing sketch. The first idea was to restart the port connection on button press, but then an error would pop up and freeze the whole computer, because reestablishing the port connection would not work. The second thing I tried was rerunning the setup void (on button press) again once the game is over, however, that would crash processing and freeze the computer as well. Then I got lucky because Pierre walked into the room and suggested something very simple that I have not thought of for some reason. His idea was to create a gameRunning Boolean that would start the initialize void and the draw void. Thus, rather than restarting the whole setup void, I would just have a gameRunning Boolean on button press, and then depending on that the initialize and draw would either run or not, which will then be connected to the gameOver Boolean, and together, if game is over and game is not running the button press would restart the initialize and draw loops to start the game again.

As some improvements from the last time I’ve presented the game, I’ve added a couple of things. First of all, the platforms are now fading in rather than just suddenly appearing out of nowhere. Then I’ve also added the “Welcome” screen (I forgot to change ‘lol’ from when I was trying out if it works, and now I think it is just a part of the project), and the “Game Over” screen.

Here is the new code:

 

Game Sketch

Player p;
ArrayList platforms, platformsDanger;
boolean upPressed, leftPressed, downPressed, rightPressed;
int score, fallCount;
boolean gameOver;
int Ypos;
int Xpos;
int Sel;
boolean gameRunning;
int led=0;
int led2=0;

// to control a ball with a joystick
import processing.serial.*;
Serial myPort;

void setup()
{
 Xpos = 0;
 Ypos=0;
 Sel=1;
 size(480, 640);
 frameRate(60);
 //initializeDanger();
 ellipseMode(CORNER);

// for joystick
 printArray(Serial.list());
 String portname=Serial.list()[2];
 println(portname);
 myPort = new Serial(this, portname, 9600);
 myPort.clear();
 myPort.bufferUntil('\n');
}

void initialize()
{
 if (Sel==0) {
 gameRunning = true;
 score = 0;
 fallCount = 0;
 gameOver = false;
 p = new Player(width/2, height/2);
 platforms = new ArrayList();
 platforms.add(new MovingPlatform(20, 80, 70, 8, false));
 platforms.add(new Platform(width/2, height/2, 100, 8, false));
 platforms.add(new Platform((int)random(40, 500), 320, (int)random(50, 120), 8, false));
 platforms.add(new Platform((int)random(40, 500), 220, (int)random(50, 120), 8, false));
 platforms.add(new Platform((int)random(40, 500), 120, (int)random(50, 120), 8, false));
 platforms.add(new Platform((int)random(40, 500), 20, (int)random(50, 120), 8, false));
 //platforms.add(new MovingPlatform((int)random(20,400),(int)random(10,150),20,20, true));
 //platforms.add(new MovingPlatform((int)random(20,400),(int)random(10,150),30,30, true));
 //platforms.add(new MovingPlatform((int)random(20,400),(int)random(10,150),45,45, true));
 } else { 
 background(255);
 fill(0);
 textSize(50);
 textAlign(CENTER);
 if (gameOver == false) { 
 text("lol", width/2, height/2);
 textSize(25);
 text("press to start", width/2, 500);
 
 } else if (gameOver == true) {
 text("You Lost", width/2, height/2);
 textSize(25);
 text("press to restart", width/2, 500);
 }
 }
}

//void initializeDanger(){
// platformsDanger = new ArrayList();
// platforms.add(new MovingPlatform(20,80,20,20, true));
// platforms.add(new MovingPlatform(20,80,30,30, true));
// platforms.add(new MovingPlatform(20,80,25,25, true));

//}

void draw() { 
 if (gameRunning ==true && gameOver == false) {
 led = 1;
 led2=0;
 } else if (gameOver == true) {
 led=0;
 led2=1;
 } else {
 led2=1;
 led = 0;
 }
 if (gameRunning == true) {
 //println(score);
 background(255);
 fill(0, 10, 153, 204);
 textSize(12);
 text("score", 15, 15);
 text(score, 60, 15);
 //println(platforms.size());
 for (int i=0; i<platforms.size(); i++)
 {

p.collide((Platform)platforms.get(i));
 ((Platform)platforms.get(i)).display();
 ((Platform)platforms.get(i)).move();
 //if(i<3){
 // p.collide((Platform)platformsDanger.get(i));
 // ((Platform)platformsDanger.get(i)).display();
 // ((Platform)platformsDanger.get(i)).move();
 //}
 }
 p.display(); 
 p.move();

adjustViewport();
 cleanUp();
 seedNewPlatforms();
 if (platformsBelow() == 0) gameOver = true;
 if (gameOver) fallCount++;
 if (fallCount > 3 ) initialize();
 } else { 
 initialize();
 }
}

int platformsBelow()
{
 int count = 0;
 for (int i=0; i<platforms.size(); i++)
 {
 if (((Platform)platforms.get(i)).y >= p.y) count++;
 }
 return count;
}


void adjustViewport()
{
 // above midpoint
 float overHeight = height * 0.5 - p.y;
 if (overHeight > 0) {
 p.y += overHeight;
 for (int i=0; i<platforms.size(); i++)
 {
 ((Platform)platforms.get(i)).y += overHeight;
 }
 score += overHeight;
 }
 // falling
 float underHeight = p.y - (height-p.h-4);
 if (underHeight > 0)
 {
 p.y -= underHeight;
 for (int i=0; i<platforms.size(); i++)
 {
 ((Platform)platforms.get(i)).y -= underHeight;
 }
 } 
 //above midpoint danger
 //for(int i=0; i<platformsDanger.size(); i++)
 // {
 // ((Platform)platformsDanger.get(i)).y += overHeight;
 // }
 // score += overHeight;

// falling
 //underHeight = p.y - (height-p.h-4);
 //if(underHeight > 0){
 // p.y -= underHeight;
 // for(int i=0; i<platformsDanger.size(); i++)
 // {
 // ((Platform)platformsDanger.get(i)).y -= underHeight;
 // }
 //}
}

void cleanUp()
{
 for (int i=platforms.size()-1; i>=0; i--) {
 // scrolled off the bottom
 if (((Platform)platforms.get(i)).y > height) {
 platforms.remove(i);
 }
 }
 //for(int i=platformsDanger.size()-1; i>=0; i--){
 // // scrolled off the bottom
 // if(((Platform)platformsDanger.get(i)).y > height){
 // platformsDanger.remove(i);
 // }
 //}
}

void seedNewPlatforms()
{
 if (platforms.size() < 9)
 {
 float randomizer = random(0, 10);

if (score < 1250) {
 if (randomizer < 3) {

platforms.add(new MovingPlatform((int)random(10, width-80), -10, 70, 8, false));
 } 
 //else if (randomizer < 4) {
 // platforms.add(new MovingPlatform((int)random(20,400),-10,30,30, true));
 // platforms.add(new MovingPlatform((int)random(20,400),-10,30,30, true));
 //}
 // else if (randomizer < 1) {
 // platforms.add(new MovingPlatform((int)random(20,400),-10,45,45, true));
 //} else {
 platforms.add(new Platform((int)random(20, 400), -10, (int)random(50, 120), 8, false));
 } else if (score < 500) {
 if (randomizer < 3) {
 platforms.add(new MovingPlatform((int)random(10, width-80), 300, 70, 8, false));
 } else {
 platforms.add(new Platform((int)random(20, 400), 300, (int)random(50, 120), 8, false));
 }
 } else { 
 if (randomizer < 9) {
 platforms.add(new MovingPlatform((int)random(20, 400), -10, 30, 30, true));
 } else {
 platforms.add(new MovingPlatform((int)random(10, width-80), 300, 70, 8, false));
 }
 }
 }
}

//if(platformsDanger.size() < 3)
//{
// platforms.add(new MovingPlatform((int)random(10,width-80),-10,20,20,true));
//}


void keyPressed()
{
 if (keyCode == UP) upPressed = true;
 if (keyCode == LEFT) leftPressed = true;
 if (keyCode == DOWN) downPressed = true;
 if (keyCode == RIGHT) rightPressed = true;
}

void keyReleased()
{
 if (keyCode == UP) upPressed = false;
 if (keyCode == DOWN) downPressed = false;
 if (keyCode == LEFT) leftPressed = false;
 if (keyCode == RIGHT) rightPressed = false;
}


void serialEvent(Serial myPort) {
 String s=myPort.readStringUntil('\n');
 s=trim(s);
 if (s!=null) {
 //println(s);
 int values[]=int(split(s, ','));
 if (values.length==3) {
 //Xpos=(int)map(values[0],0,1023,0, width);
 Xpos=(int)values[0];
 Ypos=(int)map(values[1], 0, 1023, 0, height);
 Sel=values[2];
 println("POS:");
 println(Xpos);
 println("ENDPOS");
 println("SEL "+Sel);
 
 myPort.write(led+","+led2+"\n");
 //println(Ypos);
 }
 }
}

 

Platform

class Platform {
 float x, y, w, h;
 float xvel; 
 boolean danger;
 float alpha;
 Platform(int x_, int y_, int w_, int h_, boolean d)
 {
 x = x_;
 y = y_;
 w = w_;
 h = h_;
 danger = d;

alpha = 0;

println("NEW PLATFORM:");
 println("Y: " + str(y));
 }

void display()
 {
 if (danger == false) {
 fill(0, 0, 0, alpha);
 } else {
 fill(255, 0, 0, alpha);
 }
 noStroke();
 rect(x, y, w, h);


if (alpha < 255) {
 alpha+=4f;
 }
 }

void move()
 {


 x += xvel;
 y += 0;
 }
}

class MovingPlatform extends Platform
{
 static final float speed = 0.9;

MovingPlatform(int x, int y, int w, int h, boolean d)
 {
 super(x, y, w, h, d);
 xvel = speed;
 }

void move()
 {
 super.move();
 if ( (x+w > width - 10) || (x < 10) )
 {
 xvel *= -1;
 }
 }
}

Player

class Player
{
 float gravity = 0.14;
 float bounceVel = 9;
 float maxYVel = 13;
 float maxXVel = 3;

float x, y, xVel, yVel;
 int w, h;
 Player(int x, int y)
 {
 w = h = 20;
 this.x = x;
 this.y = y;
 }

void display()
 {
 fill(0, 0, 240);
 ellipse(x, y, w, h);
 }

void move()
 {
 x += xVel;
 y += yVel;

if (x > width-w) x = 0;
 if (x < 0) x = width-w;

//horizontal
 // for arrows
 //if (!gameOver){
 // if (leftPressed) xVel -= 0.05;
 // else if (rightPressed) xVel += 0.05;
 // else
 // {
 // if (xVel > 0) xVel -= 0.03;
 // else xVel += 0.03;
 // }
 //}
 //for joystick
 if (!gameOver) {
 if (Xpos<500) xVel -= 0.05;
 else if (Xpos>503) xVel += 0.05;
 else
 {
 if (xVel > 0) xVel -= 0.03;
 else xVel += 0.03;
 }
 }
 if (abs(xVel) < 0.01) xVel = 0;
 xVel = min(maxXVel, xVel);
 xVel = max(-maxXVel, xVel);

// vertical
 yVel += gravity;
 yVel = min(maxYVel, yVel);
 yVel = max(-maxYVel, yVel);
 }

void collide(Platform plat) {


 if (x < plat.x + plat.w &&
 x + w > plat.x &&
 y < plat.y + plat.h &&
 y + h > plat.y)
 {
 if (plat.danger == false) {
 if (yVel > 0) {
 yVel = -bounceVel;
 }
 } else {
 if (plat.danger == true) {
 gameOver = true;
 gameRunning = false;
 Sel = 1;
 }
 // game over
 }
 }
 }
}

 

Here is how it looks 🙂

What computing means to me

Before coming to this class I was not exposed to any kind of programming or coding whatsoever, so it definitely was challenging for me at first to understand the concepts of coding and how things work in general. However, as I was getting more and more into it, I started liking what I was doing, because I started seeing connections between things we do in class and things that surround me in my everyday life. I started looking at things around me with a different perspective, things as simple as light switches and as complicated as some computer games would make me think “khm, I sort of know how to make it”, which was an amazing feeling. I am not sure if it had necessarily “made me a better person”, but I think the exposure to this new world of programming and building things in this class has changed the way I look at everyday objects and made me start appreciating them more.

On “The Digitization of Just About Everything”

“The Digitization of Just About Everything” by Erik Brynjolfsson and Andrew McAfee focuses on the digital age and how the digital age changed the way certain concepts are used. For example, if previously “Hey, have you heard about…” was a phrase that implied name of a band, restaurant, place to visit, TV show, book, or movie in the end, now this phase often goes with a name of a website or an app. Or, how with the advent of digital age, the information became non-rival rather than remained rival (unlike information on paper/in books, digital information cannot really be used up)

 

One of the things that I particularly enjoyed about this reading is how well the terms that are used explained. For example, for a person who knows what “non-rival” information is and what “zero marginal cost of reproduction” means exactly, the idea of a chapter is delivered within the first paragraph or two. However, thinking of the people who are not familiar with what non-rival means or what a marginal cost is, Brynjolfsson and McAfee explain the meaning of those terms in a very simple language using basic examples.

 

Having read the whole article, I think that the points brought up about what it takes to make any kind of digital product, be it an app or a website, are crucial when it comes to how people perceive digital content. In my opinion, many people take for granted a lot of very useful digital products they use. A great example would be Google translate, as mentioned in the reading. Yes, the computer was not actually taught human languages according all the rules that any language follows, but someone still had to write a program that would scan all the documents it has in its database (in both the language you are using and looking to translate into) and look for a close match in a matter of a second. People still complain about the quality of the translation, despite that this huge body of information was not cheap to generate. Yes, this information is cheap to replicate, but that does not mean that it does not require effort to put everything together so that it actually works as well as it does. And when people depreciate someone’s effort just because they do not realize about that amount of effort that was put into something, it is upsetting.

Assignment 9 : Jumping Ball Game

I really wanted to try making a game for this assignment, rather than some sort of an art piece, so the first thing that came to my mind was Tetris game. I thought it should be fairly simple to make, however, my expectations of Tetris being easy to make were destroyed when I started looking up different examples of how to make Tetris online. It involved making a grid, clearing rows when they were full and just a lot of math, so at that point I thought that I need to think of a game which will make me focus on programming rather than calculating.

 

I thought of some games that have very basic concept, and one that came to mind was Doodle Jump I used to play at school. I remembered how much time I used to spend playing that simple game and it still managed to keep me entertained after I’ve been playing it for a while. So I decided to do something similar, using a ball instead of a doodle.

 

I went looking for examples online to find inspiration, but most of what I found was really complicated, until I came across this very simple (as compared to other) example  https://www.openprocessing.org/sketch/7824
However, reading through this example got me really confused at points, for example sometimes I would try to understand the code he uses and think “why does this guy use ‘float yVel’ for platforms, if the platforms are not moving on the y-axis?”

Then I started playing with the code, commenting things out, like this ‘float yVel’ (among the other things I was commenting out), to see how it works.

Then, there were things that have not made much sense to me, so I asked Nahil to help me out and explain why and how things are used. Eventually, after a couple of hours, realising what is useful and what seemed dumb and redundant (at least to me), I went on and with Nahil’s help started creating my own ‘doodle jump’.

And as things started working out, after getting the circle jump off the platforms and be controlled the I wanted it to be, I started thinking of some things to add to the basic jumping. For example, difficulty levels, with invisible platforms appearing as you go, as well as creating “dangerous” or “monster” squares under Platforms that will make you lose the game once you collide with them.

 

It took around 5-8 hours (can’t really remember at this point), but eventually I am happy with how the project turned out to be. However, there are still things to work on, such as textual visualisations of game start, game over, and just interface in general.

Here is how it looks on the highest difficulty level!
With no platforms you can see to jump next on, and with red ‘dangers’ floating on the screen.

Here is the code, divided into 3 parts: game sketch-the main one, and two classes : platform and player

 

Game sketch

Player p;
ArrayList platforms, platformsDanger;
boolean upPressed, leftPressed, downPressed, rightPressed;
int score, fallCount;
boolean gameOver;



void setup()
{
 size(480, 640);
 frameRate(60);
 initialize();
 //initializeDanger();
 ellipseMode(CORNER);
}

void initialize()
{
 score = 0;
 fallCount = 0;
 gameOver = false;
 p = new Player(width/2, height/2);
 platforms = new ArrayList();
 platforms.add(new MovingPlatform(20,80,70,8, false));
 platforms.add(new Platform(width/2,height/2,100,8, false));
 platforms.add(new Platform((int)random(40,500),320,(int)random(50,120),8, false));
 platforms.add(new Platform((int)random(40,500),220,(int)random(50,120),8, false));
 platforms.add(new Platform((int)random(40,500),120,(int)random(50,120),8, false));
 platforms.add(new Platform((int)random(40,500),20,(int)random(50,120),8, false));
 //platforms.add(new MovingPlatform((int)random(20,400),(int)random(10,150),20,20, true));
 //platforms.add(new MovingPlatform((int)random(20,400),(int)random(10,150),30,30, true));
 //platforms.add(new MovingPlatform((int)random(20,400),(int)random(10,150),45,45, true));
}

//void initializeDanger(){
// platformsDanger = new ArrayList();
// platforms.add(new MovingPlatform(20,80,20,20, true));
// platforms.add(new MovingPlatform(20,80,30,30, true));
// platforms.add(new MovingPlatform(20,80,25,25, true));


 
//}

void draw()
{
 println(score);
 
 background(255);
 fill(0, 10, 153, 204);
 text("score", 15, 15);
 text(score, 60, 15);
 //println(platforms.size());
 for(int i=0; i<platforms.size(); i++)
 {

 p.collide((Platform)platforms.get(i));
 ((Platform)platforms.get(i)).display();
 ((Platform)platforms.get(i)).move();
 //if(i<3){
 // p.collide((Platform)platformsDanger.get(i));
 // ((Platform)platformsDanger.get(i)).display();
 // ((Platform)platformsDanger.get(i)).move();
 //}
 }
 p.display(); 
 p.move();

 adjustViewport();
 cleanUp();
 seedNewPlatforms();
 if (platformsBelow() == 0) gameOver = true;
 if (gameOver) fallCount++;
 if (fallCount > 3 ) initialize();
}

int platformsBelow()
{
 int count = 0;
 for(int i=0; i<platforms.size(); i++)
 {
 if (((Platform)platforms.get(i)).y >= p.y) count++;
 }
 return count;
}

void adjustViewport()
{
 // above midpoint
 float overHeight = height * 0.5 - p.y;
 if(overHeight > 0){
 p.y += overHeight;
 for(int i=0; i<platforms.size(); i++)
 {
 ((Platform)platforms.get(i)).y += overHeight;
 }
 score += overHeight;
 }
 // falling
 float underHeight = p.y - (height-p.h-4);
 if(underHeight > 0)
 {
 p.y -= underHeight;
 for(int i=0; i<platforms.size(); i++)
 {
 ((Platform)platforms.get(i)).y -= underHeight;
 }
 } 
 //above midpoint danger
 //for(int i=0; i<platformsDanger.size(); i++)
 // {
 // ((Platform)platformsDanger.get(i)).y += overHeight;
 // }
 // score += overHeight;
 
 // falling
 //underHeight = p.y - (height-p.h-4);
 //if(underHeight > 0){
 // p.y -= underHeight;
 // for(int i=0; i<platformsDanger.size(); i++)
 // {
 // ((Platform)platformsDanger.get(i)).y -= underHeight;
 // }
 //}
 
}

void cleanUp()
{
 for(int i=platforms.size()-1; i>=0; i--){
 // scrolled off the bottom
 if(((Platform)platforms.get(i)).y > height){
 platforms.remove(i);
 }
 }
 //for(int i=platformsDanger.size()-1; i>=0; i--){
 // // scrolled off the bottom
 // if(((Platform)platformsDanger.get(i)).y > height){
 // platformsDanger.remove(i);
 // }
 //}
}

void seedNewPlatforms()
{
 if(platforms.size() < 9)
 {
 float randomizer = random(0,10);
 
 if(score < 1250){
 if(randomizer < 3) {
 
 platforms.add(new MovingPlatform((int)random(10,width-80),-10,70,8,false));
 } 
 //else if (randomizer < 4) {
 // platforms.add(new MovingPlatform((int)random(20,400),-10,30,30, true));
 // platforms.add(new MovingPlatform((int)random(20,400),-10,30,30, true));
 //}
 // else if (randomizer < 1) {
 // platforms.add(new MovingPlatform((int)random(20,400),-10,45,45, true));
 //}
 else {
 platforms.add(new Platform((int)random(20,400),-10,(int)random(50,120),8, false));
 
 }
 }
 
 
 else if (score < 2000){
 if(randomizer < 3) {
 platforms.add(new MovingPlatform((int)random(10,width-80),300,70,8,false));
 }
 else {
 platforms.add(new Platform((int)random(20,400),300,(int)random(50,120),8, false));
 }
 }
 
 else{ 
 if(randomizer < 3){
 platforms.add(new MovingPlatform((int)random(20,400),-10,30,30, true));
 }
 
 else {
 platforms.add(new MovingPlatform((int)random(10,width-80),300,70,8,false));
 }
 
 
 
 
 }
 }
 
 //if(platformsDanger.size() < 3)
 //{
 // platforms.add(new MovingPlatform((int)random(10,width-80),-10,20,20,true));
 //}
 
}



void keyPressed()
{
 if (keyCode == UP) upPressed = true;
 if (keyCode == LEFT) leftPressed = true;
 if (keyCode == DOWN) downPressed = true;
 if (keyCode == RIGHT) rightPressed = true;
}

void keyReleased()
{
 if (keyCode == UP) upPressed = false;
 if (keyCode == DOWN) downPressed = false;
 if (keyCode == LEFT) leftPressed = false;
 if (keyCode == RIGHT) rightPressed = false;
}

 

Platform

class Platform{
 float x,y,w,h;
 float xvel; 
 boolean danger;
 Platform(int x_, int y_, int w_, int h_, boolean d)
 {
 x = x_;
 y = y_;
 w = w_;
 h = h_;
 danger = d;
 
 println("NEW PLATFORM:");
 println("Y: " + str(y));
 
 }

 void display()
 {
 if (danger == false){
 fill(0);}
 else if (danger == true){
 fill(255,0,0);}
 rect(x,y,w,h);
 }

 void move()
 {
 

 x += xvel;
 y += 0;
 }

}

class MovingPlatform extends Platform
{
 static final float speed = 0.9;
 
 MovingPlatform(int x, int y, int w, int h, boolean d)
 {
 super(x, y, w, h, d);
 xvel = speed;
 }
 
 void move()
 {
 super.move();
 if( (x+w > width - 10) || (x < 10) )
 {
 xvel *= -1;
 }
 }
}

 

Player

class Player
{
 float gravity = 0.14;
 float bounceVel = 9;
 float maxYVel = 13;
 float maxXVel = 3;

 float x, y, xVel, yVel;
 int w, h;
 Player(int x, int y)
 {
 w = h = 20;
 this.x = x;
 this.y = y;

 }

 void display()
 {
 fill(0,0,240);
 ellipse(x,y,w,h);
 }

 void move()
 {
 x += xVel;
 y += yVel;

 if (x > width-w) x = 0;
 if (x < 0) x = width-w;

 // horizontal
 if (!gameOver){
 if (leftPressed) xVel -= 0.05;
 else if (rightPressed) xVel += 0.05;
 else
 {
 if (xVel > 0) xVel -= 0.03;
 else xVel += 0.03;
 }
 }
 if (abs(xVel) < 0.01) xVel = 0;
 xVel = min(maxXVel, xVel);
 xVel = max(-maxXVel, xVel);

 // vertical
 yVel += gravity;
 yVel = min(maxYVel, yVel);
 yVel = max(-maxYVel, yVel);
 }

 void collide(Platform plat){
 
 
 if(x < plat.x + plat.w &&
 x + w > plat.x &&
 y < plat.y + plat.h &&
 y + h > plat.y)
 {
 if (plat.danger == false) {
 if (yVel > 0) {
 yVel = -bounceVel;
 }
 } else {
 if (plat.danger == true){
 gameOver = true;
 }
 // game over
 }
 }
 }

}

 

smth

Computer Graphics and Art (replication assignment)

For this assignment I picked Simulated Color Mosaic by Hiroshi Kawano to recreate in processing:

From the first glance I already knew it would not be as easy as it looks, despite the fact it is made of simple shapes (rectangles).

I first started with trying to recreate it by making rectangles with the values I eyeballed so that whatever comes out looks somehow like the original piece of art. After a couple of rectangles I knew this is not going to work out, as I would spend too much time to place rectangles in the right spots and make them right sized by just eyeballing the values.

I realised I needed exact values and so I was thinking about using Illustrator to help me get the exact coordinates of every corner for every rectangle, so that then I could use rectmode(CORNER) to make a rectangle using two opposite corners. However, I found even easier solution, which was an online website which do the same thing: give me exact coordinates of wherever I click on the image.

So I ended up selecting all the corners I needed, which on the website looked like this:

However, having all the values, I thought it might be a little too tedious and take a bunch of time, since all I was looking at were endless digits and every mistake on my side would cause the whole thing go wrong and the pattern to be different from the original.

So about halfway into recreating the original, a friend of mine suggested that I make a pattern that looks similar but is not exactly the same, so I wrote this code that creates a similar-looking pattern and changes it 3 times a second.

void setup() {
size(610, 620);
background(255);
strokeWeight(0);
noStroke();
frameRate(3);
}


void draw(){
background(255);
for (int x=0; x< width; x++) {
for (int y=0; y< height; y++) {
if (random(1) < 0.0015) {
fill(0);
} else {
noFill();
}
rect(x,y,random(50), random(50));
}
}

}

This is how one of the endless amount of random patterns it creates looks like

However, with a little help this took me no longer than 10 minutes to write, so I felt like I had to do something more, more specifically finish my first idea of making an exact replica of the original artwork.

It took a while, because my eyes would often get tired and I would make lots of typos, but eventually I recreated Simulated Color Mosaic in processing. This is how my version of it looks :

and here again is the original

There are some minor differences that are not noticeable unless someone takes lots of time comparing them, at some point I just realised its time to give up and stop fixing every minor detail. But if you have lots of spare time, you can take on the challenge and let me know if you found them 🙂

The code for the replica is simply too long to attach and would take ages to scroll through to get to other people’s works, but if anyone is interested- feel free to ask, I will send it directly to you.

 

 

Portrait assignment

This assignment was pretty straightforward, but I did not know what level of accuracy is expected from the portrait. So first I looked up a way to insert an image to processing and then I thought about doing something with pixels in order to reveal the image.

I found about pointillism function, which looks really nice, and what it does is it reveals an image by selecting random coordinates on a page, and revealing those coordinates one by one. But I wanted to have some level of interactivity, so what I did at first is I inserted my image, and thought it would be cool to reveal it using the mouse. So the coordinates are no longer picked randomly, but followed the mouse, which looked like a reverse process of erasing something.

But then, however, I was told that we had to draw our portrait line by line in code, so then I immediately thought about simple interactions and basic shapes.

First, I made the face, using ellipses and bezier curves for the most part, and then, once the face was done, I started working on the interactive part of changing the smile, closing the eyes, and pulling the hair. It was fairly simple, except for the part where I had to adjust bezier anchor points to the mouse position, and putting them within a certain range that would limit the mouse move the bezier curve to the edge of the canvas. I did that in order not to let people distort the face with an interactive function I put in, as I mean the function to be interactive, not intended to ruin the initial image 🙂

This is the code I used:

int w;
int h;
int xcoord = 300;
int ycoord = 550;
int haircoordx1 = 390;
int haircoordy1 = 10;
int haircoordx2 =250;
int haircoordy2 = 50;
int haircoordx3 = 300;
int haircoordy3 = 20;
int haircoordx4 = 200;
int haircoordy4 = 100;
int haircoordx5 = 500;
int haircoordy5 = 50;
int haircoordx6 = 400;
int haircoordy6 = 50;
void setup(){
size(600,600);
smooth();
h=height;
w=width;
}

void draw(){
background(30,30,255);

//ears

noStroke();
fill(255, 225, 190);
ellipse(150, h/2, 50, 70);
ellipse(450, h/2, 50, 70);
fill(255, 170, 150);
ellipse(150, h/2, 30, 45);
ellipse(450, h/2, 30, 45);


//face
noStroke();
fill(255, 225, 190);
ellipse(w/2,h/2, 300,350);


//mouth
stroke(255,170,150);
strokeWeight(5);


if(mousePressed && mouseX<400 && mouseX>200 && mouseY>300 && mouseY<550){
bezier(200, 330, 200,330, mouseX, mouseY, 400, 330);
xcoord = mouseX;
ycoord = mouseY;
}

else {
bezier(200, 330, 200,330, xcoord, ycoord, 400, 330);
}

//eyes
noStroke();
fill(255, 255, 255);
ellipse(240, 250, 70, 80);
ellipse(350, 250, 70, 80);
fill(122,150,255);
ellipse(240, 250, 50, 60);
ellipse(350, 250, 50, 60);
fill(0);
ellipse(240, 250, 20, 30);
ellipse(350, 250, 20, 30);


//eyes close
if(mousePressed && mouseX > 205 && mouseX < 385 && mouseY>210 && mouseY<290)
{

noStroke();
fill(255, 225, 190);
ellipse(240, 250, 72, 82);
ellipse(350, 250, 72, 82);
strokeWeight(10);
stroke(0);
noFill();
bezier(205, 260, 205, 305, 275, 305, 275, 260);
bezier(315, 260, 315, 305, 385, 305, 385, 260);

}
//nose
strokeWeight(5);
stroke(255, 170, 150);
noFill();
bezier(290, 330, 305, 345, 325, 325, 310, 310);


//hair

fill(255,240,0);
stroke(255,200,0);
if(mousePressed && mouseX<500 && mouseX>120 && mouseY>50 && mouseY<190){
bezier(260, 130, mouseX, mouseY, 300, 1, 320, 130);
bezier(190, 190, mouseX, mouseY, 10, 80, 280, 130);
bezier(200, 170, mouseX, mouseY, 180, 50, 320, 130);
bezier(240, 150, mouseX, mouseY, 120, 50, 280, 130);
bezier(300, 150, mouseX, mouseY, 400, 50, 400, 170);
bezier(270, 140, mouseX, mouseY, 300, 50, 380, 160);
haircoordx1 = mouseX;
haircoordy1 = mouseY;
haircoordx2 = mouseX;
haircoordy2 = mouseY;
haircoordx3 = mouseX;
haircoordy3 = mouseY;
haircoordx4 = mouseX;
haircoordy4 = mouseY;
haircoordx5 = mouseX;
haircoordy5 = mouseY;
haircoordx6 = mouseX;
haircoordy6 = mouseY;
}

else{
bezier(260, 130, haircoordx1, haircoordy1, 300, 1, 320, 130);
bezier(190, 190, haircoordx2, haircoordy2, 10, 80, 280, 130);
bezier(200, 170, haircoordx3, haircoordy3, 180, 50, 320, 130);
bezier(240, 150, haircoordx4, haircoordy4, 120, 50, 280, 130);
bezier(300, 150, haircoordx5, haircoordy5, 400, 50, 400, 170);
bezier(270, 140, haircoordx6, haircoordy6, 300, 50, 380, 160);}
//fill(255,240,0);
//stroke(255,200,0);
//bezier(260, 130, 390, 10, 300, 1, 320, 130);
//bezier(190, 190, 250, 50, 10, 80, 280, 130);
//bezier(200, 170, 300, 20, 180, 50, 320, 130);
//bezier(240, 150, 200, 100, 120, 50, 280, 130);
//bezier(300, 150, 500, 50, 400, 50, 400, 170);
//bezier(270, 140, 400, 50, 300, 50, 380, 160);
}

On “Chance Operations” and “Her Code Got Humans on the Moon”

I enjoyed watching the video of Casey Reas’ talk on chaos in art for the most part, with the exception for his critique of ordered art. I found most of the examples of computer-generated chaotic art very interesting, especially since most of them probably were/could be done using Processing, which is exciting, since I could try doing something alike.

However, even critiquing ordered art, Reas mentions the “idea to use a rational machine to do something unexpected”. And I found it somewhat controversial, since a machine is rational, therefore it can be seen as a form of ordered art, since it takes a lot of effort and ordered operations to make a machine work (maybe I’m saying something stupid here, I just felt like that can be a perspective on what a machine is, and whether a machine can be considered a piece of art or not).

 

As for the “Her Code Got Humans on the Moon” article, when I first opened the page, I did was slightly confused by the picture. The title of the article seemed very inspirational, but the image you first see when you open the article is of a woman (supposed the woman who the article is about) with an upset look on her face. And if she is the one who “Got Humans on the Moon”, why does she have that facial expression?

Anyways, despite that picture, I found this article very interesting and very personal. I though it was less focused on the code, somewhat about the process of making it, and totally about the story of Margaret Hamilton. About her life, her husband, how she got into programming at MIT, how she was so into programming to the extent that she took her little daughter with her to work, and while a 4-year-old girl slept on the floor her mother “programmed away”. And, most importantly, about how she persisted in a male-dominated diversity-challenged industry.

I really enjoyed reading the article and wish there was more about the stress and the process both Margaret and the crew went through when fixing mistakes after Apollo 8 was successfully launched, especially when launching P01 program has wiped our all the navigation data and the Apollo computer wouldn’t be able to figure out how to get the astronauts home. I would really love to know more about what they had to face when coming up with a plan to return Apollo 8 astronauts back home.