Week 9: Reaction-Time Game

Concept

I got the idea for this assignment after watching a campus cat swiftly pounce upon a tiny lizard on the wall in front of the arts center. It got me thinking about reflexes and reaction times and how I can create a mini-game out of this using Arduino. The game basically works like this:
There are two LEDs, one red and one green, one button, and one potentiometer. When the green LED turns on, the player must press the button as fast as they can. If they press it in time, the green LED flashes thrice, indicating a win, otherwise the red LED flashes, indicating the loss. If the green LED flashes, they can see their reaction time in milliseconds on the screen as well. The potentiometer controls the difficulty of the game in two ways. Firstly, it controls the random waiting period (i.e. how long the Arduino waits before turning on the green LED). A decrease in the delay between instances would need the player to be on higher alert. Secondly, it controls the reaction time threshold (i.e. how fast the player must press the button to win). At the easiest setting, the player has 400ms to react, and at the hardest, only 100ms.

Image & Video Documentation

The code

// potentiometer (A0), button (2), red LED (9), green LED (8)

const int potPin = A0;
const int buttonPin = 2;
const int greenLED = 8;
const int redLED = 9;

void setup() {
  pinMode(greenLED, OUTPUT);
  pinMode(redLED, OUTPUT);
  // use a resistor in the microcontroller instead of on the breadboard
  // causes inverted logic: not pressed - HIGH, pressed - LOW
  pinMode(buttonPin, INPUT_PULLUP); // Internal pull-up resistor
  Serial.begin(9600);
  randomSeed(analogRead(A5)); // Add some randomness
}

void loop() {
  Serial.println("Get ready");
  digitalWrite(redLED, LOW);
  digitalWrite(greenLED, LOW);
  delay(2000); // brief pause before start

  // Read difficulty from potentiometer
  int potValue = analogRead(potPin);
  int waitTime = map(potValue, 0, 1023, 1000, 3000); // 1s to 3s random wait range
  // potValue = 0 then threshold = 400 ms; 
  // potValue = 1023 then threshold = 100 ms (hard)
  int threshold = map(potValue, 0, 1023, 400, 100);  

  // Random gap time before LED turns on
  int randomDelay = random(1000, waitTime);
  delay(randomDelay);

  // Start the test
  digitalWrite(greenLED, HIGH);
  unsigned long startTime = millis();

  // Wait for button to be pressed or timeout
  bool pressed = false;
  unsigned long reactionTime = 0;

  while (millis() - startTime < 2000) {       // 2 seconds max to press button
    if (digitalRead(buttonPin) == LOW) {
      pressed = true;   // player successfully pressed the button
      reactionTime = millis() - startTime;    // calculate the player's reaction time
      break;
    }
  }

  digitalWrite(greenLED, LOW);    // turn off green LED

  if (pressed && reactionTime < threshold) {
    // Player wins
    // Display reaction time
    Serial.print("Your reaction time: ");
    Serial.println(reactionTime);

    // Flash green LED thrice to indicate win
    for (int i = 0; i < 3; i++) {
      digitalWrite(greenLED, HIGH);
      delay(150);
      digitalWrite(greenLED, LOW);
      delay(150);
    }

  } else {
    // Player loses
    Serial.println("Too slow!");
    // Flash red LED thrice to indicate loss
    for (int i = 0; i < 3; i++) {
      digitalWrite(redLED, HIGH);
      delay(200);
      digitalWrite(redLED, LOW);
      delay(200);
    }
  }

  delay(1000); // small 1s pause between game rounds
}

Reflection & Future Improvements

I had a lot of fun making this project. I think the physical setup was not too complicated, and still gave an effective output through the implemented code. In future versions, I think it would be nice to have a buzzer that gives different sounds on win/loss conditions, and maybe also implement a score counter that keeps track of how many times the player won throughout multiple rounds. I believe it would also be effective to add another LED (probably yellow) that blinks during the “get ready” phase of each round.

Reading Reflection – Week 9

Physical Computing’s Greatest Hits (and misses)

Sometimes I feel like the authors of our readings can read my mind. Usually, my first thought when thinking of a project idea is that I don’t want to do something that’s already done, but the author starts by saying not to think that way. I guess he is right, and that no work is ever truly ‘original’, but always takes inspiration from somewhere or something.

One of the applications of physical computing that really struck me was the Dance Dance Revolution. I realized that I never really thought much about the mechanics of it, but it’s crazy to think that just having a few switches on the floor managed to create this dynamic that took over the world. I also don’t think that I’ve ever encountered the theremin instrument, but it seems very interesting that even though it’s a physical object affected by your actions, you can’t actually think about the actions, but must actually think about the music.

The author’s section on Dolls and Pets also got me thinking about how that concept has evolved compared to the year 2008. The image in the reading shows normal dolls physically connected to a device, but nowadays we have toys with the screens built in, so that the doll/pet seems even more life-like. An example of this is the Eilik Interactive Robot Pet, which went viral on social media a while ago (I actually really want one, but they’re so expensive).

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

I agree with everything the author says throughout this reading, however, I do think that it’s easier said than done. He mentions, “Some will be emotionally moved, some will not get it, others won’t care.” This dynamic is why I believe that artists tend to offer interpretation notes about their artwork to their audience. I think it’s quite painful to see people not understand or not care about an interactive work you poured hours of your time into. Therefore, to save themselves from this struggle, artists tend to offer the script beforehand. On a similar note, this is also the approach I took with the first half of this class. Every time I showed someone my work, I would be quick to explain the concept behind it before they even began to explore, making sure that they didn’t do anything “wrong”. But after reading the insightful comparison done by the author between planning interactive artworks and a director working with actors, I think I’ve really understood that an interactive artwork is only completed by the audience’s curiosity. I’m excited to apply this new approach to my own interactive artworks, where the audience is nudged to discover the statement of the artwork by themselves.

Week 8: Unusual Switch

Concept

The idea behind this project is to create a hands-free switch that uses body weight to control a simple electrical circuit. When someone sits down, the pressure causes two layers of aluminum foil, separated by a bouncy piece of foam, to touch, completing the circuit and lighting up an LED. When the person stands up, the foam returns to its original shape, breaking the connection and turning the light off. This project demonstrates how everyday materials like foil and foam can be turned into a functional pressure-sensitive switch, showing how creative design and basic electronics can work together to make an interactive, responsive object.

Image & Video Documentation

Future Improvements

In future versions, the pressure switch could be made more durable and precise by replacing the foil with conductive fabric or copper tape, and using a softer, more uniform spacer material to ensure consistent contact. It could also be used to trigger more complex outputs, such as sounds, animations, or notifications, when someone sits down. Equally important, it could also be made more aesthetically pleasing by adding color and by making it smaller, making it not only functional but also a cute device to have hidden somewhere, such as on a chair.

Reading Reflection – Week 8

Her Code Got Humans on the Moon – And Invented Software Itself

I’ve known about Margaret Hamilton for quite some time. Her name was the one I would give out when people asked me who my favourite woman in STEM was, and the picture in the reading is the same one that went viral on social media a while ago. Nevertheless, I was mostly familiar with her accomplishments, and I was quite shocked to find out that she had a daughter at this time, over whom she would be criticized. It only makes me think even higher of her.

I really liked it when Hamilton said, “I was always imagining headlines in the newspapers, and they would point back to how it happened, and it would point back to me.” This shows that Hamilton was very well aware that the stakes of her work went far beyond code or computation. Also, I think her ability to think of potential failures before they happen is what made her so exceptional, and this is a skill that all programmers should try to build. Therefore, a certain attentiveness to detail and analysis of possible outcomes can help build stronger and more reliable programs.

My favorite quote of hers is, “When I first got into it, nobody knew what it was that we were doing. It was like the Wild West.”

Emotion & Design: Attractive things work better

This reading made me realize that emotion plays such a big role in the things we use. An example is Norman owning three teapots, which I found quite funny. He doesn’t really need 3 teapots, but it’s about the feeling that each one brings, and the fact that each teapot brings its own experience.

This idea also connects to what I’ve been learning in my Have a Seat class, where we design and build chairs. I’ve realized that a chair is never just a chair, it holds an emotional presence in a space. The curve of the backrest, the texture of the wood, even the way light hits it can make someone feel calm, inspired, or comforted. Just like in Norman’s article, emotion becomes part of usability. When people feel good around a design, they tend to engage with it more positively, and that’s when it truly “works better.”

I used to think that attractive designs were somehow less practical. Growing up, I often heard that bright or playful designs were less “serious.” But this article completely challenges that idea. Beauty can enhance function rather than distract from it. So I guess my main takeaway from this reading is that human beings are drawn to designs that are not just usable, but that bring pleasure, comfort, and meaning. 

Midterm Project: Twenty Seconds

View Twenty Seconds here:
https://editor.p5js.org/siyonagoel/full/VcTSl8x7V

My Concept:

Twenty Seconds is an immersive minigame experience that aims to make its users uncomfortable. Every part of the project has been developed with the intention that its use should ultimately make someone feel uneasy, and most of this logic is based on things that make me uncomfortable. This also means that some parts of the project may not cause the same level of discomfort for everyone, and that’s okay, but I’ve tried my best to use things that make most of the population uncomfortable. The project essentially features 8 rooms, each of which has a 20-second timer running, and the user has to either complete some sort of a challenge, or sit through a bad experience (like uncomfortable sounds) for 20 seconds. They cannot move to the next room until they complete the challenge in their current room, and they complete the experience only after going through all rooms.

There are some deliberate design choices within this project. To start with, I made sure that there is a very minimal use of color throughout the project. Hence, the only colors you will see are white, black, and red. Initially, I was thinking of only white and black, but after I realised one more color is a necessity, I added red as I find it one of the most uncomfortable colors due to its association with violence. Also, there is no general background music that plays throughout the project, although there are some specific sounds for a few rooms and the pop-up messages. What can be more uncomfortable than silence, when people can actually hear their own thoughts? The font I used—Reenie Beanie—was the best mix I could find between a readable font and human handwriting, something that looks like it was scrawled on a blackboard with chalk.

For my midterm project, I wanted to do something that is a unique mix of both a game and interactive art, and I believe Twenty Seconds captures this quite nicely.

Technical and Game Design:

The project treats each room as a self-contained mini challenge while keeping a single central state (the hallway leads to a door which leads to a room which leads back to the hallway). I am proud of the clear division in my code between different sections, such as resource loading, room initialization, and rendering. For example, preload() gathers all the images and sounds that I use, each initRoomX() sets up the state, and draw() delegates to the current room. Because of this structure, I could easily extend the code every time I wanted to add a new room, and made debugging predictable. Here’s an example:

function initRoom2() {
  roomItems = [];
  
  // Define positions and images for the 4 items
  let positions = [
    { x: 50, y: 130, w: 230, h: 192, img: bedImg, name: "bed" },
    { x: 320, y: 130, w: 230, h: 204, img: labubuImg, name: "labubu" },
    { x: 600, y: 130, w: 162, h: 263, img: toiletImg, name: "toilet" },
    { x: 810, y: 150, w: 220, h: 157, img: sofaImg, name: "sofa" }
  ];
    
  roomItems = positions;  
  
  startTimer();
}

function drawRoom2() {
  background(0);

  // Instructions
  fill("white");
  textSize(30);
  textAlign(CENTER, TOP);
  text("Which of these definitely does not belong in any of our homes?", width / 2, 10);
  
  drawTimer();

  // Draw all room items
  for (let item of roomItems) {
    image(item.img, item.x, item.y, item.w, item.h);
    
    // show the item's name when hovering over it
    if (isMouseOverItem(item)) {
      if (item.name === "bed") {
        fill("white");
        textSize(30);
        text("Spiky bed", 190, 350);
      } else if (item.name === "labubu") {
        fill("white");
        textSize(30);
        text("A labubu", 480, 350);
      } else if (item.name === "toilet") {
        fill("white");
        textSize(30);
        text("Snake toilet", 755, 400);      
      } else if (item.name === "sofa") {
        fill("white");
        textSize(30);
        text("Centipede sofa", 995, 350); 
      }
    }
      
    // failure condition
    checkTimerExpired("You're trapped until you find the right answer >:)");
  }
}

So, every time I had to implement a new room, I would just add its required initRoomX() function and drawRoomX() function to the existing code, along with the required functionality and pop-up logic in the mousePressed() function. Since elements like the pop-ups and the timer were to be used repeatedly for all the rooms, I made sure to structure them as easily reusable functions that I can call in one line without having to paste the same 4-5 lines of code in the code for every room.

On the technical side, there are a couple of algorithms I’m proud of for some of the games. The first is the ones I used for room 1, the room filled with clocks. I used a circle packing algorithm, learnt from here, to generate the placement of the clocks without them overlapping.

// circle packing algorithm for generating non-overlapping clocks
while (attempts > 0) {
  // random position and size of circles
  let r = random(minR, maxR);
  let x = random(r, width - r);
  let y = random(r + topBuffer, height - r);
  
  // Check if position overlaps with existing clocks
  let valid = true;
  for (let c of clocks) {
    let d = dist(x, y, c.x, c.y);
    if (d < r + c.r) {
      valid = false;
      break;
    }
  }
  
  // generate a clock if the position is valid
  if (valid) {
    clocks.push(new Clock(x, y, r));
  }

For the warping of the clock’s hands when the clock “melts”, I created a function called drawMeltingHand() in the Clock class that uses subtle bezier deformation for the cool effect. Before this I had no idea that something known as Bezier curves exist, and found out that there is a p5js function for it when i was searching for ways online to draw curved lines smoothly.

drawMeltingHand(x, y, length, angle, melt, weight) {
    push();
    stroke("red");
    strokeWeight(weight);

    // Midpoint of the hand
    let midLength = length * 0.5;
    let x1 = cos(angle) * midLength;
    let y1 = sin(angle) * midLength;
    // straight first half part of the hand
    line(0, 0, x1, y1);

    // curved tip that bends downwards
    let x2 = cos(angle) * length;
    let y2 = sin(angle) * length + melt * 0.5;
    // bezier(x1, y1, x2, y2, x3, y3, x4, y4)
    bezier(x1, y1, x1, y1 + melt * 0.3, x2, y2 - melt * 0.2, x2, y2);

    pop();
  }

Another interaction design choice I’m proud of is the reversed cursor for whack-a-mole. I thought it would be complicated to implement, but the math actually turned out to be very very simple. If I just subtract from the center of the canvas the distance between the center of the canvas and the user’s real cursor, it would give me the corresponding coordinate for the virtual reversed cursor.

// this calculates the reversed cursor position
// the virtual mouse moves opposite to the user's actual mouse
let centerX = width / 2;
let centerY = height / 2;
virtualMouseX = centerX - (mouseX - centerX);
virtualMouseY = centerY - (mouseY - centerY);

I also really like the implementation of the eyes in room no. 6. I learnt about using the atan2() function for this purpose from here. It’s probably one of my most favorite rooms, because the code wasn’t too complicated, and the resulting effect was still very cool.

if (this.isSpecial) {
  // Static pupil
  pupilX = this.x + this.staticPupilX;  // keep it centered
  pupilY = this.y + this.staticPupilY;
} else {
  // Following pupil
  // tracks the mouse
  // atan2() finds the angle formed by a point, origin, and positive x-axis
  // calculate the angle between the eye center and the mouse position
  let angle = atan2(mouseY - this.y, mouseX - this.x);
  
  // don't want pupil leaving the eyeball
  // Set a maximum distance that the pupil can move from the eye center
  // 15% of the eye’s width or height (whichever is smaller)
  let distance = min(this.w * 0.15, this.h * 0.15);
  
  // calculate new pupil position
  pupilX = this.x + cos(angle) * distance;
  pupilY = this.y + sin(angle) * distance;
}

Problems I ran into:

  1. I made the hallway doors on Canva. So the distance and angle between the doors was based on their relative orientation to each other on the canva surface size. What I didn’t realize was that I would need to have the exact same relative distance between the doors on my p5js canvas as well so that the perspective lines align, and due to this the hallway pathway ended up becoming much broader than I had planned. The only way to fix this would have been to remake all the doors again keeping this in mind, but since that wasn’t a time-feasible option, I left the hallway as is.
  2. Another problem I ran into was with drawing Dali’s clock. While I achieved the effect that I wanted with the clock hands, I cannot say the same for the circular frame of the clock. I wanted the bottom half of the clock to stretch downwards in a wavy shape so it would resemble Dali’s clocks, but I could not figure out how to achieve that effect. I tried asking large language models like ChatGPT and Claude to help with this but their attempts failed quite horrendously. Finally, I settled for the next best thing and just made the bottom part of the clock stretch downwards in a straight fashion. I did this using the following code:
    if (angle > PI / 4 && angle < (3 * PI) / 4) {
      py += map(sin(angle), 0, 1, 0, this.meltAmount);
    }
    

    The if condition selects only the lower arc of the circle. map(sin(angle), 0, 1, 0, this.meltAmount) converts the gradient gotten from sin(angle) into a vertical offset that increases toward the bottom of the circle. So basically, the value returned by sin(angle) in the range 0-1 is mapped proportionally to a value in the range of 0- the melting amount value set by me, and by doing py += , I am able to pull the y-coordinate downwards.

  3. Figuring out the ideas for each room. It took some time, but here’s the reason each room has what it has:
  • Room 1: Clocks and the sound of a ticking clock just makes me really uncomfortable, a room full of them is just terrible. 
  • Room 2: Self-explanatory.
  • Room 3: Needle-in-a-haystack kind of a situation. I grew up hearing this phrase a lot, and I don’t like it.
  • Room 4: I hate the feeling of disorientation. I wanted people to go through a similar feeling but making them deal with a reversed cursor.
  • Room 5: I think there are some sounds that make you want to pull your hair out. I made sure they’re all in this one room.
  • Room 6: The idea of being watched all the time is so uncomfortable.
  • Room 7: Some words in the English language just feel so wrong. I thought a collection of them floating around in a one place would be nice.
  • Room 8: This room was technically to give people a break. So while they can relax and watch rain pouring for 15 seconds, that last 5 seconds the rain turns red, and I think red rain definitely doesn’t make anyone feel more comfortable.

Areas for improvement:

  1. A friend suggested this really cool idea for jumbling up the doors everytime someone returns back to the hallway. This would make the whole experience so much worse.
  2. Currently, the rooms aren’t manipulated by any kind of user input. What I mean by this is that yes the user interacts with the elements in the room, but it’s only through moving the cursor around or clicking. In the future, I would like to add more direct user interaction, such as user text input. I would also like to experiment with machine learning tools like computer vision and use the audience’s bodily movement or facial experiments as inputs.
  3. I purposely chose not to have background music that runs throughout the game, but I think if I really found the perfect music for the ambience I’m going for, I would be open to using it.
  4. In room 5, the sounds stop bluntly when crossing regions. In the future I would implement smooth crossfades, to create a more cohesive soundscape and reduce abruptness, which will make transitions feel intentional and less jarring.

Week 5 – Midterm Project Progress

Project Concept & Design

For my midterm project, I wanted to make something that was a little different. Looking at past projects, I noticed that all the sketches used a ton of colors and soft shapes that created an aesthetically pleasing and welcoming environment for the user. So I thought, why not this time, I make the user uncomfortable? Of course, I can’t know the different things that make various people uncomfortable, so this interactive artwork + game is based on things that make me uncomfortable.

Basically, it’s a series of challenges that the user must get through in order to win. But each challenge is supposed to evoke some sort of uncomfortable emotion/feeling (hopefully) within the user. The design is such that there are 8 doors, behind each of which is a challenge that the user must complete. Once the user has clicked on a door, they are trapped in that room until they successfully complete the challenge. Only then are they given the option to return to the hallway and go through another door. The only colors I aim to use across this game is black, white, and red, as I think it throws people off and adds to the overall mood I’m going for.

Production & The most frightening part

There were two things I was most worried about. First was making the hallway. This is because I couldn’t find the right kind of doors online, and had to design my own using Canva, and then go through the complicated process of cutting out backgrounds, resizing, and figuring out the coordinates on the canvas for each door so that they align in a perfect one-point perspective. That said, I’m pretty happy with how the end result turned out.

Second, I had imagined that it would take a lot of work to implement the general algorithm for the game. That means the structure of dividing it into different rooms, coding a timed mini game for each room (while keeping everything in theme), and keeping the user trapped in a room until they solved it. To minimize this risk, I decided to divide the code into different game states, so that whenever I need to add the code for a new door, for example door 2, I can just add the required code and set the game state to room 2 and test it out. Also for elements like the popups and the timer, which get used for every door, I made them into reusable functions so that I can just call them for every door, only with different arguments (popup messages) based on what I want. Hopefully, for every door I can continue to have the format I have implemented now, which is an initRoom__ and drawRoom__ function for each door. Then it’s pretty much just following the same pattern 8 times.

Risks/Issues

My biggest problem right now is that I may have overestimated my abilities by choosing to do 8 rooms. I’m going to have to think of 8 different mini-game ideas that make people uncomfortable and implement it in the given time frame.

Next Steps

Currently I’ve only implemented 2 rooms. I need to implement the other 6, as well as decide on what to do for the end page of the game when the user finishes all the challenges. I also need to decide on a background soundtrack that will run for the entire game. I also haven’t been able to think of a name for this project yet, and that’s pretty important.

It’s much better to view the sketch in full-screen: https://editor.p5js.org/siyonagoel/full/eEPhmUoLw

Reading Reflection – Week 5

As Levin noted in the article, there is a wide range of opportunities to utilize computer vision for interactive projects in the real world. On the surface level, human vision and computer vision seem similar, but at their core, the differences between them are striking. Human sight is based on context and shaped by years of experience living life, but computer vision is technically just raw pixel data at the start. Computer vision depends on the compatibility of the image with its abilities. If we give it the image of a person in different lighting or a new angle, it can result in unexpected processing outcomes, even though our human vision can easily identify that it’s the same person.

To help computers track what we’re interested in, I think it comes down to building a contrast between the object we wish to scan and its immediate surroundings. The author mentioned several techniques for doing this, such as frame differencing, which compared changes between video frames, background subtraction, which identified what was new compared to a static scene, and brightness thresholding, which isolated figures using light and dark contrasts. What I found most interesting was the use of difference in movement in the Suicide Box project, where it was the odd vertical motion of the persons that was the contrasting event in the image, and what the computer consequently identified as the target.

That said, computer vision’s capacity for tracking and surveillance makes its use in interactive art complicated. On one hand, it can make artworks feel so much more alive, and on the other, like in the Suicide Box project, it leads to significant controversy and even disbelief that the recordings could be real. It’s also interesting to think that what computer vision did in the Suicide Box project, human vision could never do, at least without causing the observer lifelong trauma. So computer vision does not just enable interactive art, but helps raise questions about privacy and control, and reflects cultural unease with the idea of being watched. 

I would also like to add how cool I find it that I’m now learning about these technologies in detail, when as a child I would go to art and science museums to see artworks that would use this technology and leave me feeling like I just witnessed magic; a similar feeling when I got my Xbox One and all the sports games would detect my movement as the characters’.

Week 4: Fortune Cookie

I got my idea for this week’s artwork when my friend and I were talking about common childhood toys, and she mentioned the Magic 8 Ball. This got me thinking about other fortune-telling knick-knacks, when I remembered fortune cookies. I thought it would be really fun to make a digital version of a fortune cookie and populate it with interesting fortunes for users to come across. Just having a cookie on the canvas seemed a little aesthetically boring, though, so I found an image of anime-style food on a table on Pinterest, and chose to draw the fortune cookie as part of that setting and match the same style (to the best of my abilities).

Before I started coding, it took me a long time to decide on how I would draw the cookie. I looked at a lot of pictures online for cartoon fortune cookie, and the final look of it that I decided upon is really a mix of a bunch of photos, and more importantly, a realistic structure through which I though I could animate the effect of the cookie breaking open. Overall though, I am happy with how I mirrored the aesthetics of the background for my own cookie and plate. The fortune texts are a collection of messages I found online as fortune cookie messages and thought were funny.

In this sketch, the program is organized into four main functions: preload() loads the external assets like the background image and the cookie snap sound before anything else runs, setup() initializes the canvas, text settings, and starting positions of the cookie and fortune paper, draw() continuously updates the scene by displaying the image, plate, cookie halves, shading, and animating the cracking motion and sliding fortune paper, while mousePressed() handles the interaction by toggling the cookie state. I particularly like this part of the code:

  // fortune paper
  if (isOpen) {
    fill("#ebd4b4");
    stroke("#4e1408");
    // keep it where cookie is
    rect(xPaper*0.9, height * 0.61, 320, 40, 5);
    
    // fortune text
    noStroke();
    fill("#4e1408");
    textFont('Gloria Hallelujah');
    textSize(14);
    text(fortune, xPaper*0.9, height * 0.61);
  
    // paper sliding effect
    xPaper = lerp(xPaper, xTarget, 0.1);
  }
  
  // animate cookie parts cracking apart
  angleL = lerp(angleL, leftTarget, 0.05);
  angleR = lerp(angleR, rightTarget, 0.05);  
}

function mousePressed() {
  if (!isOpen) {    
    isOpen = true;
    fortune = random(fortunes); // Pick a random fortune
    // Rotate left half outward slightly
    leftTarget = -QUARTER_PI / 2; 
    // Rotate right half outward slightly
    rightTarget = QUARTER_PI / 2; 
    // Slide fortune paper out to the right
    xTarget = width / 2 + 150;
    
    // cookie opening sound
    if (snapSound.isLoaded()) {
      snapSound.play();
    }
    
  } else {
    // reset cookie to closed state when clicked again
    isOpen = false;
    leftTarget = 0;  // Return left half to original position
    rightTarget = 0; // Return right half to original position
    xTarget = width / 2; // Hide fortune paper
  }
}

I think what’s interesting here is the lerp() function. It is what makes the cookie and paper feel smooth instead of jerky. Without it, the halves and the fortune paper would instantly jump to their final positions. Basically what it does is it tells p5.js to move the current value a little closer to the target value on every frame. This creates a gradual transition so that the cookie halves slowly rotate outward like they’re cracking, and the fortune paper slides out as if being pulled, instead of teleporting into place. I also really like the cookie breaking sound I included when it opens and think that it increases the interactive feel in this sketch.

The biggest challenge I faced with this artwork was drawing the cookie and its shadow. It involved translations and well as the arc() function which was completely new to me, and I had to carefully go through its documentation of the p5js site to understand how the different arguments it takes work. The shadow took some trial and error, but I achieved it by first drawing a darker arc on the upper-right edge of the cookie, then cutting it down with a lighter arc. The second arc overlaps most of the first one and leaves only a slim crescent of shadow visible.

Looking forward, I would like to enhance the aesthetics of the cookie so that it looks less flat, and make its shape more closely matching with real fortune cookies. On the interaction side, fortunes could be randomized with different fonts, animations, or even themed backgrounds for variety. Also, right now the paper just sort of slides into the canvas from nowhere, I would like to make it so that it clearly pops out from the cookie, or maybe change the design completely so that it’s like a little slip that users can pull out from the cookie by dragging their cursor. Maybe, I could also add the sound of crinkling paper!

Reading Reflection – Week 4

I feel like I have spent a lot of time complaining about design issues throughout my life. That’s why I’m especially annoyed that it took me so long to actually think of them when I’m being given the platform to complain. But now I would gladly list the following two that drive me crazy:

The sensor faucets: I have been to so many bathrooms in malls and airports that require your hand to be so close to the sensor for them to work that if you move your hand away to actually wash it, the water stops running. I’ve literally had to keep one hand at the sensor while I attempt to wash one hand at a time. I understand the premise of sensors for water conservation, but this design can easily be improved if the sensor facility is designed such that it senses your hands at the right distance so that they can stay together under the running water.

The writing tablet chairs: My coaching center in the last 2 years of high school had these chairs, and they were my worst enemy. I hate everything about them. The table is too small, it is designed for right-handed people, I can’t get up without clearing the entire “table”, and I can’t rock the chair even a little bit without toppling over. Again, I understand that these were made for small spaces, but the design could be improved by making simple movable extensions for the table part (so at least a decent notebook can fit)  that people can add, make the material of the chair more comfortable so that students (who would be sitting on these for long durations) don’t have back pain, and introducing left-handed versions.

I think the way to apply the author’s principles of design to IM is by truly understanding this phrase that he uses, “emphasizing understandability and usability”. In my own sketches, I’ve noticed that I often rely too heavily on the assumption that my audience will share my intuition. But as Norman explains, intuition varies from person to person. What feels obvious to me as the creator may not be obvious to someone encountering the project for the first time.Therefore, my IM projects should have more signifiers to create more affordability in their designs. This can mostly be through visible cues that reduce ambiguity and increase chances for the user to know exactly how to interact with the piece. I think there is a sense of balance that can be achieved between intuition and explicit direction, and that sweet spot is what makes user interaction truly effective.

Week 3: Make a Wish <3

I love Disney animated films, and one of my most favorite movies to date is Tangled. As someone who has the lantern festival at the top of her bucket list (hopefully I will get to cross it out someday), I wanted to recreate the festival + the scene from the movie in my artwork. Since these lanterns are used to make wishes, I wanted my sketch to give its users the ability to “send in” their wishes as well. Thus, there are 2 kinds of interactive elements in this sketch. First, users can click anywhere on the screen, and a new lantern will be generated there. I made these lanterns glow a little brighter than the ones that are generated randomly; it’s a subtle but important difference, I think. Second, there’s a text box where users can type in their wish, and when they press “Enter”, their wish appears on the canvas and floats up and fades away with the lanterns. I really like that part!

There were a couple of new things I learnt through this artwork. First was how to make a gradient background in p5js. I searched online and found this code, which I used as is, just by changing the colors as per my needs. The trick is to use the lerpColor() function which blends two colors to find a third color between them. Second was how to add a text box to take user input and then insert that text in the canvas. For this, I used the createInput() function. I couldn’t really find an aesthetically pleasing position within the canvas to place the text box, so I placed it right below the canvas.

This sketch took a lot of time for 2 reasons. I started by first making just a sketch of a lantern and the little glow orb around it, and after I got the look I wanted, I had to look at the coordinates and do the math so that I could generalize it for the Lantern class. I’m pretty bad with ratios, so this was definitely a challenge. I also ran into the problem of the glow becoming opaque, since the opacity level added up every time the draw() function was called, so the transparency would immediately go away. This was solved by generating the background again under the draw() function. The second reason was the castle. I had to figure out the coordinates for every single rectangle and triangle involved. In the initial idea, the hill on each side of the main castle had a different design, but in the interest of time, I took the design I made first on the left side and flipped it for the right side of the hill using what we learnt in Wednesday’s class. But I am still quite happy with how the silhouette turned out.

I’m particularly proud of the organization of the lantern code:

// spawn a new lantern
if (random(1) < 0.09) {
  // min size = 7 and max size = 15
  let size = random(7, 15);
  // don't want lanterns touching the edges
  let x = random(width * 0.1, width * 0.9); 
  // start at 2/3 the height of the canvas
  let y = height * 0.67;     
  // min lantern speed = 0.3 and max lantern speed = 1.5
  let speed = random(0.3, 1.5);
  // add lantern object to the array of lanterns
  lanterns.push(new Lantern(x, y, size, speed, 30));
}

for (let i = lanterns.length - 1; i >= 0; i--) {
  let lantern = lanterns[i];
  // Move the lantern upwards
  lantern.update();
  // Draw lantern
  lantern.draw();

  // If the lantern has floated off the top of the screen,
  // remove it from the array so it no longer gets updated/drawn
  if (lantern.isOffScreen()) {
    lanterns.splice(i, 1);
  }
}

Making a class for each Lantern object and an array to store the currently visible lanterns made the above part of the code very modular, organized, and easy to understand.

For future improvements, instead of keeping the text separately floating with the lanterns, I would want to transform the text into a lantern as it floats upwards, and make the lantern store the message so that if the user clicks on it while it’s floating it displays the wish stored in it. I would also want to spend more time on the silhouette and make the two sides of the hill different.