Week 14-FINAL PROJECT BLOGPOST

1.Concept

This project is a fast reaction game where the user controls a spacecraft without seeing the controls directly. The screen gives instructions like “press red, “flip switch,” or actions using Arduino sensors like “cover the light sensor” or “move closer.” The user has to read the command and quickly do the action on a physical console. It’s timed, so you need to react fast and accurately. If you’re right, the game continues, and if not, you lose. The idea is to make simple actions feel intense and engaging using both physical controls and sensor based interaction.

The idea for this project came from two places. First, Project Hail Mary inspired the feeling of controlling something important without fully seeing or understanding the whole system, just like how the main character has to react quickly and solve problems using limited tools. Second, the game Bop It influenced the fast‑reaction style of the gameplay. I liked how Bop It gives quick commands and forces the player to respond immediately, and I wanted to bring that same energy into a physical console using Arduino sensors and buttons.

2. Images/videos of project

 

3. A clear and well labeled schematic

4. User Testing videos (I need to see the whole experience/interaction in documented videos)

User testing

During user testing, I focused on watching how people actually interacted with the console. Most testers understood the button tasks immediately, but the light sensor and keypad made them pause for a second, which helped me confirm that the timing and difficulty were working as intended. I also noticed that some players pressed the buttons too lightly or at an angle, which made me reinforce the button mounts and secure the wiring so every press registered cleanly. This made the console feel more reliable and confident to use. Overall, the testing helped me refine the physical build.

5. How does the implementation work?

• Description of interaction design

The system works in a loop. First, the screen shows a command. Then the user reacts using the console or sensors. Arduino reads that input and sends it to p5. p5 checks if it’s correct and responds with the next step or a fail. This repeats quickly, so the user is always reacting and the system is always responding.The interaction design focuses on making the player react fast while switching between different types of physical actions. The player looks at the laptop for the command, but all the actual actions happen on the console, so there’s this constant back‑and‑forth between reading and doing. Each input feels different on purpose: buttons give a quick click, the light sensor needs a hand movement, and the keypad needs a press. This mix keeps the player alert because they never know what type of action is coming next. The Arduino keeps sending all the sensor and button data to the laptop, and the laptop checks if the player did the right thing. If they did, the game continues; if not, it stops. The LEDs give simple feedback so the player knows what’s happening without needing extra text. Overall, the interaction is meant to feel fast, physical, and a little stressful in a fun way, similar to the quick‑reaction style of Bop It but with the more “mission‑like” feeling inspired by Project Hail Mary.

• Description of Arduino code + code snippets + add link to Github full code

The Arduino code handles all the physical inputs and sends them to the laptop so the game can check if the player reacted correctly. It reads the buttons, the light sensor, and the keypad, then sends everything in one line over serial. I also remap the keypad using a small function so the keys match the layout I want. The Arduino listens for simple commands from the laptop to turn the LEDs on or off, which gives quick feedback to the player.

char fixKey(char key) {
  if (key == '1') return '1';
  if (key == '4') return '2';
  if (key == '7') return '3';
  if (key == 'A') return '4';
  if (key == 'B') return '5';
  if (key == 'C') return '6';
  if (key == '3') return '7';
  if (key == '6') return '8';
  if (key == '9') return '9';
  return 'N';
}

Code snippet I’m proud of

This was actually the part of the code I struggled with the most. My keypad didn’t match the number layout I wanted, and the values it was giving me felt completely random at first. I kept getting letters like A, B, C instead of the numbers I needed for the game. I tried rewiring and changing the library settings, but nothing fixed the layout. In the end, I realized the simplest solution was to write my own mapping function. So I made fixKey(), which basically translates the raw keypad output into the numbers I want. It looks simple now, but it took a lot of trial and error to figure out which key was actually sending what value.

Serial.print(ldrValue);
Serial.print(",");
Serial.print(greenState);
Serial.print(",");
Serial.print(blueState);
Serial.print(",");
Serial.print(yellowState);
Serial.print(",");
Serial.print(whiteState);
Serial.print(",");
Serial.println(lastKey);

I’m also proud of this part where I send all the sensor values, button states, and the last keypad key in one clean line. It took a lot of testing to get the order right and make sure nothing broke when the laptop tried to read it. This line basically keeps the game and the Arduino in sync, and if anything here is off, the whole game stops understanding the player’s actions. Getting this to work smoothly felt like a big win for me.

Github Link:https://github.com/MouzaAlMheiri/Intro-to-IM/blob/main/Final%20Project

• Description of p5.js code + code snippets + embedded sketch

My p5.js code controls everything the player sees and interacts with. It reads the data coming from the Arduino the light sensor, the four buttons, and the keypad and turns those raw values into actual gameplay. The code constantly listens to the serial port, updates the variables, and then checks if the player did the correct action based on the current command. It also sends simple characters back to the Arduino to trigger the LEDs, so the physical console reacts instantly when the player is right or wrong. All the timing, scoring, lives, and task switching happen inside p5.js, so this file is basically where the whole game logic lives.

The rest of the code handles the visuals and the flow of the game. I built multiple screens like the home page, instructions, how to, the main game, and the lose screen, and I switch between them using a simple screen variable referencing to my midterm since it had a similar game logic. I also added a star background animation to match the space theme, and I used sounds and a custom font to make the game feel more polished. Overall, the p5.js file ties the hardware and the visuals together so the game feels smooth and responsive.

let data = port.readUntil("\n");
if (data) {
  let values = data.trim().split(",");
  if (values.length == 6) {
    ldr = int(values[0]);
    greenButton = int(values[1]);
    blueButton = int(values[2]);
    yellowButton = int(values[3]);
    whiteButton = int(values[4]);
    keypadKey = values[5].trim();
  }
}

This snippet reads one full line from the Arduino, splits it into the six values, and updates all the inputs in the game. It’s the part that makes the whole system feel connected, because every button press and keypad input shows up instantly in p5.js.I’m proud of this part because it took a while to get the serial format stable, and once I figured it out, the game finally started responding smoothly. It made the whole project feel like a real interactive system instead of random signals.

function newTask() { //resets lights and picks a new task type
  send("r");
  send("g");

  let tasks = ["GREEN", "BLUE", "YELLOW", "WHITE", "LIGHT", "KEYPAD"];
  currentTask = random(tasks); //chooses one random task
  taskStart = millis(); //records when the task started

  if (currentTask == "KEYPAD") { //special case for keypad tasks
    let keys = ["1", "2", "3", "4", "5", "6", "7", "8", "9"];
    targetKey = random(keys);
    taskText = "Press " + targetKey;
  } else {
    taskText = "Tap " + currentTask.toLowerCase();
  }
}

This snippet shows how the game picks a new task by choosing one option from a list. It also resets the Arduino lights, saves the start time, and updates the text on the screen. For keypad tasks, it chooses a random number the player must press. This keeps the game unpredictable and makes sure every round starts with a simple, clear instruction

function correct() { //handles the whole reward flow when the player answers correctly
  feedbackActive = true; //activates the green feedback state
  score++; //adds one point to the score
  streak++; //increases streak because the answer was correct

  if (score > highScore) { //updates high score if the new score is higher
    highScore = score;
  }

  timeLimit = max(minTime, timeLimit - timeDecrease); //speeds up the game but never below minTime which I set 

  if (warningSound && warningSound.isPlaying()) { //stops warning sound if it's still playing
    warningSound.stop();
  }

  if (correctSound && correctSound.isLoaded()) { //plays the correct-answer sound if it's ready
    correctSound.play();
  }

  send("G"); //tells Arduino to turn green light on
  send("r"); //tells Arduino to turn red light off

  setTimeout(() => { //waits before resetting lights and generating next task
    send("g"); //turns green light off on Arduino
    newTask(); //moves to the next task immediately after correct answer
    feedbackActive = false; //turns off feedback state
  }, 400); //delay for feedback animation
}

This snippet shows what happens when the player gets a task right. The game adds to the score, increases the streak, and updates the high score if needed. It also makes the game a little faster by lowering the time limit, but never below the minimum you set. The function stops any warning sound, plays the correct sound, and sends signals to the Arduino to show green feedback. After a short delay, it turns the light off, starts a new task, and ends the feedback state so the game can continue smoothly..

Screen shots from p5js:

 

Description of communication between Arduino and p5.js

The Arduino and p5.js talk to each other through the serial port. The Arduino sends one line of data every loop that includes the light sensor value, all the button states, and the last keypad key. p5.js reads that line, splits it by commas, and uses each value to check if the player did the right action. When p5.js needs to give feedback, it sends a single character back to the Arduino, like “R” or “G,” and the Arduino turns the LEDs on or off. The whole game depends on this back‑and‑forth, and once the format was stable, everything stayed in sync.

6. What are some aspects of the project that you’re particularly proud of?

I’m proud of three main things in this project. The first is fixing the keypad. It gave me the most trouble because the values didn’t match the layout at all, and it took a lot of trial and error to figure out a mapping system that actually worked. It looks simple now, but getting there took patience.

The second thing is the physical design of the console. I wanted it to feel clean and easy to use, and I think the mix of buttons, the light sensor, the keypad, and the LEDs makes it feel like a real control panel. Seeing it come together physically was really satisfying.

The third thing I’m proud of is getting the communication between Arduino and p5.js to work smoothly. At first, the data kept breaking or coming in the wrong order, but once I figured out a stable format, everything synced perfectly. It made the whole game feel responsive and reliable.

7. Links to resources used

Fullscreen:

https://p5js.org/reference/p5/fullscreen/#:~:text=fullscreen,such%20as%20a%20mouse%20press.

Stars background:

https://editor.p5js.org/robert0504/sketches/srSzgJcCS

Game concept:

https://editor.p5js.org/skgmmt/sketches/Sk5VaX2yN

For Keypad:

Using a Keypad with Arduino

https://www.circuitbasics.com/how-to-set-up-a-keypad-on-an-arduino

https://www.ibm.com/docs/en/zos/2.5.0?topic=statements-return-statement

Arduino:

Analog input:https://docs.arduino.cc/built-in-examples/basics/AnalogReadSerial/

Button:https://docs.arduino.cc/built-in-examples/basics/DigitalReadSerial/

P5js:

Connecting arduino :https://editor.p5js.org/aa11972/sketches/YcwX3DgTK

Audio:https://p5js.org/reference/p5/loadSound/

Light Sensor:https://www.build-electronic-circuits.com/arduino-light-sensor/

8. Proper and detailed referencing of any use of AI tools (how were they used? Where?)

I used AI for fixing the keypad. The keypad was completely bugging, and nothing I tried was working. I rewired it, changed the rows and columns, and even tested different key pads, but the keys were still giving random values that didn’t match the layout. I used Ai to help me understand how to check each key through the Serial Monitor and see what character it was actually sending. Once I had that information, I mapped the keys myself and translated it into my official Arduino code using the fixKey() function.

9. Challenges faced and how you tried to overcome them

The biggest challenge I faced was the keypad. It was giving completely wrong values, and nothing I tried fixed it. I rewired it, flipped the rows and columns, and even tested different libraries, but the keys still didn’t match the printed layout. It was honestly really frustrating. I ended up using the Serial Monitor to check what each key was actually sending, and once I had that, I created my own mapping system in the Arduino code. That finally made the keypad usable.

Another challenge was one of the buttons breaking. It kept giving inconsistent readings, and sometimes it wouldn’t register at all. I tested the wiring, changed the resistor, and eventually replaced the button completely. After that, the input became stable again. Both issues took time, but solving them helped me understand the hardware better and made the final console more reliable.

10. What are some areas for future improvement?

For future improvement, I would like to add more diverse interactions so the game feels less predictable. Right now, the inputs work well, but adding things like sliders, switches, or buttons with built‑in LEDs would make the console more interesting to use. I also want to upgrade some of the components so they feel more solid and don’t break as easily. Overall, the system works, but adding more variety and better hardware would make the whole experience smoother and more fun.

 

 

 

Final Project

I always wanted make the rgb light at my study space be as per what I am doing. They had different settings in terms of blinking, intensity and hue. But adjusting them every time and keeping track of those setting was troublesome and would rather kill the mood then set it. My final project is inspired by this. Having deck to control the ambience of the room with one click. This is main idea. Other than the lights added vinyl record as well to give more character to this. They behave as per the mood choice. Adding music to each mode was the plan but during the set I noticed the record make kinda eerie sounds which feel more immersive then the songs. On the down side I had to turn them off for focus mode because it felt counter intuitive.

To take user’s input from Arduino to P5 i but a distance sensor and if the user hold their hand in front of it for more 3 sec everything pauses. This was a fun addition as it give some sense of gesture control. While testing it was to put the hand in the excat distance range so I put an LED which generally flickers but stops flickering  if the hand is in the range

This is my first user testing video with my brother. This actually pointed out that how hard it will be for the user to put the hand in the range. I guess it was easy when I was testing it myself because I exactly new the points. This led to the addition of the distance smoothing

https://drive.google.com/file/d/16gIRB_NWMZ8BK_nEDwCJMTRAQSwjMLZl/view?usp=sharing

Here are my favorite modes in dark:

Party

Focus

Rain

Eerie

Schematic

Code:

Arduino

There are basically 3 things happening inside this. First is that each mode is mapped to a loop of predefined movements for motors. Arduino gets the signal from the serial communication and it run that mode. I used a mapping of modes as well. Along side running the motors it also sends  IR signal to the lights to change mode accordingly. I got the mapping of the IR signal earlier from the IR receiver .Second part the the distance measurement to control the gestures, as the  distance sensor is right next the vinly it some fluctuates a lot so I added a distance smoothing function. This is the part I am most proud of

int getSmoothedDistance() {
  distBuffer[distIndex] = getDistance();
  distIndex = (distIndex + 1) % SMOOTH_SAMPLES;

  long sum = 0;
  for (int i = 0; i < SMOOTH_SAMPLES; i++) {
    sum += distBuffer[i];
  }

  return sum / SMOOTH_SAMPLES;
}

Lastly, it keep track of the user’s hand in the distance range and indicates it by turning on the LED and pauses the motors if the light is on for 3 sec.

P5

It start by a start screen which has connect to Arduino button on top and a start button. Start button makes if full screen and displays the mood options, The Connect to Arduino button stays there in case user forgot to connect earlier. Each mode has its relevant animation which when clicked. User can easily change modes by click the back button on top left and choosing the new mode. Even thou all the animations feel very relavent to me the one I am most proud of the eerie one. As it is very simple and subtle but catches the mood fully

eerie() {
  background(random(20));

  for (let i = 0; i < 5; i++) {
    stroke(255, random(100));
    line(random(width), random(height), random(width), random(height));
  }
}

sonder() {
  background(10);

  if (random(1) < 0.08) {
    this.sonderDots.push({
      x: random(width),
      y: random(height),
      life: 255
    });
  }

  for (let i = this.sonderDots.length - 1; i >= 0; i--) {
    let d = this.sonderDots[i];

    fill(255, d.life);
    noStroke();
    circle(d.x, d.y, 4);

    d.life -= 2;

    if (d.life <= 0) {
      this.sonderDots.splice(i, 1);
    }
  }
}

After is the communication between Arduino and P5 is relatively is simple but the signal interpretation was a bit challenging. This is the part where it gets managed

handleArduinoMessage(msg) {
  console.log("Arduino:", msg);

  // NEXT MODE trigger
  if (msg === "NEXT_MODE") {
    this.nextMode();
    return;
  }

  // PAUSE
  if (msg === "PAUSE_TOGGLED") {
    console.log("Pause toggled");
    return;
  }

  // sync
  if (msg.startsWith("MODE:")) {

    let m = int(msg.substring(5)); // after "MODE:"

    let modes = [
      "chill", "focus", "party",
      "rain", "wind", "eerie",
      "sonder", "hireath", "glitch"
    ];

    let newMode = modes[m];

    if (this.state === "ambience") {

      // to avoid rushing
      if (!this.currentAmbience || this.currentAmbience.type !== newMode) {
        this.currentAmbience = new Ambience(newMode);
        console.log("UI synced to:", newMode);
      }

    }
  }
}

  nextMode() {
    if (this.state !== "ambience") return;

    let modes = [
      "chill","focus","party",
      "rain","wind","eerie",
      "sonder","hireath","glitch"
    ];

    let index = modes.indexOf(this.currentAmbience.type);
    index = (index + 1) % modes.length;

    this.startAmbience(modes[index]);
  }
}

Other than log messages coming form Arduino to P5, their are basically two commands. From P5 to Arduino, it commands to spin the vinyls and blink the lights according to the mode. From Arduino to P5 is the pause everything when user put their hand.

Overall I am happy about the outcome, I like how am able to use it in daily life. I am most proud the whole vibe it creates in each modes as the lights, the animation and the screeching sound from vinyls, everything fall into place making a collective vibe.

Other than this I am really happy about using the Arduino to make something. Because before this the only physical out come of code I created was my laptop heating up.

Ai Usage

I used Chatgpt to help me with the animations and distance smoothing in general. Other than that before working on the project it helped learn about the who to use IR receiver and transmitter. That’s when I made all the mappings. This is how I used AI for the most part. Other than this it helped tidy up and organize the code as I struggled with App class in P5

Challenge

The biggest Issue  faced was with the IR transmitter as the it was giving out corrupted signals and was very very sensitive to direction. I needs to be pointed direct at the light and had two light but I wanted to use only one transmitter. These issues were resolved by using 100ohm resistor instead of 330ohm. I found out that even thou it still can work at higher resistance, It became almost useless.

For future I think there is a lot of room for improvement in this. The ambience control by light is and vinyls is very limited. This dies set a good blueprint. One major improvement  I am planning on adding is being able to run the Arduino from a touch screen so I can place at a corner and use it in real life, rather than running it every time

Conway’s Prism – Final Project Blogpost!!!

Concept:

Infinity Mirror’s are a cool thing I came across while scouring the internet for inspiration for my final project, but I needed to add some sort of spice to it, and not just leave it with some rainbow hue that rotates in the mirror. That’s where Conway’s game of life comes in, each LED strip acts as a cell, and there’s nothing more perfect for it than a Conway’s game of life visualization.

Demo:

Implementation:

Sketch:

Schematic:

ESP32 Snippets:
WiFi.begin(SSID, PASSWORD);
while (WiFi.status() != WL_CONNECTED && tries < 30) {
    delay(500);
    tries++;
}
lcd.print(WiFi.localIP().toString());

The ESP32 connects to an exisiting Wi-Fi network and prints its assigned IP address on boot. The IP only needs to be read once and then hardcoded into sketch.js. The 30-attempt timeout prevents the firmware from hanging forever if credentials are wrong and returns and error message instead.

unsigned long lastLEDUpdate  = 0;
unsigned long lastLCDUpdate  = 0;
unsigned long lastMatrixBeat = 0;

if (now - lastLEDUpdate > 20)  { updateLEDs();   lastLEDUpdate = now; }
if (now - lastLCDUpdate > 500) { updateLCD();    lastLCDUpdate = now; }
if (now - lastMatrixBeat >

 

max(80, simSpeed / 4)) { updateMatrix(); lastMatrixBeat = now; }

There is 3 completely independant timers running concurrently with zero blocking. Each one records when it last fired and only acts again once enough time has elapsed. This is using the blink-without-delay pattern we learnt in class since delay would freeze the entire micro controller. With this approach the LEDs animate at 50fps, the LCD refreshes every 500ms, and the matrix beats at a rate derived from simulation speed.

if (now - lastMatrixBeat > max(80, simSpeed / 4)) {
    updateMatrix();
    lastMatrixBeat = now;
}

When paused, the matrix runs its own idle animation, a border with a pulsing center block. The rate of that animation is derived from simSpeed/4, meaning if you’ve set the simulation to run fast, the idle animation also pulses quickly. This makes the physical hardware feel responsive to your settings even when nothing is simulating. The max (80,…) floor prevents it from updating faster than ~12fps at maximum speed.

void onWSEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) {
  switch (type) {
    case WStype_CONNECTED:
      wsServer.sendTXT(num, "HELLO:ESP32_READY");
      lcdFlash("p5.js Connected");
      matrixFlash();
      break;
    case WStype_DISCONNECTED:
      lcdFlash("Client Left    ");
      break;
    case WStype_TEXT:
      parseMessage(String((char*)payload));
      break;
  }
}

Websocket connections have lifecycle events. WStype_CONNECTED fires when p5.js first connects, and the ESP32 immediately sends a handshake back confirming its ready. WStype_DISCONNECTED fires if the browser closes or Wi-Fi drops, letting the hardware react gracefully rather than freezing on the last received frame. Only WStype_TEXT actually carries simulation data.

void parseMessage(String msg) {

  if (msg.startsWith("GRID:")) {
    int gridEnd  = msg.indexOf(',');
    String cells = msg.substring(5, gridEnd);

    int genStart = msg.indexOf("GEN:") + 4;
    int genEnd   = msg.indexOf(',', genStart);
    generation   = msg.substring(genStart, genEnd).toInt();

    int spStart = msg.indexOf("SPEED:") + 6;
    int spEnd   = msg.indexOf(',', spStart);
    simSpeed    = msg.substring(spStart, spEnd == -1 ? msg.length() : spEnd).toInt();

    int thStart = msg.indexOf("THEME:") + 6;
    if (thStart > 5) {
      themeIdx = constrain(msg.substring(thStart).toInt(), 0, 5);
    }

    // Decode the 64-char string into the grid array
    if (cells.length() == 64) {
      for (int r = 0; r < 8; r++)
        for (int c = 0; c < 8; c++)
          grid[c][r] = (cells[r * 8 + c] == '1') ? 1 : 0;
    }

    renderGridToLEDs();

  } else if (msg.startsWith("CMD:")) {
    String cmd = msg.substring(4);

    if (cmd == "PLAY") {
      simRunning = true;
      lcdFlash(">> RUNNING     ");
      matrixFlash();
    } else if (cmd == "PAUSE") {
      simRunning = false;
      lcdFlash("|| PAUSED      ");
    } else if (cmd.startsWith("SPEED:")) {
      simSpeed = cmd.substring(6).toInt();
    } else if (cmd.startsWith("THEME:")) {
      themeIdx = constrain(cmd.substring(6).toInt(), 0, 5);
    }
  }
}

This function is the backbone of the hardware side, it’s what parses the commands received from p5.js. The first part is “GRID: …”: This carries the full 64-character cell string plus metadata (generation step, or whenever the grid changes) The next part “CMD” is a lightweight command fired when a button is pressed in p5.js without a grid change, so pressing pause doesn’t redundantly re transmit all 64 cells. The ESP32 parses by prefix rather than a fixed schema, making it easy for me to extend with new commands later.

char genBuf[7];
sprintf(genBuf, "%06lu", generation % 1000000UL);
lcd.print(genBuf);
lcd.print(simRunning ? "  RUN" : "  PSE");

“%06lu” formats the generation count as exactly 6 zero-padded digits, so generation 42 displays as “000042” rather than shifting all the other text on the line (This fixes the issue where the LCD will keep past text if that specific position isn’t changed, so it will lead to a lot of gibberish if positions kept changing). The “% 1000000UL” wraps a tone million, preventing overflow on the display. For the speed part of the LCD, we print genBuf, which uses a map to convert the raw millisecond speed value into a visual bar of # characters (a progress bar).

if (simRunning) {
for (int r = 0; r < 8; r++)
for (int c = 0; c < 8; c++)
mx.setPoint(r, c, grid[c][r] == 1);
}

We take the grid we got from p5.js, and in the MAX7219 matrix, we simply turn on the point in that matrix wherever the grid is turned on to show the simulation on the dot matrix.

if (simRunning) {
    // mirror the actual grid
    for (int r = 0; r < 8; r++) { ... mx.setRow(0, r, rowByte); }
} else {
    // border + pulsing center block
    matrixFrame++;
    uint8_t f = matrixFrame % 16;
    mx.setRow(0, 0, 0xFF); mx.setRow(0, 7, 0xFF);
    if (f < 8) { mx.setPoint(3,3,true); mx.setPoint(3,4,true); ... }
}

When the simulation is paused the matrix doesn’t go blank, it switches to an idle animation: a solid border with a 2×2 block in the center that blinks at half the matrix update rate (f < 8 out of a 16 frame cycle). This makes it immediately obvious from the hardware alone whether the simulation is running or paused, without needing to read the LCD.

leds[i] = CHSV(baseHue + hue + (i * 6), 255, 255);
// dead cells:
leds[i] = CHSV(baseHue + hue + (i * 6), 200, 18);

Every LED gets a unique position in HSV colorspace, i*6 spreads 64 LEDs across 64×6 = 384 degrees of the color wheel, nearly a full rotation. As the global hue++ increments every 20ms the entire gradient rotates. baseHue shifts the starting color per-theme so each theme has its own dominant color family. Dead cells aren’t black, they get value:18, a very dim glow at the same hue, which inside an infinity mirror creates subtle depth between live and dead cells rather than a hard on/off contrast.

 

p5.js Snippets:
[grid, nextGrid] = [nextGrid, grid];

I took this optimization from my assignment 2 code, it has been a really long time so I will mention why this simple line really optimizes the algorithm by a lot. The GoL rules require all births and deaths to happen simultaneously, you can’t modify the grid you’re currently reading or updated cells corrupt neighbor counts of cells not yet processed. nextGrid is written during the step, then this single destructuring line swaps the two array references in O(1) with no copying. The old nextGrid becomes the write target for the next generation automatically.

let nc = (c + dc + COLS) % COLS;
let nr = (r + dr + ROWS) % ROWS;

Without wrapping, cells on the edge checking out-of-bounds neighbors return undefined, which correlates to 0 in arithmetic, silently giving border cells fewer neighbors than they should have. The “+ COLS” and “+ ROWS” before modulo is non-negotiable: in JavaScript -1 % 8 returns -1, not 7, so the addition ensures the value is always positive before wrapping. The top edge connects to the bottom, left to right, so the grid is a torus.

for (let [c, r] of born) {
    let px = GRID_X + c * CELL_SIZE + CELL_SIZE / 2;
    let py = GRID_Y + r * CELL_SIZE + CELL_SIZE / 2;
    for (let i = 0; i < 4; i++) particles.push(new Particle(px, py));
}

“born” is populated during “stepLife(),” only newly created cells (dead->alive) trigger particles, not cells that were already alive. Four particles spawn at the cell’s center pixel coordinates and fly outward. This gives the simulation visual feedback about where activity is happening, dense birth events create bursts of particles, stable still life produces nothing.

function sendStateToESP32() {
  let cells = "";
  for (let r = 0; r < ROWS; r++)
    for (let c = 0; c < COLS; c++)
      cells += grid[c][r];
  ws.send(`GRID:${cells},GEN:${generation},SPEED:${simSpeed},THEME:${themeIdx}`);
}

function sendCmd(cmd) {
  ws.send("CMD:" + cmd);
}

The grid is serialized as a flat 64-character string of 0 and 1s. A full state message looks like “GRID:0010011100…., GEN:42, SPEED:300, THEME:2.” Lightweight commands like “CMD:PAUSE” or “CMD:SPEED:180” skip the cell string entirely, pressing pause doesn’t re transmit the 64 characters redundantly. The ESP32 reads the prefix to know which parser to run.

let speedLabel = nf(map(simSpeed, 30, 900, 1, 0.03), 1, 2);
drawStat("SPEED", speedLabel + "x", ...);

Internally simSpeed is raw milliseconds between steps, easier to work with in timing logic. But displaying 300ms to a user is meaningless. map(0 convers the range 30-900ms to 1-0.03, so the UI shows a human-readable multiplayer like 0.50x or 1.00x. The inversion (low ms = higher multiplier) is intentional, fast simulation = high number feels intuitive.

function applyPreset() {
  initGrid();
  generation = 0;
  let key = presetKeys[presetIdx];
  if (key === "RANDOM") {
    for (let c = 0; c < COLS; c++)
      for (let r = 0; r < ROWS; r++)
        grid[c][r] = random() > 0.55 ? 1 : 0;
  } else {
    let pts = PRESETS[key];
    for (let [c, r] of pts) {
      if (c < COLS && r < ROWS) grid[c][r] = 1;
    }
  }
}

Each preset is stored as absolute [col, row] coordinates rather than offsets, sized to fit the 8xx grid. The “if (c < COLS && r < ROWS) guard means a badly defined preset can never write out of bounds and corrupt memory. RANDOM gets its own branch, each cell independently has a 45% chance of being alive. That specific threshold was tuned: lower than ~35% and the grid dies out almost immediately, higher than ~55% and it suffocates just as fast. 45% tends to produce a chaotic but sustainable starting population.

Proud moments:

Honestly I did not originally like this idea, it was like 3 am and I just wanted to put an idea and go to sleep. However it turned out so much better than I expected and I ended up really liking with what I came up with. Finishing the mirror and turning it on for the first time to see the effect actually working made me really happy and proud of myself, as well as running the code for the first time with p5js to see everything working just how I intended and look even better than I expected. Overall I am really happy and proud of this project.

Future improvement:

– Sound reactivity: I was considering using my microphone module on the ESP32 to inject new live cells in sync with audio, but I did not know if I had time.

– Saved patterns: Letting users save their favorite starting configurations to flash memory so they persist between sessions would be a really nice addition.

– HUD Tutorial: A real-time tutorial over the HUD would be a nice addition to get new users started with how a simulation works and how my simulation works.

– Population graph: I originally wanted to add an LED strip that would show the current population with different colors (green, yellow, red) but I did not have an LED strip connector, and my DIY attempts and connecting the wires to the copper pads did not work out successfully sadly.

Links:

github.com/Links2004/arduinoWebSockets

docs.espressif.com/projects/arduino-esp32/en/latest/api/wifi.html

https://developer.mozilla.org/en-US/docs/Web/API/WebSocket

GitHub Link

 

Week 14 – Final Project (PawPortion)

🐾 PawPortion🐾 Smart Pet Feeding Assistant

Concept

I started this project from a very simple everyday problem: feeding a pet on time. It sounds like something that should be easy to remember, but in real life, routines can get busy, and small responsibilities can be delayed or forgotten. I did not want to make a fully automatic feeder because that would remove the user from the process completely. Instead, I wanted to create a system that supports the user while still keeping them involved in the act of care.

PawPortion is an interactive pet feeding assistant that combines an Arduino-controlled physical feeder with a p5.js digital dashboard. The system keeps track of feeding time, reminds the user when it is time to feed, and responds when the feeding action is completed. The project is not just about dispensing food. It is about creating a clear relationship between the user, the interface, and the physical machine.

The main idea behind PawPortion is assisted responsibility. The system does not take over the task entirely, but it helps make the task more visible, structured, and responsive. The user can see when the pet was last fed, when the next feeding should happen, and whether the feeding has been missed. The pet’s mood on the screen also changes depending on the state of the system, which makes the interaction feel more emotional and less robotic.

Video of Project

Images of Project


Schematic



Assembly Instructions 


User Testing Videos

During user testing, I wanted to see whether people could understand the system without me explaining every step. The main interaction was mostly clear. Users understood that the p5 dashboard was giving them feeding information, and they were able to recognize the “Feed Now” button as the main action. Once the servo moved and food was released, the connection between the digital interface and the physical feeder became much clearer.

One important issue appeared during testing with the IR sensor. I had placed a printed image near the sensor to guide the user, but the image made some people interpret the IR sensor as a physical button. Instead of triggering it by placing something near it, they tried to press it directly. The problem was not that the sensor failed. The problem was that my visual instruction gave the wrong impression.

This taught me that physical interaction is not only about whether the circuit works. It is also about how the user reads the object. A small label, icon, or printed sign can completely change how someone understands what they are supposed to do. After seeing this confusion, I changed the printed label so it was clearer that the sensor detects presence rather than pressure. This made the interaction easier to understand and reduced the need for verbal explanation.

How Does the Implementation Work?

Description of Interaction Design

The interaction is built around a cycle between the user, p5, and Arduino. First, the p5 dashboard tracks time and displays the feeding schedule. The user can see when the pet was last fed, when the next feeding is due, and how much time is left. This makes the system readable before anything physical happens.

When feeding time arrives, the dashboard changes state. The pet becomes hungry, and the status message tells the user that it is feeding time. This is meant to guide the user instead of forcing the system to act automatically. The user then presses the “Feed Now” button on the p5 interface, which sends a command to Arduino.

Arduino receives the command and moves the servo motor. The servo acts like a small door or gate that opens to release food, then closes again. When Arduino finishes dispensing, it sends a message back to p5. After p5 receives that message, the dashboard updates the last feeding time, resets the countdown, and changes the pet’s mood to happy.

If the user does not feed within the grace period, the system marks the feeding as missed. The pet becomes sad, which makes the missed action more visible. I wanted this to feel gentle, not dramatic, but still noticeable enough that the user understands the consequence of ignoring the schedule, and that they should probably feed their pet.

Description of Arduino Code

Github Full Code

The Arduino code controls the physical side of the project. It is responsible for reading the IR sensor, controlling the servo motor, and communicating back to p5 when feeding is complete. I used named constants for the pins instead of random numbers, which makes the code easier to understand and edit later.

Snippet: defining the sensor pin, servo pin, and servo object.

#include <Servo.h>

const int SENSOR_PIN = 7;

const int SERVO_PIN  = 6;

Servo tap_servo;

The main function in the Arduino code is dispenseFood(). This function opens the servo, waits while food is released, closes the servo, and then sends “DONE” to p5. This message is important because it tells the dashboard that the physical feeding action has finished.

Snippet: the feeding function that opens the servo, closes it, and sends confirmation to p5.

void dispenseFood() {

  isDispensing = true;

  tap_servo.write(110);

  delay(2000);

  tap_servo.write(0);

  delay(300);

  lastFedTime = millis();

  isDispensing = false;

  Serial.println("DONE");

}

The Arduino also listens for serial messages from p5. When p5 sends “FEED”, Arduino immediately calls the feeding function. This is what connects the digital button to the physical movement.

Snippet: receiving the FEED command from p5.

if (Serial.available() > 0) {

  String cmd = Serial.readStringUntil('\n');

  cmd.trim();

  if (cmd == "FEED") {

    dispenseFood();

  }

}

I also added a cooldown so the IR sensor does not trigger repeatedly if something stays in front of it. Without this, the servo could keep dispensing again and again. The cooldown makes the physical interaction more controlled.

Description of p5.js Code

 

The p5.js code controls the digital dashboard. It handles the schedule, the pet mood, the buttons, and the communication with Arduino. The dashboard shows the last feeding time, the next feeding time, and a countdown. It also shows the pet’s mood, which changes depending on what is happening.

Snippet: timing values for the feeding schedule.

const FEED_INTERVAL_SEC = 60;

const MISSED_GRACE_SEC = 15;

The main schedule logic checks whether feeding time has arrived. If there is still time left, the pet stays neutral. If feeding time has arrived but is still within the grace period, the pet becomes hungry. If the grace period passes, the pet becomes sad.

Snippet: schedule logic that updates the pet mood.

if (secUntilFeed > 0) {

  petMood = "neutral";

  if (serialConnected) {

    statusMsg = "Waiting for next feeding";

  }

} else if (secUntilFeed > -MISSED_GRACE_SEC) {

  petMood = "hungry";

  statusMsg = "Feeding time!";

} else {

  petMood = "sad";

  statusMsg = "Feeding missed!";

}

When the user clicks “Feed Now,” p5 sends the word “FEED” to Arduino through serial communication. I used a newline at the end because it helps Arduino read the command as one complete message.

Snippet: sending the FEED command to Arduino.

const encoder = new TextEncoder();

const writer = port.writable.getWriter();

await writer.write(encoder.encode("FEED\n"));

writer.releaseLock();

The p5 code also listens for messages from Arduino. When it receives “DONE”, it updates the dashboard. This is what makes the interface respond only after the physical action finishes.

Snippet: checking for DONE from Arduino.

if (line === "DONE") {

  onFeedingDone();

}

I also added fullscreen functionality using the F key. This makes the dashboard easier to present during the final demo and makes it feel more like a complete interface rather than just a small sketch window. The course documentation specifically mentions fullscreen and responsive resizing as part of final project programming considerations.

Description of Communication Between Arduino and p5.js

The communication between Arduino and p5.js is one of the most important parts of the project. The system works through a two-way serial communication loop. p5 sends “FEED” to Arduino, Arduino moves the servo to dispense food, and then Arduino sends “DONE” back to p5.

This means the dashboard does not simply assume that feeding happened. It waits for Arduino to confirm that the physical action was completed. This made the project feel more reliable because the screen and the machine were connected through actual feedback.

This was also one of the hardest parts of the project because if the serial communication failed, the entire interaction felt broken. The dashboard could look fine, and the Arduino could work alone, but the project only felt complete when both sides were speaking to each other correctly.

Aspects Of The Project I’m Proud Of 

One of the strongest parts of this project is how far it goes compared to where I started at the beginning of the course. If I had seen this project in week one, I genuinely would not have believed that I could build it. It combines physical computing, serial communication, a digital dashboard, user interaction, timing logic, an animated interface, and a mechanical feeding system. That feels ambitious for me, and I am proud that I was able to execute it in a way that actually works.

I am also proud of how complete the project feels as an experience. It is not just Arduino moving a servo, and it is not just a p5 interface on a screen. The two parts depend on each other. The dashboard guides the user, the Arduino performs the physical action, and the screen updates after receiving confirmation. That connection makes the project feel like a full interactive system.

Another part that I think worked well is the personality of the interface. The pet mood makes the system feel more alive, and it helps communicate the feeding state without needing complicated instructions. The sad, hungry, happy, and resting states make the dashboard easier to understand and more engaging.

I am also proud that I improved the project through user testing. The IR sensor confusion could have been ignored, but I used it as a design lesson. Changing the printed label made the interaction clearer, which showed me that the physical design and the code are equally important.

Resources Used

p5.js

https://p5js.org/reference/ https://p5js.org/tutorials/get-started/ https://www.youtube.com/watch?v=c3TeLi6Ns1E

Serial Communication

https://itp.nyu.edu/physcomp/labs/labs-serial-communication/lab-webserial-input-to-p5-

js/https://itp.nyu.edu/physcomp/labs/labs-serial-communication/lab-webserial-output-from-p5-

js/https://makeabilitylab.github.io/physcomp/communication/p5js-serial.html

https://medium.com/@yyyyyyyuan/tutorial-serial-communication-with-arduino-and-p5-js-cd39b3ac10ce

Servo Motor

https://docs.arduino.cc/tutorials/generic/basic-servo-control/

https://learn.adafruit.com/adafruit-arduino-lesson-14-servo-motors/overview

https://www.youtube.com/watch?v=1mDnaiEytAI

IR Sensor

https://arduinogetstarted.com/tutorials/arduino-infrared-obstacle-avoidance-sensor

https://projecthub.arduino.cc/aboda243/obstacle-detector-using-ir-module-tutorial-101320

https://www.youtube.com/watch?v=vi4hkrrkwkY

https://www.youtube.com/watch?v=ESqhOKgKt5c

AI Usage

I used AI as a support tool during the development of this project. The most important use was debugging my p5.js code, especially because this was one of my first larger coding projects involving serial communication between Arduino and p5. Debugging was difficult because problems could come from the Arduino code, the p5 code, the serial connection, or the browser. AI helped me break down the problem and understand where the issue might be coming from.

AI also helped me understand how to structure parts of the p5 dashboard, especially the schedule logic, and serial communication. I still tested, adjusted, and integrated the code myself to make sure it worked with my actual Arduino setup.

if (secUntilFeed > 0) {
  petMood = "neutral";

  if (serialConnected) {
    statusMsg = "Waiting for next feeding";
  }

} else if (secUntilFeed > -MISSED_GRACE_SEC) {
  petMood = "hungry";
  statusMsg = "Feeding time!";

} else {
  petMood = "sad";
  statusMsg = "Feeding missed!";
}

I also used AI to generate visual design elements for the project, including the PawPortion logo and printable signs for the physical machine. These visuals helped make the project feel more polished and easier for users to understand.

Challenges Faced and How I Tried to Overcome Them

The biggest challenge was serial communication between Arduino and p5. This was one of the first times I worked on a larger project where hardware and software had to communicate continuously. When something did not work, it was hard to know whether the problem was in the Arduino code, the p5 code, the USB connection, or the browser’s serial port.

I overcame this by testing each part separately. First, I tested the servo on Arduino by itself. Then I tested whether p5 could connect to the Arduino. Then I tested sending one simple command. After that, I tested receiving “DONE” back from Arduino. Breaking it into smaller steps made the project less overwhelming.

Another challenge was making the interaction clear to users. The IR sensor confusion showed me that a working sensor does not automatically mean a clear interaction. Users interpreted the printed image as a button because that was the visual language I accidentally created. I fixed this by changing the label and making the physical instruction clearer.

A third challenge was making the project feel polished. Since the project includes both physical and digital parts, it needed to look intentional from both sides. I worked on the dashboard design, printable labels, and project logo so the final setup felt like one system rather than separate pieces.

Future Improvements

If I had more time, I would improve the physical build of the feeder. I would make the container more stable, the food release cleaner, and the overall structure more durable. Right now, the prototype communicates the idea, but a more refined version could look and function more like a real product.

I would also improve the interface by adding sound feedback. For example, a small sound could play when feeding time arrives, when food is dispensed, or when feeding is missed. This would make the system more noticeable and more satisfying to use.

Another future improvement would be adding more customization to the schedule. Instead of using a short demo interval, the user could choose real feeding times, like morning and evening. This would make the system more practical outside of the class demo.

I would also conduct more user testing with people who have not seen the project before. Watching how people understand the physical setup, the IR sensor, and the dashboard would help me refine the interaction even more.

Overall, I am very very proud of how far i’ve come and can confidently say that this class was a very formative step for me in my university journey and the first step to taking on my passion for Interactive Media. 

Stipend Breakdown (50$):

Total Spend: 47.92$

Final Project – User Testing

I’ve made a few changes to my project as I worked through the coding and circuit building. Since I couldn’t get Force-Sensitive Resistors in my hometown quickly, I decided to use foil sensors instead. I also chose to remove the “black rule” (the ignore part) from my code because it was difficult to integrate, and I don’t think it changes the game much. I’ve settled on three levels, each with three randomized sequences. The flow is simple: the screen shows a sequence, and then a timer starts to give the player time to input what they saw. It gets much harder, and the time limits get tighter as you reach the third level.

For user testing, I let my cousin try the game out. She understood the intro and instructions well, though I realized I forgot to update my text after changing the rules. This caused a bit of confusion at first, she wasn’t sure if she should press the board while the sequence was showing or wait for the timer. While the first level worked perfectly, most of them had a hard time getting past the second level. I discovered that as the sequences get more complicated, my Arduino and p5.js struggle to talk to each other fast enough. Even when my cousins did the right thing, the game wouldn’t always register it.

Moving forward, my main goal is to fix the lag in levels two and three, so the game plays smoothly. I want to make sure the players actually enjoy the challenge rather than fighting with the controls. I also plan to improve the UI by adding more sound effects, especially for when a player levels up, to make the whole experience feel more polished and fun.

Final Project: Pressure-Based Digital Piano (Pressyr)

Concept of project:

This project explores how physical pressure can be translated into sound and visual feedback through an interactive system. I created a simple piano-like device using force-sensitive resistors (FSR), where users can press pads to generate different notes while also seeing corresponding visuals on the screen.

Rather than using traditional keys, the system relies on pressure-based input, making the interaction feel more experimental and expressive. I also included an arcade button to control when the system starts, giving users a clear entry point before interacting with the instrument.

To connect the physical and digital aspects, I used p5.js to generate real-time visuals that respond to each input. When a pad is pressed, it triggers both sound and visual feedback simultaneously, creating a more engaging and responsive experience.

Overall, the project focuses on how simple physical input can be transformed into a combined audio-visual interaction that feels intuitive while still allowing room for exploration.

Pictures of Project:

VDO of project:

https://youtu.be/PZ8KxiPyQQQ?si=CkNlTvbEHZVMZ3Hm

Schematic:

Wiring inside the cardboard box:

VDO of user testing(without giving any instruction):

https://youtu.be/GeI6QWZyzsk?si=tPqhPQI3fib59SSb

 

Interaction Design:

The interaction starts when the user presses the red arcade button. This turns the system ON, which is also shown on the LCD screen. The LCD then guides the user by displaying “Press pads”.

After that, the user can press any of the pressure sensors (FSR). Each sensor corresponds to a musical note (C4, D4, etc.). When a pad is pressed, the system sends the signal to p5.js, which plays a sound and shows a visual effect on the screen.

If no interaction happens for a few seconds, the system resets back to the default state, which helps new users understand what to do next.

 

Arduino code:

The Arduino is responsible for reading input from multiple FSR sensors using a CD74HC4067 multiplexer. This allows me to read many sensors using only one analog pin.

Each FSR is connected using a voltage divider with a resistor to produce stable analog values. The Arduino checks whether the value is above a threshold to determine if a pad is being pressed.

When a pad is pressed, the Arduino sends the index of the key through Serial communication to p5.js. It also receives note names back from p5.js and displays them on the I2C LCD screen.

The arcade button is used to control whether the system is ON or OFF, and the LED inside the button provides visual feedback.

Codes snippets:

(1) Multiplexer reading

// Select channel on multiplexer
digitalWrite(S0, bitRead(channel, 0));
digitalWrite(S1, bitRead(channel, 1));
digitalWrite(S2, bitRead(channel, 2));
digitalWrite(S3, bitRead(channel, 3));

int sensorValue = analogRead(SIG);

This part selects which FSR is being read through the multiplexer and reads its analog value.

(2) Threshold logic

if (sensorValue > threshold) {
  Serial.println(channel);
}

This checks if a pad is being pressed and sends the index to p5.js.

(3) LCD display

lcd.setCursor(0, 0);
lcd.print("System ON");
lcd.setCursor(0, 1);
lcd.print("Press pads");

The LCD provides instructions to guide the user.

Arduino and p5.js GitHub:

https://github.com/skyorachorn/Intro-to-IM/tree/a7a5972712776f5ea0d3234a5193a3694f5a07d0/Final_Project

 

p5.js code:

The p5.js program receives data from Arduino using serial communication. When it receives a key index, it maps it to a specific note and plays the corresponding sound using an oscillator.

At the same time, a visual effect is generated at the position of the key. The visual consists of expanding circles with glow effects and fading animation.

I also added instructions on the screen to guide the user, especially for first-time interaction. The canvas is responsive and can switch to full screen mode for a better experience.

Codes snippets:

(1) Serial receive

let str = port.readUntil("\n");

if (str.length > 0) {
  let keyIndex = int(trim(str));
}

p5.js reads incoming data from Arduino.

(2) Trigger sound

osc.freq(note.freq);
env.triggerAttack(osc);

This plays the sound when a pad is pressed.

(3) Visual effect

balls.push({
  x: sx,
  y: sy,
  size: 28,
  alpha: 255
});

This creates the visual feedback on screen.

Communication between Arduino and p5.js:

The communication between Arduino and p5.js is done through Serial communication.

Arduino sends:
– Key index (0–12) when a pad is pressed
– -1 when the key is released

p5.js sends:
– The note name (e.g. C4, D#4) back to Arduino

This allows Arduino to display the correct note on the LCD screen while p5.js handles sound and visuals.

 

What I am proud of:

One part I am particularly proud of is how the physical and digital systems are connected together. The interaction feels responsive, and the combination of sound, visuals, and LCD feedback creates a complete experience.

I am also proud of the user guidance system using the LCD and on-screen instructions, which helps users understand what to do without needing verbal explanation.

Finally, I think the visual effects in p5.js improved the overall experience, making the interaction more engaging.

 

Breakdown cost:

 

Resources and References:

I used online resources such as YouTube tutorials to understand how to use the I2C LCD and arcade button wiring. I also referred to Arduino and p5.js documentation for serial communication and sound generation.

Some tutorials were used as references and then adapted to fit my project.

p5.js Sound Library (official reference)

https://p5js.org/reference/#/libraries/p5.sound

Basic FSR (Force Sensitive Resistor) guide
https://learn.adafruit.com/force-sensitive-resistor-fsr

Multiplexer(CD74HC4067)

https://learn.sparkfun.com/tutorials/multiplexer-breakout-hookup-guide

Multiplexer(CD74HC4067)

https://youtu.be/XAOo8DEbvck?si=oO7VKZ046IhJgHwh

Sounds

https://youtu.be/b_MMGJiUcbM?si=z7di2mc-pPGQBt1R

Triangle wave

https://kjh.ypj.mybluehost.me/glossary/triangular-wave/

I2C LCD

https://youtu.be/4o9VM7Nl9Os?si=hwz3Y6-QvSkRbGje

https://youtu.be/-jiHul1kQh4?si=WqZlRTMM3psv217o

https://youtube.com/watch?v=QIdERbxDVuM&feature=shared

Arcade button

https://youtu.be/Pkm8nRCSsIY?si=sFWSKIZpOXYXWQXT

 

AI assisting:

AI tools were used mainly to assist with debugging and improving code structure, especially for serial communication between Arduino and p5.js.

However, the overall design, concept, wiring, and implementation decisions were developed and tested by myself. I also modified and adapted any suggestions to fit my own project requirements.

 

Challenges:

One challenge was reading multiple FSR sensors reliably. Using a multiplexer required careful wiring and debugging to ensure each channel worked correctly.

Another challenge was the communication between Arduino and p5.js. At first, the data was inconsistent, but after adjusting the threshold and improving the logic, it became stable.

I also faced challenges in user experience, since users were initially confused about what to do. This led me to add instructions on the LCD and on the screen.

 

Future Improvements:

In the future, I would improve the design of the pads to be closer in size and spacing to a real piano keyboard, since some users mentioned that the pads felt too large.

I would also improve the visual system by making it more noticeable or adding more variation, so users might pay more attention to the screen.

Additional future improvement would focus on the hardware setup. The current prototype uses many exposed wires and breadboards, which can be messy and less stable. In the future, I would organize the wiring more cleanly and integrate the components into a more compact and durable enclosure.

Another improvement would be making the system wireless or more compact, instead of relying on a wired connection to a laptop.

 

Week 13 – User Testing

For my user testing, I had my sister test my game. I did not give her instructions beforehand on the way the game works; I wanted to see if she will be able to figure it out just from the instructions on the screen. This allowed me to test whether my game was self-explanatory and intuitive enough to be played without intervention or explanation from the developer (me). Thankfully, she indeed was able to figure out the game through the instructions given on the p5 sketch and successfully played the game.

She did not have any feedback or comments on the game. This made me really proud of the user-friendliness of my game, which is something I was focusing on during my development. I reflected on the readings we did in class regarding self-explanatory user-interfaces that do not require the developer’s input to give the user the best experience, and wanted to ensure I apply these ideas. I would like to say I think I succeeded 🙂 I wish we had the IM showcase or were back on campus so I can test my game on more people and truly see how intuitive my game is.

User testing

User testing with no instructions:

When two users tried my project without any instructions, both were able to figure out how to navigate it. The p5.js screen made it clear what action was required, so they understood that they had to respond to what they see on the screen. One user was able to move through the interaction smoothly and quickly connected the on screen commands to the physical controls like the buttons and keypad.

The other user showed some hesitation at the beginning. They got confused when instructions appeared quickly or were repeated, especially when multiple inputs were introduced at the same time. This made the experience feel slightly overwhelming in that moment. Even then, they were still able to understand the system after a short time. Both users understood the mapping between the controls and what happens in the experience, but one needed more time to adjust. This shows that the interaction is intuitive but depends on the user’s familiarity with fast paced games.

What worked well and what could be improved

Several parts of the experience worked well. The card interaction, LEDs, and overall input and output system were clear and responsive. The home page and flow of the experience helped guide the user, and the audio added an important layer by giving clear feedback for actions. These elements made the experience feel engaging and easy to follow once the user started playing.

One area that could be improved is the clarity of the instructions. Making them more specific and slightly more paced would help users who are less familiar with this type of interaction. Another important improvement is the stability of the buttons. When users started playing faster, the buttons sometimes shifted or felt less secure, which could interrupt the experience.

Reinforcing the buttons and making them more stable would make the interaction feel more reliable and comfortable. This was clear during testing and also when I disassembled and reassembled the project, which helped me better understand how to improve the physical setup.

What needed explanation and how to improve

Most parts of the project did not need explanation because users were able to understand them through interaction and feedback. The main part that needed explanation was the card. Since it is a separate component, users did not immediately understand how it works or what action triggers it.

To improve this, I could make the card interaction more clear by adding more detailed instructions or clearer visual cues. For example, showing a clearer message on the screen when the card is needed or adding simple labeling near the sensor would help users understand it faster. This would make the experience more accessible for first time users without changing the core interaction.

 

User one:

 

 

User two:

https://vimeo.com/1188956184?share=copy&fl=sv&fe=ci

Week 13: User Testing

Documentation:

Reflection:

My project was generally easy for users to figure out, mainly because instructions are provided at each step. One point of confusion was that the experience begins with the tamper selected, which made users unsure whether to start there. However, after referring to the steps, they were able to correct themselves and continue. One user also initially tried to use the joystick instead of the sensor for movement, but quickly understood the correct interaction. Overall, users were able to grasp the mapping between the controls and the actions and complete the process smoothly.

Most parts of the experience are working well, especially since the instructions are clearly displayed within the sketch. However, this also depends on the user taking the time to read them. Areas that could be improved include the clarity of the sprite sheet animations and making the interaction with the ultrasonic sensor more intuitive. This could be addressed by refining the instructions to more clearly indicate that movement in front of the sensor is required.

The main aspects I needed to explain were that users should use the physical button rather than the keyboard, and that the animations are controlled through hand movement using the ultrasonic sensor instead of controls. I believe this will become more intuitive once the physical setup is finalized and the wiring is concealed, allowing users to focus entirely on the interaction without distractions.

User Testing – Week 13

For my project, I created an interactive maze game where the player controls Minnie Mouse using two potentiometers connected to an Arduino. The maze appears on the screen, and the player must guide Minnie through it without touching the walls. The player collects items such as cheese, stars, and hearts, which trigger a yellow LED and a pickup sound. Hitting a wall turns on a red LED and plays a bump sound, and reaching Mickey at the end lights a green LED and plays a happy sound.

For user testing, I asked my sister to try the game. She immediately noticed that Minnie did not move smoothly. Instead of following the potentiometer movements, the character would jump or move to random parts of the maze. Even when she moved the knobs slowly, the character keeps moving as if it was lagged, making the maze impossible to complete or time consuming and not very easy. She described the controls as confusing and it wasn’t consistent. However, she said that the start page was clear and to connect the arduino it was easy and she mentioned that the instructions was clear aswell.

After watching my sister play, I realized the problem came from how I mapped the potentiometer values to the maze. The potentiometer sends numbers from 0 to 1023, but the way I converted those numbers into Minnie’s position on the screen was wrong. Because the mapping didn’t match the actual maze area, Minnie kept appearing in the wrong places. So basically the numbers coming from the Arduino did not match the part of the screen where the maze actually is.

From this user testing session, I learned that the mapping has to be extremely accurate when you connect real‑life controls to movement on the screen. Even a tiny mistake in the numbers can completely break how the game feels. I also realized how helpful it is to test with someone else, because they notice problems that I get used to. Even though the game was not working perfectly before and I realized that before the user testing, the testing helped me understand exactly what was wrong since my sister could see the same issue.

User Testing:

 

UPDATE:

Issue was from wiring. The connection from the bread board to the 5v was missing.