Week 11: Serial Communication (Reading + Final Concept)

Reading Response:

I resonate with the reading’s premise that disability design must evolve from mere practicality to an embrace of fashion and artistic expression. This shift not only empowers users but enables them to sculpt their identities—both in how they see themselves and how they are seen by others—through distinctive, personalized devices. Take eyewear as a poignant illustration of this concept: its triumph lies in the diversity of choices, such as an array of frame styles that resonate culturally, enabling individuals to exude confidence rather than embarrassment. In the same vein, Mullins’ prosthetic designs highlight how aesthetics can harmonize with personal flair, bolstering self-worth and enhancing social engagement, much like the way we choose our attire or adorn ourselves with jewelry.

To further this dialogue, I suggest harnessing innovative, interactive design tools like p5.js to create dynamic platforms where users can tailor assistive devices in real-time. By allowing them to select shapes, hues, and materials that echo their personal tastes and lifestyle choices, we align with the reading’s call for user autonomy. This transforms design into a participatory experience where individuals take an active role in shaping the aesthetics and functionality of their devices, akin to selecting outfits that express their unique style. Such tools have the potential to democratize the design process, making it accessible and inclusive while cultivating a culture that celebrates disability as a vibrant expression of individuality.

Moreover, this approach tackles the reading’s concerns about universal design by emphasizing personalized solutions. By incorporating sensor-driven inputs like gesture or voice controls, these platforms can cater to a broad spectrum of abilities, reflecting the user-friendly elegance reminiscent of the iPod interface. This not only fulfills the reading’s vision of design as an act of empowerment but also positions technology as a dynamic intersection of art, fashion, and disability, resulting in devices that are not only functional and beautiful but also deeply personal.

Final Project Preliminary Concept:

 

My concept combines generative music and art with arduino through a new type of sensor (Heart Rate Sensor). This would connect to the radial artery on the wrist, and the user’s heart rate will be sent to arduino and then through a serial connection to p5.js in real-time. p5.js will have a pre-defined set of musical nodes and visual graphics which will respond to the user’s heart rate, this is effectively a visual data representation of BPM.

 

The output then is the generative artwork (in synch and contributing to the experimental generated music). The experience would last 2 minutes and the user’s input to change the visuals and music.

I also want to incorporate 3 midi style touch sensors which facilitate the person’s interaction with my project. To make them intuitive , I will place them vertically (left for back, middle for pausing/continuing, right to go forward) which will allow the user to filter through different algorithms of how the musical nodes and visual representations are produced.

 

Week 11: Serial Communication

Arduino and p5.js:

Zayed and Zein

Exercise 1: Moving an Ellipse with One Sensor

Arduino Code:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
void setup(){
Serial.begin(9600);
}
void loop(){
int pot = analogRead(A0);
// Less responsive: smaller output range
int xPos = map(pot, 0, 1023, 100, 300);
Serial.println(xPos);
delay(100); // longer delay = slower updates
}
void setup(){ Serial.begin(9600); } void loop(){ int pot = analogRead(A0); // Less responsive: smaller output range int xPos = map(pot, 0, 1023, 100, 300); Serial.println(xPos); delay(100); // longer delay = slower updates }
void setup(){
  Serial.begin(9600);
}

void loop(){
  int pot = analogRead(A0);
  // Less responsive: smaller output range
  int xPos = map(pot, 0, 1023, 100, 300); 
  Serial.println(xPos);
  delay(100);  // longer delay = slower updates
}

 

Challenges:

It was difficult to make the ball move gradually, this was an issue with the p5.js sketch and we added a smoothing factor to make the movement more ideal.

Video:

Exercise 2:

Arduino Code:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// Arduino: LED brightness via Serial input
const int ledPin = 6;
const unsigned long BAUD = 9600;
void setup() {
Serial.begin(BAUD);
while (!Serial) ; // wait for Serial Monitor
pinMode(ledPin, OUTPUT);
Serial.println("LED Brightness Control");
Serial.println("Send a number 0–255, then <Enter>:");
}
void loop() {
// only proceed if we have a full line
if (Serial.available()) {
String line = Serial.readStringUntil('\n');
line.trim(); // remove whitespace
if (line.length() > 0) {
int b = line.toInt();
b = constrain(b, 0, 255);
analogWrite(ledPin, b);
Serial.print("▶ Brightness set to ");
Serial.println(b);
}
}
}
// Fade smoothly to a target brightness
void fadeTo(int target, int speed = 5, int delayMs = 10) {
int curr = 0;
// read current duty by trial (not perfect, but illustrates the idea)
for (int i = 0; i < 256; i++) {
analogWrite(ledPin, i);
if (i == target) {
curr = i;
break;
}
}
while (curr != target) {
curr += (target > curr) ? speed : -speed;
curr = constrain(curr, 0, 255);
analogWrite(ledPin, curr);
delay(delayMs);
}
}
// Arduino: LED brightness via Serial input const int ledPin = 6; const unsigned long BAUD = 9600; void setup() { Serial.begin(BAUD); while (!Serial) ; // wait for Serial Monitor pinMode(ledPin, OUTPUT); Serial.println("LED Brightness Control"); Serial.println("Send a number 0–255, then <Enter>:"); } void loop() { // only proceed if we have a full line if (Serial.available()) { String line = Serial.readStringUntil('\n'); line.trim(); // remove whitespace if (line.length() > 0) { int b = line.toInt(); b = constrain(b, 0, 255); analogWrite(ledPin, b); Serial.print("▶ Brightness set to "); Serial.println(b); } } } // Fade smoothly to a target brightness void fadeTo(int target, int speed = 5, int delayMs = 10) { int curr = 0; // read current duty by trial (not perfect, but illustrates the idea) for (int i = 0; i < 256; i++) { analogWrite(ledPin, i); if (i == target) { curr = i; break; } } while (curr != target) { curr += (target > curr) ? speed : -speed; curr = constrain(curr, 0, 255); analogWrite(ledPin, curr); delay(delayMs); } }
// Arduino: LED brightness via Serial input
const int ledPin = 6; 
const unsigned long BAUD = 9600;

void setup() {
  Serial.begin(BAUD);
  while (!Serial) ;        // wait for Serial Monitor
  pinMode(ledPin, OUTPUT);
  Serial.println("LED Brightness Control");
  Serial.println("Send a number 0–255, then <Enter>:");
}

void loop() {
  // only proceed if we have a full line
  if (Serial.available()) {
    String line = Serial.readStringUntil('\n');
    line.trim();           // remove whitespace

    if (line.length() > 0) {
      int b = line.toInt();  
      b = constrain(b, 0, 255);

      analogWrite(ledPin, b);
      Serial.print("▶ Brightness set to ");
      Serial.println(b);
    }
  }
}

// Fade smoothly to a target brightness
void fadeTo(int target, int speed = 5, int delayMs = 10) {
  int curr = 0;
  // read current duty by trial (not perfect, but illustrates the idea)
  for (int i = 0; i < 256; i++) {
    analogWrite(ledPin, i);
    if (i == target) {
      curr = i;
      break;
    }
  }

  while (curr != target) {
    curr += (target > curr) ? speed : -speed;
    curr = constrain(curr, 0, 255);
    analogWrite(ledPin, curr);
    delay(delayMs);
  }
}

 

Challenges:

For this exercise the challenges were 1: making the gradient slider animation look intuitive for functionality, and we spent a lot of time debating on whether a rainbow slider was cool or not. We decided that it was.

 

 

Exercise 3:

For this exercise we decided to build on the example provided and improve the physics of the wind as well as the animation of the ball itself. To keep track of the sensor values and ensure we are receiving consistent results, we included variables to track the potentiometer, bounce (1 results in LED high, 0 results in LED low) and the mass is just an added feature.

Challenges:

The issues we faced were plenty, including making the wind seem more realistic and making the interaction smoother. The first approach to solve this challenge were to implement debounce to discard those operations that would occur too closely during the runtime interval. We also had to be particularly careful about how often the p5.js asks for the potentiometer reading, considering the frequency of the frames displaying the visual text and the frequency of the potentiometer reading.

Arduino Code:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const int LED_PIN = 9; // LED pin
const int P_PIN = A0; // Potentiometer pin
void setup() {
Serial.begin(9600);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW); // Initial LED state
// Handshake: Wait for p5.js to send a start signal
while (Serial.available() <= 0) {
Serial.println("INIT"); // Send "INIT" until p5.js responds
delay(100); // Avoid flooding
}
}
void loop() {
// Check for incoming serial data
if (Serial.available() > 0) {
char incoming = Serial.read(); // Read a single character
if (incoming == 'B') { // p5.js sends 'B' followed by 0 or 1
while (Serial.available() <= 0); // Wait for the value
int bounce = Serial.read() - '0'; // Convert char '0' or '1' to int
digitalWrite(LED_PIN, bounce); // Set LED (0 = LOW, 1 = HIGH)
}
else if (incoming == 'R') { // p5.js requests potentiometer reading
int potValue = analogRead(P_PIN); // Read potentiometer (0-1023)
Serial.println(potValue); // Send value
}
// Clear any remaining buffer (e.g., newlines)
while (Serial.available() > 0) {
Serial.read();
}
}
}
const int LED_PIN = 9; // LED pin const int P_PIN = A0; // Potentiometer pin void setup() { Serial.begin(9600); pinMode(LED_PIN, OUTPUT); digitalWrite(LED_PIN, LOW); // Initial LED state // Handshake: Wait for p5.js to send a start signal while (Serial.available() <= 0) { Serial.println("INIT"); // Send "INIT" until p5.js responds delay(100); // Avoid flooding } } void loop() { // Check for incoming serial data if (Serial.available() > 0) { char incoming = Serial.read(); // Read a single character if (incoming == 'B') { // p5.js sends 'B' followed by 0 or 1 while (Serial.available() <= 0); // Wait for the value int bounce = Serial.read() - '0'; // Convert char '0' or '1' to int digitalWrite(LED_PIN, bounce); // Set LED (0 = LOW, 1 = HIGH) } else if (incoming == 'R') { // p5.js requests potentiometer reading int potValue = analogRead(P_PIN); // Read potentiometer (0-1023) Serial.println(potValue); // Send value } // Clear any remaining buffer (e.g., newlines) while (Serial.available() > 0) { Serial.read(); } } }
const int LED_PIN = 9;    // LED pin
const int P_PIN = A0;     // Potentiometer pin

void setup() {
  Serial.begin(9600);
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW); // Initial LED state

  // Handshake: Wait for p5.js to send a start signal
  while (Serial.available() <= 0) {
    Serial.println("INIT"); // Send "INIT" until p5.js responds
    delay(100);             // Avoid flooding
  }
}

void loop() {
  // Check for incoming serial data
  if (Serial.available() > 0) {
    char incoming = Serial.read(); // Read a single character
    if (incoming == 'B') {        // p5.js sends 'B' followed by 0 or 1
      while (Serial.available() <= 0); // Wait for the value
      int bounce = Serial.read() - '0'; // Convert char '0' or '1' to int
      digitalWrite(LED_PIN, bounce);    // Set LED (0 = LOW, 1 = HIGH)
    }
    else if (incoming == 'R') {       // p5.js requests potentiometer reading
      int potValue = analogRead(P_PIN); // Read potentiometer (0-1023)
      Serial.println(potValue);         // Send value
    }
    // Clear any remaining buffer (e.g., newlines)
    while (Serial.available() > 0) {
      Serial.read();
    }
  }
}

 

ONE VIDEO FOR ALL 3 EXERCISES IN RESPECTIVE ORDER

 

Week 10 Reading Response

Whilst reading a Brief Rant on The Future of Interactive Design I thought of the essential framework as controversial. Firstly the assumption that humans require tactile feedback and attributing the cause of high numbers of nerve endings in fingertips only reinforces concepts of evolutionary outcomes. Yes, by evolution we do have the most nerve endings in the tips of our fingers, that does not contradict the future of interactive design removing the necessity for human touch. Unlike Humans 5000 years ago, we will not need more tactile feedback in modern society. I prefer completely removing human touch when needed. We should not have to turn a light switch on, that should be automated. We should not have to turn the AC on through an outdated retro interface as well. Several things are way better without human touch. If we take the framework further, then we should rethink even current technology and not just future technology.

Whilst Reading Responses: A Brief Rant on the Future of Interaction Design, I realized that the author completely dismissed the reasoning behind the framework under the guise that it is just a rant. Perhaps the examples of solutions such as haptic holography and nanobot assemblies is conceptually useful, but not without a further explanation of the framework. Overall I disliked this reading for the above reasons but I found the response to provide some helpful

Week 10 Instrument (Z Z)

Concept:

We decided to use two digital switches (push switches) to play two different notes, with the LDR effectively acting as an analogue volume adjustment mechanism. The video demonstrates how this feedback from the LDR changes the volume, and if you focus when the light intensity pointed towards the LDR is decreased, there is a very small noise.

Demo (Circuit):

Demo (Video):

Arduino Code:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
// Define pins for the buttons and the speaker
int btnOnePin = 2;
int btnTwoPin = 3;
int speakerPin = 10;
void setup() {
// Initialize both button pins as inputs with built-in pull-up resistors
pinMode(btnOnePin, INPUT_PULLUP);
pinMode(btnTwoPin, INPUT_PULLUP);
// Configure the speaker pin as an output
pinMode(speakerPin, OUTPUT);
}
void loop() {
// Check if the first button is pressed
if (digitalRead(btnOnePin) == LOW) {
tone(speakerPin, 262); // Play a tone at 262 Hz
}
// Check if the second button is pressed
else if (digitalRead(btnTwoPin) == LOW) {
tone(speakerPin, 530); // Play a tone at 530 Hz
}
// No button is pressed
else {
noTone(speakerPin); // Turn off the speaker
}
}
// Define pins for the buttons and the speaker int btnOnePin = 2; int btnTwoPin = 3; int speakerPin = 10; void setup() { // Initialize both button pins as inputs with built-in pull-up resistors pinMode(btnOnePin, INPUT_PULLUP); pinMode(btnTwoPin, INPUT_PULLUP); // Configure the speaker pin as an output pinMode(speakerPin, OUTPUT); } void loop() { // Check if the first button is pressed if (digitalRead(btnOnePin) == LOW) { tone(speakerPin, 262); // Play a tone at 262 Hz } // Check if the second button is pressed else if (digitalRead(btnTwoPin) == LOW) { tone(speakerPin, 530); // Play a tone at 530 Hz } // No button is pressed else { noTone(speakerPin); // Turn off the speaker } }
// Define pins for the buttons and the speaker
int btnOnePin = 2;
int btnTwoPin = 3;
int speakerPin = 10;

void setup() {
// Initialize both button pins as inputs with built-in pull-up resistors
pinMode(btnOnePin, INPUT_PULLUP);
pinMode(btnTwoPin, INPUT_PULLUP);

// Configure the speaker pin as an output
pinMode(speakerPin, OUTPUT);
}

void loop() {
// Check if the first button is pressed
if (digitalRead(btnOnePin) == LOW) {
tone(speakerPin, 262); // Play a tone at 262 Hz
}
// Check if the second button is pressed
else if (digitalRead(btnTwoPin) == LOW) {
tone(speakerPin, 530); // Play a tone at 530 Hz
}
// No button is pressed
else {
noTone(speakerPin); // Turn off the speaker
}
}

Challenges:

The initial concept started out with a Light Dependent Resistor and the Piezo speaker/buzzer. We faced issues as the readings from the LDR did not behave as expected, there was an issue with the  sound produced, and the change in music produced was not adequate.

We also faced challenges with the programming, as the noise production was inconsistent. We fixed this by adjusting the mapping of the notes to produce more distinct frequencies for each independent push button (red vs yellow). for 262 and 540 Hz respectively.

Done by: Zayed Alsuwaidi (za2256) and Zein Mukhanov (zm2199)

Week 9 Reading

Physical Computing’s Greatest Hits(and misses):

I am particularly interested in theremin-like instruments, gloves, and video mirrors. I believe that these three examples of physical compute (in harmony and implemented altogether) have the potential to change the representation of the lived physical world. To extend these concepts, I thought of methods of taking input from the environment. These could be the sounds of birds, wind, the temperature, the perceived luminosity of the sun (at the place of input), as well as small movements (either from the wind or sound). Combining these inputs together and processing them, an algorithm can generate certain sounds. This music then comes together with video mirroring and processing, where the composed music dictates a new perception of the scenery / place of physical compute. I found value in the limitations explained in this reading, as the meaning attributed to the actions performed by physical compute and the method of input is key towards making a truly interactive and valuable physical compute experience. To address those, if I were to use the previous example I would think of a way (through testing) by seeing how people received the experience, and how to fine tune it. Things within this line are intuitive experience (ease of use), meaning from interaction, and ability to interact for a sufficient amount of time whilst maintaining meaningful interaction.

 

 

Making Interactive Art: Set the Stage, Then Shut Up and Listen.

 

This reading builds on ‘Physical Computing’s Greatest Hits (and misses)’ by explaining the intricacies of limitations in the design of interactive art. The concept of intuition in interaction is therefore represented as the ability of a person to understand and find meaning in the interaction with the artwork. I found the organization and flow of this reading very helpful because it helped re-iterate and solidify the concepts of listening (by both the artist and the audience) and the statement made by the artist. The statement (as I understand it) is not a standalone expression, but rather an interactive conversation, that aims to have a meaningful exchange of actions and expressions by both the artist and his/her art and the audience.

Week 9 Analogue + Digital

For this assignment I started by connecting one push-switch to one LED, and I used that as the basis of my draft schematic and work. I then re-drew my schematic and included the photoresistor (LDR) sensor. Here I faced challenges in both the hardware and software, specifically within the timing of the code. Initially, the light would stay on for too long, and I rechecked the timing of my Arduino code and adjusted it to be in a shorter high (on) state.

 

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const int ldrPin = A2; // LDR connected to analog pin A0
const int buttonPin = 2; // Push button connected to digital pin 2
const int ledAnalogPin = 10; // PWM LED pin
const int ledDigitalPin = 11; // On/off LED pin
void setup() {
pinMode(buttonPin, INPUT);
pinMode(ledAnalogPin, OUTPUT);
pinMode(ledDigitalPin, OUTPUT);
Serial.begin(9600);
}
void loop() {
int ldrValue = analogRead(ldrPin);
int brightness = map(ldrValue, 0, 1023, 255, 0); // This adjusts the brightness
analogWrite(ledAnalogPin, brightness);
int buttonState = digitalRead(buttonPin);
if (buttonState == HIGH) {
digitalWrite(ledDigitalPin, HIGH);
} else {
digitalWrite(ledDigitalPin, LOW);
}
delay(100);
}
const int ldrPin = A2; // LDR connected to analog pin A0 const int buttonPin = 2; // Push button connected to digital pin 2 const int ledAnalogPin = 10; // PWM LED pin const int ledDigitalPin = 11; // On/off LED pin void setup() { pinMode(buttonPin, INPUT); pinMode(ledAnalogPin, OUTPUT); pinMode(ledDigitalPin, OUTPUT); Serial.begin(9600); } void loop() { int ldrValue = analogRead(ldrPin); int brightness = map(ldrValue, 0, 1023, 255, 0); // This adjusts the brightness analogWrite(ledAnalogPin, brightness); int buttonState = digitalRead(buttonPin); if (buttonState == HIGH) { digitalWrite(ledDigitalPin, HIGH); } else { digitalWrite(ledDigitalPin, LOW); } delay(100); }
const int ldrPin = A2;              // LDR connected to analog pin A0
const int buttonPin = 2;            // Push button connected to digital pin 2
const int ledAnalogPin = 10;         // PWM LED pin
const int ledDigitalPin = 11;       // On/off LED pin

void setup() {
  pinMode(buttonPin, INPUT);
  pinMode(ledAnalogPin, OUTPUT);
  pinMode(ledDigitalPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  int ldrValue = analogRead(ldrPin);        
  int brightness = map(ldrValue, 0, 1023, 255, 0);  // This adjusts the brightness

  analogWrite(ledAnalogPin, brightness);

  int buttonState = digitalRead(buttonPin);

  if (buttonState == HIGH) {
    digitalWrite(ledDigitalPin, HIGH);
  } else {
    digitalWrite(ledDigitalPin, LOW);
  }

  delay(100);
}

For the schematic I followed the fritz schematics format:

 

 

 

Video Demonstration:

https://drive.google.com/file/d/1kAYD6v6C86fbftmEfKM37j6jtumxzql1/view?usp=sharing

 

Some challenges I would still like to overcome and solve better are the output of the LED from the photoresistor (LDR) sensor, as I wanted the output to be stronger. I will address these issues by having a closer adherence to the schematic and a better methodology of implementing the changes to the circuit (thinking of what would happen if I added a certain connection or made a specific change before making that change).

 

Week 8 – Reading Response

The key aspects I noticed in Norman’s Emotion & Design: Attractive Things Work Better were the negative and positive valences, where affects have great implications towards how humans interact with and therefore achieve productivity in the use of everyday things (or better described as any tool).  I want to expand this concept and interpret this very affect and behavior to not only items such as teapots or any tool alike, but for the lived environment. I tend to agree with this framework, to the extent that I am fully confident that you will produce different work and express different levels of creativity through a lived metaphysical conversation in realtime. To highlight this with an example, I prefer sitting in a natural setting (garden, desert, beach) with no seating or furniture when I try to solve an issue or I want to focus on my creative aspects. After all, nature inspired half of the things we know today.

After Reading McMillan’s Her Code Got Humans on The Moon – And Invented Software itself I connected the storyline and experience of Margaret to that of Ada Lovelace. After all, they are both pioneers in their own respect. Lovelace developed the first known and documented computer program and directly worked with and gave more influence to Charles Babbage. Margaret reflects this pioneering and ingenious creativity by working on highly-technical work on software engineering through punch-cards and simulation. During the 1960s, this development process required a great deal of manual human intervention. I am inspired by her motives and ambition, and I wonder how many people she inspires today. Margaret’s work and achievements resonate within me today, and I believe she deserves even more credit, just like how Lovelace has a GPU architecture named after her.

Unusual Switch

For the Unusual switch I decided to continue the concepts I was exploring within the first seven weeks of interactive and generative art using p5.js. I call this switch the “Handcuff” switch. A general rule of thumb tells you “gif > any other file format” so here is my demonstration whilst also trying to re-enact how the person wearing the handcuffs would feel

 

 

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const int switchPin = 2;
const int ledPin = 13;
bool handcuffsLocked = false; // tracks if handcuffs are on or off
void setup() {
pinMode(switchPin, INPUT_PULLUP); // Internal pull-up resistor
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
}
void loop() {
int switchState = digitalRead(switchPin);
if (switchState == LOW && !handcuffsLocked) {
handcuffsLocked = true; // lock the handcuffs
digitalWrite(ledPin, HIGH);
Serial.println("Handcuffs ON");
}
if (switchState == HIGH && handcuffsLocked) {
handcuffsLocked = false; // unlock the handcuffs
digitalWrite(ledPin, LOW);
Serial.println("Handcuffs OFF");
}
delay(100);
}
const int switchPin = 2; const int ledPin = 13; bool handcuffsLocked = false; // tracks if handcuffs are on or off void setup() { pinMode(switchPin, INPUT_PULLUP); // Internal pull-up resistor pinMode(ledPin, OUTPUT); Serial.begin(9600); } void loop() { int switchState = digitalRead(switchPin); if (switchState == LOW && !handcuffsLocked) { handcuffsLocked = true; // lock the handcuffs digitalWrite(ledPin, HIGH); Serial.println("Handcuffs ON"); } if (switchState == HIGH && handcuffsLocked) { handcuffsLocked = false; // unlock the handcuffs digitalWrite(ledPin, LOW); Serial.println("Handcuffs OFF"); } delay(100); }
const int switchPin = 2;
const int ledPin = 13;
bool handcuffsLocked = false; // tracks if handcuffs are on or off

void setup() {
  pinMode(switchPin, INPUT_PULLUP);  // Internal pull-up resistor
  pinMode(ledPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  int switchState = digitalRead(switchPin);

  if (switchState == LOW && !handcuffsLocked) {
    handcuffsLocked = true; // lock the handcuffs
    digitalWrite(ledPin, HIGH);
    Serial.println("Handcuffs ON");
  }

  if (switchState == HIGH && handcuffsLocked) {
    handcuffsLocked = false; // unlock the handcuffs
    digitalWrite(ledPin, LOW);
    Serial.println("Handcuffs OFF");
  }

  delay(100);
}

I designed this basic code to detect and track when the handcuffs are unlocked. Mainly, it is a conditional that keeps track of the states (on) or (off).

 

Midterm Project Zayed Alsuwaidi

To bring the initial concept of Russian Roulette to life, I decided to use generative AI for the images. I faced several issues sketching the artwork myself on my iPad, and I wanted a surreal realism and impact from the art. The images are therefore generated by DALL-E.

Here are the Game Mechanics:
Players

  • Player: Player starts with 100 health, the bar is at the top-left (50, 50).
  • Opponent: Also 100 health, bar is at top-right (650, 50).
  • Health: Drops by 50 when hit. Game’s over if either of us hits 0.

Gun

  • It’s a 6-shot revolver (shotsFired tracks how many times it’s fired).
  • The chamber’s random—loaded or empty (isLoaded)—and resets on reload.
  • Every shot counts toward the 6, whether it’s a bang or a click.

States

  • “between”: Whether the choice is to shoot (S) or pass (N).
  • “playerTurn”: Player shoots, showing playergun.gif.
  • “opponentTurn”: Person shoots, showing persongun.gif.
  • “reload”: After 6 shots, we reload, and they shoot at me!
  • “gameOver”: One is down; hit R to restart.

My Journey Building It
I started with the basics—getting the images and health bars up. That was smooth, but then I hit a wall with sounds. I added gunshot.m4a for hits, and it worked sometimes, but other times—nothing. That was frustrating. Turns out, browsers block audio until you interact with the page, so I had to trigger it after a key press. Even then, emptyclick.m4a wouldn’t play right when the opponent fired an empty shot. I kept seeing “sound not loaded” in the console and realized the timing was off with setTimeout. I fixed it by storing the shot result in a variable and making sure the sound played every time—loaded or not. Adding the reload mechanic was tricky too; I wanted the opponent to shoot during it, but keeping the flow consistent took some trial and error.

Image:

The image (From DALL-E) I used to depict the person wearing a silk mask covering his facial features and creating a feeling of suspense:

 

 

Gameplay Flow

  1. Start: Game kicks off in “between” with nogun.gif.
  2. My Turn:
    • S: I shoot.
      • Loaded: gunshot.m4a, light flashes, opponent loses 50 health.
      • Empty: emptyclick.m4a, no damage.
    • N: Pass to the opponent.
  3. Opponent’s Turn: They always shoot now (I made it consistent!).
    • Loaded: gunshot.m4a, light flashes, I lose 50 health.
    • Empty: emptyclick.m4a, no damage.
  4. Reload: After 6 shots:
    • Switches to “reload”, shows nogun.gif.
    • Gun resets with a random chamber.
    • Opponent shoots at me:
      • Loaded: gunshot.m4a, light flash, I take damage.
      • Empty: emptyclick.m4a, I’m safe.
    • Back to “between”.
  5. Game Over: When me or the opponent hits 0 health.
    • “Game Over! Press ‘R’ to restart” shows up, and R starts it over.

Controls

  • S: I shoot.
  • N: Pass to the opponent.
  • R: Restart when it’s over.

Visuals

  • Canvas: 800×600—big enough to see everything.
  • Images: Centered at (400, 300), 300×300 pixels.
  • Health Bars: Red base (100 wide), green shrinks as health drops.
  • Light Effect: A cool yellow-white flash when a shot lands—fades out fast.
  • Instructions: Text at the bottom tells me what’s up.

Audio

  • Gunshot: gunshot.m4a for a hit—loud and punchy.
  • Empty Click: emptyclick.m4a for a miss—subtle but tense.
  • Volume: Set both to 0.5 so my ears don’t hate me.

Overcoming Challenges
The sounds were my biggest headache. At first, gunshot.m4a only played after I clicked something—browser rules, ugh. I fixed that by tying it to key presses. Then emptyclick.m4a kept skipping when the opponent shot an empty chamber. I dug into the code and saw the random shoot chance was messing with the timing. I simplified it—stored the shot result, made the opponent shoot every time, and checked isLoaded() right before playing the sound. Now it’s rock-solid.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
this.state = "playerTurn";
this.currentImg = playerGunImg;
let shotFired = this.gun.shoot();
if (shotFired) {
if (gunshotSound.isLoaded()) {
gunshotSound.play();
}
this.opponent.takeDamage();
this.flashAlpha = 255;
} else {
if (emptyClickSound.isLoaded()) {
emptyClickSound.play();
}
}
setTimeout(() => this.checkReloadOrNext(), 1000);
}
opponentTurn() {
this.state = "opponentTurn";
this.state = "playerTurn"; this.currentImg = playerGunImg; let shotFired = this.gun.shoot(); if (shotFired) { if (gunshotSound.isLoaded()) { gunshotSound.play(); } this.opponent.takeDamage(); this.flashAlpha = 255; } else { if (emptyClickSound.isLoaded()) { emptyClickSound.play(); } } setTimeout(() => this.checkReloadOrNext(), 1000); } opponentTurn() { this.state = "opponentTurn";
this.state = "playerTurn";
    this.currentImg = playerGunImg;
    let shotFired = this.gun.shoot();
    if (shotFired) {
      if (gunshotSound.isLoaded()) {
        gunshotSound.play();
      }
      this.opponent.takeDamage();
      this.flashAlpha = 255;
    } else {
      if (emptyClickSound.isLoaded()) {
        emptyClickSound.play();
      }
    }
    setTimeout(() => this.checkReloadOrNext(), 1000);
  }

  opponentTurn() {
    this.state = "opponentTurn";

 to show how I handled the opponent’s turn.

Key Gameplay Highlight

  • Reload Mechanic: The reload’s risky and cool—opponent gets a free shot!
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
setTimeout(() => {
let shotFired = this.gun.shoot();
if (shotFired) {
if (gunshotSound.isLoaded()) {
gunshotSound.play();
}
this.player.takeDamage();
this.flashAlpha = 255;
} else {
if (emptyClickSound.isLoaded()) {
emptyClickSound.play();
}
}
setTimeout(() => this.checkReloadOrNext(), 1000);
}, 1000);
}
checkReloadOrNext() {
if (this.gun.needsReload()) {
this.reloadGun();
} else {
this.nextRound();
}
}
setTimeout(() => { let shotFired = this.gun.shoot(); if (shotFired) { if (gunshotSound.isLoaded()) { gunshotSound.play(); } this.player.takeDamage(); this.flashAlpha = 255; } else { if (emptyClickSound.isLoaded()) { emptyClickSound.play(); } } setTimeout(() => this.checkReloadOrNext(), 1000); }, 1000); } checkReloadOrNext() { if (this.gun.needsReload()) { this.reloadGun(); } else { this.nextRound(); } }
setTimeout(() => {
     let shotFired = this.gun.shoot();
     if (shotFired) {
       if (gunshotSound.isLoaded()) {
         gunshotSound.play();
       }
       this.player.takeDamage();
       this.flashAlpha = 255;
     } else {
       if (emptyClickSound.isLoaded()) {
         emptyClickSound.play();
       }
     }
     setTimeout(() => this.checkReloadOrNext(), 1000);
   }, 1000);
 }

 checkReloadOrNext() {
   if (this.gun.needsReload()) {
     this.reloadGun();
   } else {
     this.nextRound();
   }
 }

 

 

 has the logic for resetting the gun and surviving that tense moment.

Technical Bits

  • Classes:
    • Player: Tracks my health and draws the bar.
    • Gun: Manages the 6-shot limit and random chamber.
    • Game: Runs the show—states, visuals, all of it.
  • p5.js Stuff:
    • preload(): Loads my assets.
    • setup(): Sets up the 800×600 canvas and sound volumes.
    • draw(): Keeps everything on screen.
    • keyPressed(): Listens for S, N, R.

Endgame
It’s over when me or the opponent’s health hits 0. I see “Game Over! Press ‘R’ to restart”, hit R, and it’s back to square one—health full, gun reset.

What’s Next?
Maybe I’ll add a manual reload key or a score counter. Also, I would re-design the game with different artwork to make it more immersive.

Here is a snippet of the state handling, which was key to ensuring less redundancy by preventing procedural programming of the game. Also, the states handle the logic, and this was key in establishing how the game runs i.e if there is an issue with the Game Class, the rest of the gameplay mechanics are directly impacted.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
class Game {
constructor() {
this.player = new Player("Player", 50, 50); // Player health bar at top-left
this.opponent = new Player("Opponent", 650, 50); // Opponent health bar at top-right
this.gun = new Gun();
this.state = "between"; // States: "between", "playerTurn", "opponentTurn", "reload", "gameOver"
this.currentImg = noGunImg; // Start with no gun image
this.flashAlpha = 0; // For light effect transparency
}
class Game { constructor() { this.player = new Player("Player", 50, 50); // Player health bar at top-left this.opponent = new Player("Opponent", 650, 50); // Opponent health bar at top-right this.gun = new Gun(); this.state = "between"; // States: "between", "playerTurn", "opponentTurn", "reload", "gameOver" this.currentImg = noGunImg; // Start with no gun image this.flashAlpha = 0; // For light effect transparency }
class Game {
  constructor() {
    this.player = new Player("Player", 50, 50); // Player health bar at top-left
    this.opponent = new Player("Opponent", 650, 50); // Opponent health bar at top-right
    this.gun = new Gun();
    this.state = "between"; // States: "between", "playerTurn", "opponentTurn", "reload", "gameOver"
    this.currentImg = noGunImg; // Start with no gun image
    this.flashAlpha = 0; // For light effect transparency
  }

 

 

Midterm Progress

Concept:

 

I thought of two main ideas when thinking of making a game, and to highlight  the previous themes of randomness I decided to make a gambling-oriented game. The first idea I thought of was to make a blackjack game but with Russian roulette stakes. The second idea was to just simulate a Russian roulette. I continued with the latter game because it is more intuitive and the game would be more feasible to implement in p5.js.

 

Design:

 

I drew a basic sketch due to simply show the gameplay features where the level progresses based on the health of the person interacting with the game. If it is feasible, I will also implement another health bar for the dealer, where the player can shoot the dealer as well. I am focusing on the gameplay elements more than the artwork, since the backdrop is intentionally designed to be gloomy and dark. (The filling in of the sketches also represents a vast darkness obscuring the dealer’s torso).

 

The lighting portrayed will also be minimal, and the dealer’s face is drawn in a way where the dealer is wearing a mask that obscures his facial features (essentially like a uniform nearly fully garment that wraps around his entire face, making the facial features appear opaque). I will improve the sketch and render the artwork in SVG.

 

 

Challenges:

The main challenges are producing the artwork with the goal of making the experience as fun as possible, as I might have to compromise on certain gameplay features. I also faced challenges creating the artwork in a correct SVG format, but I am continuing to research this in order to be able to finalize my methods in sketching and digitalizing the artwork. Another challenge is the animations, and to solve this challenge I will focus on 4-5 main animations, one where the dealer shoots a bullet, one where the dealer loads the gun away from the player’s perspective, and one where the dealer shoots a blank.

 

Reading Response:

 

Computer Vision differs from human vision in the sense that the processing of images and light is sub-consciously performed in human vision. On the other hand, algorithms allow computer vision to map light and images into mathematical equivalents in real-time within programs, subsequently allowing the image to be processed.

 

One such method is frame differencing, two frames are taken, where one frame represents a certain amount of light and a representation of the image in pixels, and the second frame represents a corresponding frame with changes in light and pixels. The differences , namely in color and luminosity, between these two frames is summed across all of the pixels’ locations. Similarly, detecting presence performs these same calculations of differences but with a reference of the background of the image instead of two similar frames.

 

I believe that computer vision’s capacity for tracking and surveillance creates possibilities with no bounds, where interactive artwork can take form through the medium of augmented reality, as well as the depiction of interactive media artwork simulating motion and pictures with life-like movement. As highlighted in the reading, this capacity is not limited by definition of the generalizability of computer vision algorithms, where no single algorithm is generalizable, and several different possible algorithms can work in harmony to create a digital interactive artwork.