Final Project Documentation

 Concept

For my final project, I wanted to build an installation that captures a piece of home: the experience of walking around a stupa in Nepal and spinning the prayer wheels. Growing up, I always loved the peaceful repetition of spinning each wheel, even before I fully understood their spiritual meaning. Over time, I learned that prayer wheels (Mani wheels) are believed to spread blessings and compassion every time they turn.

My goal was to translate that ritual into an interactive digital-physical artwork. The project consists of two main parts:

A physical prayer wheel that plays a sacred “Om” sound when spun.

A digitally illustrated stupa in p5.js, where different architectural sections light up on the physical stupa when touched.

Together, the two elements let the user explore both the ritualistic and symbolic aspects of the stupa.

How the Implementation Works
Interaction Design

The experience begins with the user spinning the prayer wheel. Instead of powering the wheel with a motor, I use the motor as a generator, when the wheel spins, it produces a small voltage. That voltage is read by the Arduino as a signal.

At the same time, the stupa illustration in p5.js acts like an interactive map. When the user touches different physical regions of the stupa (pinnacle, dome, Buddha’s eyes, mandala, etc.) in p5js then p5js sends signal to arudino and light up the parts in physical space.

The design relies on:

Discovery: Users figure out what is touch-sensitive by interacting.

Cultural symbolism: Each part of the stupa has meaning, and the lighting reveals that visually.

Multi-modal feedbackL Sound (prayer wheel), light (stupa), and animation (p5.js).

 

Arudino Code:

My Arduino reads four analog voltages from the prayer wheel motor and also listens for letters sent from p5.js. Each letter corresponds to a part of the stupa that should light up.

// ----- Analog input pins -----
const int volt2 = A2;   // Sensor or voltage input on A2
const int volt3 = A3;   // Sensor or voltage input on A3
const int volt4 = A4;   // Sensor or voltage input on A4
const int volt5 = A5;   // Sensor or voltage input on A5

// ----- Digital output pins (LEDs / relays / indicators) -----
const int pinnacle = 2;
const int thirteen = 3;
const int eye1 = 4;
const int eye2 = 5;
const int dome = 6;
const int mandala = 7;
const int flag = 8;

void setup() {
  // Configure analog input pins
  pinMode(volt2, INPUT);
  pinMode(volt3, INPUT);
  pinMode(volt4, INPUT);
  pinMode(volt5, INPUT);

  // Configure all digital output pins
  pinMode(pinnacle, OUTPUT);
  pinMode(thirteen, OUTPUT);
  pinMode(eye1, OUTPUT);
  pinMode(eye2, OUTPUT);
  pinMode(dome, OUTPUT);
  pinMode(mandala, OUTPUT);
  pinMode(flag, OUTPUT);

  // Start serial communication
  Serial.begin(9600);
}

void loop() {

  // ----- Read all analog inputs -----
  int a2 = analogRead(volt2);
  int a3 = analogRead(volt3);
  int a4 = analogRead(volt4);
  int a5 = analogRead(volt5);

  // ----- Send readings to the Serial Monitor (comma-separated) -----
  Serial.print(a2);
  Serial.print(",");
  Serial.print(a3);
  Serial.print(",");
  Serial.print(a4);
  Serial.print(",");
  Serial.print(a5);
  Serial.print("\n");

  delay(50); // Small delay for stable output



  // ----- Handle incoming serial commands -----
  while (Serial.available() > 0) {

    char message = Serial.read();   // Read one character command

    allOff();   // Always reset all outputs first



    // ----- Activate specific outputs based on incoming character -----
    if (message == 'p') {
      digitalWrite(pinnacle, HIGH);     // Turn on pinnacle
    }
    else if (message == 't') {
      digitalWrite(thirteen, HIGH);     // Turn on thirteen
    }
    else if (message == 'e') {          // "e" turns on both eyes
      digitalWrite(eye1, HIGH);
      digitalWrite(eye2, HIGH);
    }
    else if (message == 'd') {
      digitalWrite(dome, HIGH);         // Dome on
    }
    else if (message == 'm') {
      digitalWrite(mandala, HIGH);      // Mandala on
    }
    else if (message == 'f') {
      digitalWrite(flag, HIGH);         // Flag on
    }
    // Any other character is ignored
  }
}



// ----- Helper function: turn ALL outputs OFF -----
void allOff() {
  digitalWrite(pinnacle, LOW);
  digitalWrite(thirteen, LOW);
  digitalWrite(eye1, LOW);
  digitalWrite(eye2, LOW);
  digitalWrite(dome, LOW);
  digitalWrite(mandala, LOW);
  digitalWrite(flag, LOW);
}

Schematic of the Circuit

p5.js Code Description

The p5.js sketch does these things:

Draws multiple screens (welcome screen → prayer wheel → stupa)

Listens for sensor values sent from Arduino

Plays an “Om” sound only when the wheel spins

Sends letters (‘p’, ‘t’, ‘e’, ‘d’, ‘m’) back to Arduino to activate lights

Handles all on-screen interactions through mouse clicks

code:

// -------------------------------------------------------------
// GLOBAL VARIABLES
// -------------------------------------------------------------

let port;                 // Serial port object for Arduino communication
let button;               // Connect button
let open = false;         // Tracks whether the port is open
let trimvalue;            // Parsed Arduino sensor values
let screen = 1;           // Screen state controller
let sentCommand = false;  // Tracks if Arduino command is already sent
let soundPlaying = false; // Prevents OM sound from retriggering too fast


// -------------------------------------------------------------
// ASSET LOADING (Audio + Images)
// -------------------------------------------------------------
function preload() {

  /////////music////////
  om_sound = loadSound('om.mp3'); // sound from https://pixabay.com/music/search/om/

  // UI Images (all from canva.com as cited)
  welcomescreen = loadImage("startpage.png"); // image from canva.com
  screen2i = loadImage("screen2i.png");       // image from canva.com
  screen3i = loadImage("screen3i.png");       // image from canva.com
  screenpi = loadImage("screenpi.png");       // image from canva.com
  screenmi = loadImage("screenmi.png");       // image from canva.com
  screendi = loadImage("screendi.png");       // image from canva.com
  screenei = loadImage("screenei.png");       // image from canva.com
  screenti = loadImage("screenti.png");       // image from canva.com
}


// -------------------------------------------------------------
// SETUP FUNCTION — Runs once
// -------------------------------------------------------------
function setup() {

  createCanvas(400, 400);

  // Create serial port object (p5.js → Arduino communication bridge)
  port = createSerial();

  // Create connect button
  button = createButton("Connect to Arduino");
  button.position(width / 2 - 50, height / 2);
  button.mousePressed(openArduino); // Attach handler
}


// -------------------------------------------------------------
// OPEN ARDUINO SERIAL PORT
// -------------------------------------------------------------
function openArduino() {

  // If port is not already open, open it
  if (!port.opened()) {

    port.open(9600);   // Must match Arduino baud rate
    open = true;       // Mark port as open
    button.remove();   // Hide button after connecting
  }
}


// -------------------------------------------------------------
// MAIN DRAW LOOP — Runs continuously
// -------------------------------------------------------------
function draw() {

  // Only run UI + sound + sensor logic after port is open
  if (open == true) {

    // ---------------------------------------------------------
    // Screen Navigation
    // ---------------------------------------------------------
    if (screen == 1) {
      welcomescreenf();  // Start page
    }
    else if (screen == 2) {
      screen2f();
    }
    else if (screen == 3) {
      screen3f();
    }
    else if (screen == 4) {
      screenpf();
    }
    else if (screen == 7) {
      screend();
    }
    else if (screen == 8) {
      screenm();
    }

    // ---------------------------------------------------------
    // Read serial input (Arduino → p5.js)
    // ---------------------------------------------------------
    value = port.readUntil("\n");  // Read full sensor line
    port.clear();                  // Clear leftover buffer

    trimvalue = value.trim().split(",");  
    console.log(trimvalue);        // Print array of sensor values


    // ---------------------------------------------------------
    // SOUND TRIGGER LOGIC — OM sound plays when any sensor > 0
    // ---------------------------------------------------------
    if (!soundPlaying) {

      if (
        parseInt(trimvalue[0]) > 0 ||
        parseInt(trimvalue[1]) > 0 ||
        parseInt(trimvalue[2]) > 0 ||
        parseInt(trimvalue[3]) > 0
      ) {
        soundPlaying = true;  // Prevents double-trigger

        om_sound.play();      // Play OM sound

        // Reset lock after sound finishes
        om_sound.onended(() => {
          soundPlaying = false;
        });
      }
    }
  }

  // If port is closed → pause sound
  else {
    om_sound.pause();
  }
}


// -------------------------------------------------------------
// WELCOME SCREEN
// -------------------------------------------------------------
function welcomescreenf() {
  image(welcomescreen, 0, 0, 400, 400);
}


// -------------------------------------------------------------
// MOUSE-PRESSED HANDLER FOR SCREEN NAVIGATION + ARDUINO COMMANDS
// -------------------------------------------------------------
function mousePressed() {

  // ---------------- Screen 1 → Screen 2 -----------------
  if (screen == 1 &&
      mouseX >= 135 && mouseX <= 263 &&
      mouseY >= 354 && mouseY <= 371) {

    screen2f();
  }

  // ---------------- Screen 2 → Screen 3 -----------------
  else if (screen == 2 &&
           mouseX >= 120 && mouseX <= 346 &&
           mouseY >= 192 && mouseY <= 366) {

    screen3f();
  }

  // ---------------- Screen 3 Interactive Hotspots -----------------
  else if (screen == 3) {

    // Pinnacle (Top)
    if (mouseInside(192, 211, 117, 144)) {
      screenpf();  // Arduino: 'p'
    }

    // Thirteen tiers
    else if (mouseInside(185, 225, 147, 178)) {
      screent();   // Arduino: 't'
    }

    // Eyes
    else if (mouseInside(183, 244, 183, 195)) {
      screene();   // Arduino: 'e'
    }

    // Dome
    else if (mouseInside(124, 289, 194, 233)) {
      screend();   // Arduino: 'd'
    }

    // Mandala
    else if (mouseInside(0, 400, 240, 286)) {
      screen = 8;
      screenm();   // Arduino: 'm'
    }
  }

  // ---------------- Back Buttons for All Detail Screens -----------------

  else if (screen == 4 && mouseInside(148, 240, 339, 355)) goBackToMain();
  else if (screen == 5 && mouseInside(126, 274, 302, 325)) goBackToMain();
  else if (screen == 6 && mouseInside(122, 260, 302, 326)) goBackToMain();
  else if (screen == 7 && mouseInside(129, 274, 305, 329)) goBackToMain();
  else if (screen == 8 && mouseInside(115, 259, 304, 325)) goBackToMain();
}


// -------------------------------------------------------------
// HELPERS
// -------------------------------------------------------------

// Reusable function for BACK NAVIGATION
function goBackToMain() {
  port.write(' ');  // Sends "turn everything OFF" to Arduino
  screen = 3;
  screen3f();
}

// Check if mouse is inside a bounding box
function mouseInside(x1, x2, y1, y2) {
  return mouseX >= x1 && mouseX <= x2 &&
         mouseY >= y1 && mouseY <= y2;
}


// -------------------------------------------------------------
// SCREEN FUNCTIONS + ARDUINO COMMANDS
// -------------------------------------------------------------

function screen2f() {
  image(screen2i, 0, 0, 400, 400);
  screen = 2;
}

function screen3f() {
  image(screen3i, 0, 0, 400, 400);
  screen = 3;
}

function screenpf() {
  image(screenpi, 0, 0, 400, 400);
  port.write('p');  // Send “pinnacle”
  screen = 4;
}

function screent() {
  image(screenti, 0, 0, 400, 400);
  port.write('t');
  screen = 5;
}

function screene() {
  image(screenei, 0, 0, 400, 400);
  port.write('e');
  screen = 6;
}

function screend() {
  image(screendi, 0, 0, 400, 400);
  port.write('d');
  screen = 7;
}

function screenm() {
  image(screenmi, 0, 0, 400, 400);
  port.write('m');
  screen = 8;
}

p5js screen:

Arduino  and p5.js Communication

Flow:

User spins the wheel → motor sends voltage

Arduino reads values → prints as CSV string

p5.js reads the CSV → detects movement → plays sound

User clicks a part of the stupa in p5.js → p5 sends a letter

Arduino receives the letter → lights the corresponding LEDs

This loop creates a tight physical and digital connection.

What I’m Proud Of

One of the things I’m most proud of in this project is the way I used a motor as a sensor. In class, we mostly learned how to drive a motor, how to make it spin, how to control its speed, how to power it. But we never talked much about using a motor in reverse, as a generator. The idea actually came from something I learned back in high school: when you spin a motor manually, it becomes a dynamo and creates a small voltage. Remembering that old concept and realizing I could apply it here felt like a huge breakthrough. Instead of attaching extra sensors or complicated hardware, I turned the motor itself into the perfect input device for the prayer wheel. It made the interaction feel more authentic and made me feel resourceful like I made something out of almost nothing.

I’m also proud that the project became more than just a technical assignment. A lot of Arduino + p5.js demos end up being simple lights or sliders, but I wanted my project to feel culturally grounded and emotionally meaningful. Recreating the experience of spinning a prayer wheel and interacting with a stupa allowed me to share a part of Nepalese culture in a way that felt personal. It wasn’t just, “Here’s a sensor and an LED” it was a small spiritual journey for the user. The moment the “Om” sound plays when the wheel turns feels like the installation is breathing with you.

Finally, I’m proud of creating a fully two-way communication system between Arduino and p5.js. At the beginning of the semester, I struggled even to understand how serial communication worked. But in this project, the Arduino and p5.js are constantly talking to each other. Arduino sends sensor data to p5.js, p5.js analyzes it, then sends back precise commands to control the lights on the physical stupa. This feedback loop makes the experience feel alive and responsive. Building this system made me feel like I actually understand how physical computing and digital interaction can merge into one continuous experience.

Overall, the project pushed me technically, creatively, and culturally. It’s the first time I felt like I wasn’t just completing a class assignment. I was creating something that feels like mine.

How This Was Made

I used several tools throughout this project:

Arduino UNO for sensing and controlling LEDs

p5.js for the interactive visuals and sound

Adobe Illustrator / Canva (or whichever tool you used) for drawing the stupa

A small DC motor as a dynamo sensor

WordPress to document the process

Generative AI (ChatGPT) to help debug my Arduino code,  and explain concepts more clearly

The write-up for this project came together very organically. Instead of sitting down and trying to write a perfect report in one go, I started by brainstorming everything in my head and dumping ideas onto paper. I wrote down fragments of thoughts, sketches of memories about stupas and prayer wheels, notes about how the interactions should feel, and even some quick diagrams. It was messy at first, but that process helped me understand what parts of the project mattered the most to me.

From there, I organized the ideas into sections—concept, interaction, technical breakdown, cultural meaning, challenges, and future improvements. I rewrote and refined them little by little. Some parts came from real experiences I’ve had at stupas in Nepal, and others came from experimenting with the Arduino and p5.js until something clicked. Once I had the raw content, I shaped it into the final narrative you see here.

 

Area for future improvement:

I also want to add more lights and more detailed lighting zones on the stupa. At the moment, the LEDs represent the main sections (like the pinnacle, dome, eyes, mandala, etc.), but the lighting could be much richer. I imagine having multiple LEDs in each section, maybe even different colors or subtle animations (like pulsing or fading) to show the sacredness and energy of that part of the structure. More lights would not only make the physical model more visually striking, but also help guide the user’s attention and make the mapping between touch and light feel clearer.

Lastly, I’d like to include more educational content about stupa symbolism. Right now, the project hints at the meanings (like the dome representing the world or the eyes representing wisdom), but I could go deeper. For example, when a section lights up, a short description could appear explaining its spiritual role, history, or connection to Buddhist philosophy. This would turn the installation not just into an interactive artwork, but also a small learning experience about Nepali and Himalayan culture.

Final working video:

Google drive link

Week 13: User testing

Most users were able to understand the basic concept of interacting with the prayer wheel and the stupa. They could figure out that touching different parts of the stupa would cause the lights to turn on and trigger audio responses. However, the main area of confusion was which specific areas of the stupa were touch-sensitive. Users often touched the wrong parts and then tried to adjust their behavior based on trial and error.

Even though the mapping between each touch point and the animation/light feedback did make sense to them after discovery, the initial uncertainty slowed down the interaction. Some users expressed that they “felt like they knew better,” meaning they expected the interactive areas to match their own understanding of how a stupa is structured, rather than how my sensor layout was designed.

What Worked Well & What Could Be Improved

The prayer wheel interaction worked especially well. Users intuitively understood that the wheel was meant to be spun, and the connection between spinning motion and sound was clear. The feedback loop felt natural and satisfying.

The lighting on the stupa, however, could be improved. While the concept of touching different architectural parts (pinnacle, dome, mandala, etc.) was meaningful, the technical responsiveness wasn’t always consistent. This inconsistency made some users second-guess whether they were interacting correctly or whether the system was malfunctioning. Improving sensor sensitivity or adding clearer visual affordances would help eliminate this confusion.

Areas I Felt the Need to Explain

The two aspects that required the most explanation were:

The cultural concept of the prayer wheel
Some users were not familiar with what a prayer wheel is or how it traditionally functions. This lack of background knowledge made the interaction less immediately intuitive for them.

Which specific parts of the stupa activate the LEDs
The stupa layout visually makes sense, but it wasn’t obvious to users where to touch. They needed guidance on how the different zones mapped to the lighting changes.

To make these areas clearer in the future, I could incorporate visual cues, such as subtle highlights, icons, or glowing outlines that indicate interactive regions. Alternatively, I could add a brief onboarding animation that demonstrates the interaction without relying on text.

User testing video: google drive

Week 12: Final Project Proposal-Experience Nepal: The Prayer Wheel Interaction

For my final project, I went through a long process of brainstorming, sketching ideas, drawing diagrams, and listing out possible interactions. After exploring several concepts, I decided to continue developing my theme of “Experience Nepal.” I wanted to design something meaningful something that carries a piece of home with me and ultimately chose to focus on a single, iconic scene: the prayer wheels found around the stupas of Nepal.

File:Bhudda´s eyes oversee a pilgrim circumambulate the stupa and spin its prayer wheels - Boudhanath (17663021918).jpg - Wikimedia Commons

When I talked to some of my Buddhist friends from countries like Sri Lanka, they told me that prayer wheels outside stupas are not common in their culture at all. That surprised me, because growing up even though I belong to a different religion. I often visited stupas just because I loved spinning the prayer wheels. As a kid, it was simply fun to go around the stupa and rotate each one. But as I got older, I began to understand the deeper spiritual meaning behind them.

Cultural Significance of the Prayer Wheel

Prayer wheels (known as “Mani wheels”) are an important part of Tibetan and Himalayan Buddhist practice. Each wheel is filled with rolls of mantras, most commonly “Om Mani Padme Hum.” According to tradition, spinning a prayer wheel is equivalent to chanting the mantra itself each rotation is believed to spread compassion, purify negative karma, and bring blessings to all beings. The clockwise motion follows the direction in which the sacred texts inside the wheel are written, symbolizing harmony and the continuous cycle of prayer. That understanding made the act of spinning them feel intentional and peaceful, and it became something I always looked forward to whenever I visited a stupa.

Because of that emotional and cultural connection, I decided to recreate this experience through my final project a small, interactive piece that can bring a little bit of Nepal into the classroom.

What I Have Completed So Far
1. Motor-as-Sensor Mechanism

In class, we learned how to power motors, but for this project I am using a motor as a dynamo meaning the motor becomes an input device instead of an output.

When someone spins the prayer wheel, the motor also spins. This produces a small voltage. The Arduino reads that voltage and interprets it as “the wheel is spinning.”

When motion is detected, the Arduino sends a signal to my P5.js sketch.

Once P5 receives the signal, it plays an “Om” sound (or a short chant) to represent the spiritual energy released through the turning of the wheel. The goal is to make the digital output feel connected to the physical ritual.

2. 2D Stupa Visualization

I am also working on a simple 2D illustration of a stupa specifically inspired by the Boudhanath Stupa, one of the largest sacred Tibetan Buddhist stupa in Kathmandu, Nepal, recognized as a UNESCO World Heritage Site.

Boudhanath Stupa | Nepal, Asia | Attractions - Lonely Planet

In the final version:

Different parts of the stupa will light up when interacted with.

Each highlighted part will display or trigger information about its symbolic meaning.

For example:

The eyes of the Buddha represent wisdom and awareness.

The pinnacle represents enlightenment.

The dome symbolizes the world.

The prayer flags/wheels spread blessings through the wind or motion.

This visualization will help viewers learn the cultural and spiritual context behind what they are interacting with.

Week 11: final project idea

For my final project, I plan to build an interactive installation called “Discover Nepal: Interactive Cultural Storyboard.” The goal is to create a small tabletop experience that teaches people about Nepali culture, history, geography, and fun facts in a playful, visual way. The project will use both Arduino and p5.js and will function like a tiny museum exhibit about Nepal.

Physically, I will create a flat board or “map” of Nepal with five labeled zones: Mountains, Festivals, Music & Dance, Food, and History & Fun Facts. Each zone will be connected to an Arduino input using either pushbuttons or simple touch pads made from aluminum foil. I will also add a single knob (potentiometer) that lets the user scroll through multiple facts within a category. When the user touches or presses one of the zones, the Arduino will detect which category was selected and read the knob position, then send this information as serial data to a p5.js sketch running on my laptop.

In the p5 sketch, I will draw a stylized interface representing Nepal, with icons or scenes for each category. Based on the messages from the Arduino, p5 will highlight the active category, display a short piece of text (such as a fun fact about the Himalayas, a description of Dashain or Tihar, information about Nepali instruments or foods like momo and dal bhat, or a historical event), and play a short sound that matches the theme (wind in the mountains, festival bells, a madal rhythm, etc.). Turning the physical knob will move through different facts inside that category, so the user can explore more than one piece of information in each area.

This project satisfies the course requirements because it is a physically interactive system that relies on a multimedia computer for processing and display. The Arduino “listens” to the user’s physical actions on the board (pressing zones and turning the knob), and the p5.js program “thinks” by interpreting those inputs and deciding what cultural content to show. The system “speaks” back to the user through animated graphics, text, and sound. My intention is to make the interaction feel like a fun, hands-on way to discover Nepal’s culture and history, rather than just reading a static page of information.

Week 11: Reading Response

Design Meets Disability

I never really thought about disability and design in the same breath until reading Design Meets Disability. But the more I read, the more I realized something: design isn’t just about making things pretty or functional. It’s about shaping identity, dignity, and belonging. And disability, instead of limiting design, has actually pushed it forward in powerful ways.

One of the first images that stayed with me was the Eames leg splint. It surprised me that something meant for wartime medical emergencies became a milestone in modern furniture design. It made me rethink how innovation often begins in uncomfortable or overlooked places. It reminded me that creativity isn’t always glamorous, sometimes it grows out of necessity, pain, or urgency. And yet, from that, something beautiful emerges.

I never really thought about disability and design in the same breath until reading Design Meets Disability. But the more I read, the more I realized something: design isn’t just about making things pretty or functional. It’s about shaping identity, dignity, and belonging. And disability, instead of limiting design, has actually pushed it forward in powerful ways.

One of the first images that stayed with me was the Eames leg splint. It surprised me that something meant for wartime medical emergencies became a milestone in modern furniture design. It made me rethink how innovation often begins in uncomfortable or overlooked places. It reminded me that creativity isn’t always glamorous sometimes it grows out of necessity, pain, or urgency. And yet, from that, something beautiful emerges.

Week 11: Exercise

1st Exercise

p5js:

// Serial port object
let port;

// Latest sensor value
let sensorVal = 0;

function setup() {
  createCanvas(400, 300);
  background(220);

  // Create the serial connection object
  port = createSerial();

  // If a port was used before, auto-reconnect
  let used = usedSerialPorts();
  if (used.length > 0) {
    port.open(used[0], 9600);
  }
}

function draw() {
  background(220);

  // Read one line of text until newline "\n"
  let str = port.readUntil("\n");

  // Make sure we actually received something
  if (str.length > 0) {

    // Convert the string into an integer
    let val = int(str.trim());

    // Map sensor value (0–1023) to screen X position (0–400)
    let x = map(val, 0, 1023, 0, width);

    // Draw a circle at mapped position
    ellipse(x, height / 2, 40, 40);

  } else {

    // If empty data is received, print it for debugging
    console.log("Empty:", str);
  }
}

Arduino  Code:

const int sensor=A2;
int sensorValue;
void setup() {
  // put your setup code here, to run once:
  pinMode(sensor,INPUT);
  Serial.begin(9600);
  // while (Serial.available() <= 0) {
  
    Serial.println(0); // send a starting message
   
  
}



void loop() {
  // put your main code here, to run repeatedly:
sensorValue=analogRead(sensor);
delay(60);
Serial.println(sensorValue);
  
  
}



2nd exercise: 

p5js

int ledPin = 9;

void setup() {
  Serial.begin(9600);
  pinMode(ledPin, OUTPUT);
}

void loop() {
  if (Serial.available() > 0) {
    int brightness = Serial.read();  // 0–255
    analogWrite(ledPin, brightness); // PWM LED brightness
  }
}

P5JS CODE:
let port;
let brightnessToSend = 0;
const baudrate = 9600;

function setup() {
  createCanvas(400, 400);

  // Create the serial port
  port = createSerial();

  // connect automatically if used before
  let usedPorts = usedSerialPorts();
  if (usedPorts.length > 0) {
    port.open(usedPorts[0], baudrate);
  } else {
    connectBtn = createButton("Connect to Serial");
    connectBtn.position(10, 10);
    connectBtn.mousePressed(() => port.open(baudrate));
  }
}

function draw() {
  background(30);

  drawStarButton(80, 150, 40, 20, 5, 64);
  drawStarButton(160, 150, 40, 20, 5, 128);
  drawStarButton(240, 150, 40, 20, 5, 192);
  drawStarButton(320, 150, 40, 20, 5, 255);

  fill(255);
  textAlign(CENTER);
  text("Current brightness: " + brightnessToSend, width / 2, 280);
}

function mousePressed() {
  if (!port.opened()) return; // p5.webserial function to check if open

  let stars = [
    { x: 80,  brightness: 64  },
    { x: 160, brightness: 128 },
    { x: 240, brightness: 192 },
    { x: 320, brightness: 255 }
  ];

  for (let s of stars) {
    if (dist(mouseX, mouseY, s.x, 150) < 30) {
      brightnessToSend = s.brightness;

      // Send brightness (0–255)
      port.write(brightnessToSend);
    }
  }
}

function drawStarButton(x, y, radius1, radius2, points, brightness) {
  let angle = TWO_PI / points;
  let halfAngle = angle / 2;

  if (brightnessToSend === brightness) fill(255, 200, 0);
  else fill(255);

  push();
  translate(x, y);
  beginShape();
  for (let a = 0; a < TWO_PI; a += angle) {
    vertex(cos(a) * radius1, sin(a) * radius1);
    vertex(cos(a + halfAngle) * radius2, sin(a + halfAngle) * radius2);
  }
 
  endShape(CLOSE);
  pop();
}

Arduino:

// const int ledPin = 5;
// const int lightPin = A0;
// const int a=0;

// void setup() {
//     Serial.begin(9600);
//     pinMode(ledPin, OUTPUT);
//     // pinMode(lightPin, INPUT);

//     while (Serial.available() <= 0) {
//         Serial.println(0);
// ;    }
// }

// void loop() {
//   int a=Serial.read();
//   Serial.print("message print ");
//   Serial.print(a);
//   digitalWrite(ledPin, a);
 
    // while (Serial.available()) {
    //     int ledState = Serial.parseInt();
       
    //     if (Serial.read() == '\n') {
    //         digitalWrite(ledPin, ledState);
    //         Serial.println(analogRead(lightPin));
    //     }
    // }
// }

int led = 5;
void setup() {
  Serial.begin(9600); // initialize serial communications
}
 
void loop() {
  if (Serial.available()) {
    int ballState = Serial.parseInt(); // reads full number like 0 or 1
    if (ballState == 1) {
      digitalWrite(led, HIGH); // ball on ground
    } else {
      digitalWrite(led, LOW); // ball in air or default
    }
  }
  // read the input pin:
  int potentiometer = analogRead(A1);                  
  //
 
  //remap the pot value to 0-400:
  int mappedPotValue = map(potentiometer, 0, 1023, 0, 900);
  // print the value to the serial port.
  Serial.println(mappedPotValue);
  // slight delay to stabilize the ADC:
  delay(1);                                            
 
  // Delay so we only send 10 times per second and don't
  // flood the serial connection leading to missed characters on the receiving side
  delay(100);
}

video:

3rd exercise:

p5js

let velocity;        // velocity vector of the ball
let gravity;         // gravity force vector
let position;        // position of the ball
let acceleration;    // accumulated acceleration each frame
let wind;            // wind force vector
let drag = 0.99;     // drag factor to slow down velocity a bit
let mass = 50;       // "mass" of the ball, also used as its size
let port;            // serial port object for Arduino ↔ p5 communication
let button;          // button to trigger serial connection popup
let open=false;      // flag to track if we've tried to open the port

function setup() {
  createCanvas(640, 360);
  noFill();
  position = createVector(width/2, 0);   // start ball at top center
  velocity = createVector(0,0);          // start with no movement
  acceleration = createVector(0,0);      // acceleration accumulates forces
  gravity = createVector(0, 0.5*mass);   // gravity pulls ball downward
  wind = createVector(0,0);              // wind starts with no force
  port = createSerial(); //port is an object that will be used to send message and receeieve message from arudnio
 
  //createbutton creates button in p5js with quote ma lekyeko kura aayera
  button=createButton("Connect to Arduino");
  button.position(width/2,height/2); //where you want your button to be
  button.mousePressed(openArduino); //mousePressed bhayo yo button ma bhanye kun function open garne  pass garne ani it opens "openArduino" wala function
 
}

function openArduino(){
  if(!port.opened()){ //to check if the port is opened or not we use function opened
    port.open(9600) // we open the port for communication with 9600 frequency
   
    button.remove() // once arudino is opended we removed the button to connect to ardunio
   
    open=true; //just a variable to check if we opended the port or not
  }
 
}

function draw() {
  if(port.opened()){             // run main logic only if serial port is open
  background(255);               // clear canvas every frame
  applyForce(wind);              // apply wind force first
  applyForce(gravity);           // then apply gravity force
  velocity.add(acceleration);    // a → v
  velocity.mult(drag);           // apply drag to slowly reduce velocity
  position.add(velocity);        // v → position
  acceleration.mult(0);          // reset acceleration so next frame starts fresh
  ellipse(position.x,position.y,mass,mass);  // draw the ball
 
  }
  // collision + LED control are outside the port.opened() block,
  // so they always run based on current position
  if (position.y > height-mass/2) {
     
      velocity.y *= -0.9;  // A little dampening when hitting the bottom (bounce)
      position.y = height-mass/2; // keep ball from sinking below the "floor"
      port.write("1\n")    // send "1" to Arduino → turn LED ON
    }
    else{
      port.write("0\n")    // otherwise send "0" → turn LED OFF
    }
    potvalue=port.readUntil("\n");  // read a line from Arduino (pot value as text)
    // console.log(potvalue);
 
    if(potvalue>514){      // simple threshold: above midpoint = wind to the right
      wind.x=1
    }
    else{                  // below midpoint = wind to the left
      wind.x=-1
    }
 
 
 
 
}

function applyForce(force){
  // Newton's 2nd law: F = M * A
  // or A = F / M
  let f = p5.Vector.div(force, mass);  // scale force by mass → acceleration
  acceleration.add(f);                 // accumulate acceleration for this frame
}

function keyPressed(){
  if (keyCode==LEFT_ARROW){
    wind.x=-1;              // keyboard override: push ball left
  }
  if (keyCode==RIGHT_ARROW){
    wind.x=1;               // keyboard override: push ball right
  }
  if (key==' '){
    mass=random(15,80);     // randomize mass (and drawn size)
    position.y=-mass;       // reset ball above the top so it falls back down
    velocity.mult(0);       // clear velocity so it restarts clean
  }
}
int greenLight=7;
int sensor=A0;
void setup() {
  // put your setup code here, to run once:
  pinMode(greenLight,OUTPUT);
  pinMode(sensor,INPUT);
  Serial.begin(9600);
  digitalWrite(greenLight,0);

}

void loop() {
  // put our main code here, to run repeatedly:
  // Serial.print("0");
 
  int value= analogRead(sensor);
  Serial.print(value);
  Serial.print("\n");

  while(Serial.available()>0){ //avaiable checks if there is any message in inbox of ardino
    String a=Serial.readStringUntil('\n');
    a.trim();
    if(a=="1"){ //if we receive 1 then the light turns on else turn off
       digitalWrite(greenLight,1);
    }
    else{
      digitalWrite(greenLight,0);
    }
   
   
  }
 

 

 

}

video link: Google Drive

Week 10: Reading Response

A Brief Rant on the Future of Interaction Design

After finishing reading the article, I caught myself staring at my hands and realizing how much I take them for granted. It’s strange how technology, which is supposed to make us feel more connected, can also make us feel so detached from the world right in front of us.

I’ve spent years glued to glowing screens typing, swiping, scrolling without thinking about how unnatural it all feels. Victor’s phrase “Pictures Under Glass” stuck with me like a quiet accusation. That’s exactly what these devices are: smooth, cold panes that flatten our world into something we can only look at, not touch. I thought about how I used to build things as a kid—Legos, paper circuits, even sandcastles. I remember the feeling of resistance when stacking wet sand, the satisfaction of shaping something that pushed back. Now, most of my creations live behind glass, where nothing pushes back.

What really struck me was Victor’s idea that true tools amplify human capabilities. We’ve built technologies that expand what we can see and compute, but not what we can feel. I realized that so many “futuristic” designs we celebrate are really just shinier versions of what we already have more pixels, more gestures, less connection. It’s like we’ve mistaken slickness for progress.

Reading this made me wonder what kind of future I want to help design as someone in tech. I don’t just want to make tools people use. I want to make tools people feel. Tools that understand how deeply human interaction is tied to touch, weight, and presence. Maybe that means haptic design, maybe something beyond it. But I know it means remembering that we are not just eyes and brains; we are hands, bodies, and motion.

 

Responses: A Brief Rant on the Future of Interaction Design

When I read Bret Victor’s Responses to A Brief Rant on the Future of Interaction Design, it felt like listening to a passionate inventor defending an idea most people didn’t quite understand. His frustration was almost funny at times how he keeps saying “No! iPad good! For now!” but underneath that humor was something deeper: a kind of hope that we can still dream bigger about how we interact with technology.

What struck me most was his insistence that the problem isn’t the devices themselves. It’s our lack of imagination. We celebrate flat screens as the height of innovation, but Victor reminds us they’re only a step in a much longer journey. Reading this, I realized how often I accept technology as inevitable, as if it just happens to us. But Victor insists that people choose the future. We decide what gets built, what gets funded, what gets normalized. That realization hit me hard, because it turned passive admiration into personal responsibility.

His section on “finger-blindness” especially stuck with me. The idea that children could lose part of their sensory intelligence by only swiping at glass felt unsettling. I thought about how often I see kids with tablets, and how natural it looks yet maybe that’s exactly the danger. Our hands were made to shape, feel, and learn through texture. If we stop using them that way, we’re not just changing how we play; we’re changing how we think.

What I admire about Victor’s writing is that he doesn’t reject technology. He just wants it to grow up with us. He’s not nostalgic for the past or obsessed with sci-fi fantasies; he’s practical, grounded in the body. When he says that the computer should adapt to the human body, not the other way around, it reminded me that innovation should honor what makes us human, not erase it.

Reading his response made me feel both small and inspired. Small, because it reminded me how little we really understand about designing for human experience. Inspired, because it reminded me that design is not just about what looks cool. It’s about what feels alive.

Maybe the real future of interaction design isn’t in inventing smarter machines, but in rediscovering the intelligence already built into our own bodies.

Week 10: Musical Instrument

For this week’s assignment, we had to make a musical instrument involving at least one digital sensor and one analog sensor. Aditi and I decided to create a simple piano-like instrument with lights whose pitch level can be controlled by a potentiometer. There are 4 buttons (switches) that act as the piano “keys” and play different sounds, while the potentiometer has been mapped to three different levels so that the keys produce a high-pitched, middle-pitched, and low-pitched sound.

Materials

  • Analog sensor: 10K Trimpot
  • Digital Switch: Red, Blue, Yellow, and Green tactile buttons
  • Output: Piezo Buzzer to produce the sound and LEDs for light output

Schematic

Video

link: drive

Code

const int button_yellow=8;
const int yellow_light=7;
const int button_blue=9;
const int blue_light=6;
const int green_light=5;
const int button_green=10;
const int red_light=4;
const int button_red=11;
#define BUZZER 12
int potValue=0;
const int potPin=A0;
int melody[]={262,294,330,349,392,440,492,523}; // notes from C4-C5
int melody2[] = {523, 587, 659, 698, 784, 880, 988, 1047}; // notes from C5–C6
int melody3[] = {1047, 1175, 1319, 1397, 1568, 1760, 1976, 2093}; // C6–C7
int nodeDurations=4;
int increase=0;
int potValue_p;

void setup() {
  // put your setup code here, to run once:
  pinMode(button_yellow, INPUT);
  pinMode(yellow_light,OUTPUT);
  pinMode(button_blue, INPUT);
  pinMode(blue_light, OUTPUT);
  pinMode(button_green, INPUT);
  pinMode(green_light,OUTPUT);
  pinMode(button_red, INPUT);
  pinMode(red_light, OUTPUT);

  pinMode(BUZZER, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  potValue = analogRead(potPin);
  
  if (digitalRead(8) == HIGH) {
    if (potValue <= 300) {
      tone(BUZZER, melody[1]);
    } else if (potValue >= 300 && potValue <= 550) {
      tone(BUZZER, melody2[1]);
    } else if (potValue >= 550 && potValue <= 1023) {
      tone(BUZZER, melody3[1]);
    }
    digitalWrite(yellow_light, HIGH);

  } else if (digitalRead(9) == HIGH) {
    if (potValue <= 300) {
      tone(BUZZER, melody[2]);
    } else if (potValue >= 300 && potValue <= 550) {
      tone(BUZZER, melody2[2]);
    } else if (potValue >= 550 && potValue <= 1023) {
      tone(BUZZER, melody3[2]);
    }
    digitalWrite(blue_light, HIGH);

  } else if (digitalRead(10) == HIGH) {
    Serial.print("green");
    if (potValue <= 300) {
      tone(BUZZER, melody[3]);
    } else if (potValue >= 300 && potValue <= 550) {
      tone(BUZZER, melody2[3]);
    } else if (potValue >= 550 && potValue <= 1023) {
      tone(BUZZER, melody3[3]);
    }
    digitalWrite(green_light, HIGH);

  } else if (digitalRead(11) == HIGH) {
    if (potValue <= 300) {
      tone(BUZZER, melody[4]);
    } else if (potValue >= 300 && potValue <= 550) {
      tone(BUZZER, melody2[4]);
    } else if (potValue >= 550 && potValue <= 1023) {
      tone(BUZZER, melody3[4]);
    }
    digitalWrite(red_light, HIGH);

  } else if(digitalRead(8)==LOW && digitalRead(9)==LOW && digitalRead(10)==LOW && digitalRead(11)==LOW) {  
    Serial.println("Pin is LOW");
    noTone(BUZZER);
    digitalWrite(yellow_light, LOW);
    digitalWrite(blue_light, LOW);
    digitalWrite(green_light, LOW);
    digitalWrite(red_light, LOW);
  }
}

Reflection & Future Improvements

We had fun making this project, however it was also challenging to make all of the connections. Initially, we planned to have at least 6-7 switches, but the setup became so crowded with just 4. I started drawing the schematic diagram on paper before we began to build the connections physically, and this made the process much more manageable. We definitely could not have done it without having the diagram in front of us first. Sometimes, the setup for a particular switch would not work and we would face trouble in figuring out whether the issue was in the code or the wiring. In future versions, we would love to have more keys, as well as a tiny screen that displays the letter for the current note that is being played. We would also want to research on more “melodic” or pleasant sounding notes to add to the code.

Week 9: Reading Response

 

Physical Computing’s Greatest hits and misses

When I read Physical Computing’s Greatest Hits (and Misses) by Tom Igoe, I found it fascinating how creativity often repeats itself, not out of laziness, but because some ideas are simply too human to let go of. Whether it’s waving your hand to make music like a theremin, or building dancing floor pads inspired by Dance Dance Revolution, these projects remind me that interaction is a language our bodies already know.

Igoe’s writing made me realize that physical computing isn’t just about wiring sensors or programming microcontrollers. It’s about finding meaning in movement and connection. His examples like gloves that make drum sounds or video mirrors that reflect your gestures  all blur the line between play and design. I like how he admits that some of these projects are “pretty but shallow.” It’s honest. It’s easy to get lost in flashing LEDs and forget to ask: what experience am I creating for the user?

The part that stayed with me most was his reminder that originality doesn’t mean doing something no one’s ever done before  it means doing something familiar in a new way. That’s comforting. As someone who’s just learning, I often feel like my ideas are too simple or already “done.” But after reading this, I see that even a basic project, a light sensor or a glove, can become unique if I bring my own perspective, story, or purpose to it.

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

When I was reading Tom Igoe’s Making Interactive Art: Set the Stage, Then Shut Up and Listen, I was struck by how simple yet challenging his message was. He tells artists and creators to stop explaining their own work  to resist the urge to direct, interpret, or control what others should feel. It’s funny how that sounds easy, but for anyone who’s ever made something personal, it’s actually the hardest thing to do. We want to be understood. We want people to “get it.”

Igoe reminds us that interactive art isn’t about showing, it’s about inviting. The artist’s job is not to tell a story, but to create a space where others can find their own. When he says, “Once you’ve built it, shut up,” it hit me like a creative reality check. In most traditional art forms painting, photography, sculpture  the work speaks through form and color. But with interactive art, the audience literally completes the piece through their movement, curiosity, and touch. That’s terrifying and beautiful at the same time.

I also loved how Igoe compares designing interaction to directing actors. You can’t tell an actor how to feel; you just set the stage, give them a prop, and trust that they’ll find emotion in the act itself. That idea applies perfectly to physical computing and interactive design. The best projects don’t just respond to people  they encourage discovery. They leave space for mistakes, surprises, and personal interpretation.

Reading this made me think differently about my own work. I realized that in some of my projects, I’ve tried to control the experience too tightly giving too many instructions or forcing a specific reaction. But now, I see that a good interactive project is a conversation, not a lecture. It listens as much as it speaks.

Week 9: Smart Pedestrian Light System

Concept:

In my home town, we don’t usually have automatic traffic lights for pedestrian (zebra) crossings. When I came to the UAE, I found it really interesting that many crossings have a touch-sensitive button on the pole, when a pedestrian presses it, the system detects the request and changes the traffic light to red for vehicles, allowing people to cross safely.

263 Abu Dhabi Road Signs Stock Video Footage - 4K and HD Video Clips | Shutterstock

I wanted to mimic that concept using simple electronic components. In my prototype, a light sensor (LDR) acts as the pedestrian touch detector. When I place my finger over the sensor,  “green” light turns on (in my case, a blue LED, since my green ones were damaged), signaling that a pedestrian wants to cross. When the sensor is not covered, meaning the LDR value stays above a certain threshold (around 400), it represents that no one is waiting to cross, so the “red” light for pedestrians remains on.

Additionally, I included a digital switch that simulates a traffic officer controlling the lights manually. Whey they clicked the red button, red light turns on and force they vehicle to stop.

Video:

 

Code:

const int LDR= A2;
const int LDR_LED=10;
const int red_LDR=8;
void setup() {
  // put your setup code here, to run once:

  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
  int value= analogRead(LDR);
  Serial.println(value);
   
  if(value<=500){
    digitalWrite(LDR_LED,HIGH);
    Serial.println("yes");
   
    digitalWrite(red_LDR,LOW);
   
  }
  else{
    digitalWrite(LDR_LED,LOW);
    digitalWrite(red_LDR,HIGH);
  }
  
}

 

Schematics: