Assignment 2- Art Design

CONCEPT:
For this assignment, my inspiration mostly came from Bill Kolomyjec’s art piece ‘Random Squares’.
‘Random Squares’ Bill

However, I wanted to add a bit of a twist and make it more chaotic or trippy in some sort of way. I’ve gotten the idea of the changing shapes depending on the area of the canvas from class and interpreted it into my design, which thankfully pulled the piece together. In addition, the crazy random colours definitely added to the effect of it being trippy at least I hope so. Moreover, it still felt a bit bland, and after watching Casey Reas’s video, I knew I needed to add something to make the piece seem more alive or organic, as he says. In doing so, I discovered a growing and shrinking factor, which, after some trial and error, I realized it was just a variable that I needed to create and then just adjust the shape size accordingly, and it turned out not so hard after all. This would probably be what I’m most proud of in the whole code.
Code that I’m most proud of:

/ Variable for size change
let sizeChange = 0;
// Variable for the shapes (growing or shrinking)
let growing = true;

function setup() {
  createCanvas(400, 400);
  frameRate(5);
}

function draw() {
  background("black");

  // sizeChange variable to create growing/shrinking effect
  if (growing) {
    sizeChange += 1;
    // If the size gets too large, start shrinking
    if (sizeChange > 20) {
      growing = false;
    }
  } else {
    sizeChange -= 1;
    // If the size gets too small, start growing again
    if (sizeChange < 0) {
      growing = true;
    }
  }

 

Reflection/Improvment:
Next time, I would love to have the shapes rotate, which is something I tried doing but unfortunately failed, I did watch videos on youtube but i still didn’t understand it so i decided to scratch that until i fully understand how it works. So hopefully next time!

 

My design:

 

My code:

// Variable for size change
let sizeChange = 0;
// Variable for the shapes (growing or shrinking)
let growing = true;

function setup() {
  createCanvas(400, 400);
  frameRate(5);
}

function draw() {
  background("black");

  // sizeChange variable to create growing/shrinking effect
  if (growing) {
    sizeChange += 1;
    // If the size gets too large, start shrinking
    if (sizeChange > 20) {
      growing = false;
    }
  } else {
    sizeChange -= 1;
    // If the size gets too small, start growing again
    if (sizeChange < 0) {
      growing = true;
    }
  }

  for (let x = 0; x <= width; x += 40) {
    for (let y = 0; y <= height; y += 40) {
      // Outer stroke for the shapes
      strokeWeight(3);
      stroke("black");
      // Right half of the canvas - enlarging and shrinking squares
      if (mouseX > width / 2) {
        for (let size = 30 + sizeChange; size > 0; size -= 10) {
          fill(random(255), random(255), random(255), 150);
          // nested squares
          rect(x + (30 - size) / 2, y + (30 - size) / 2, size, size);
        }
      } else {
        // Left half of the canvas - enlarging and shrinking circles
        for (let size = 30 + sizeChange; size > 0; size -= 10) {
          fill(random(255), random(255), random(255), 150);
          // nested circles
          ellipse(x + 20, y + 20, size, size);
        }
      }
    }
  }
}

 

Assignment#2_reflection

The first time watching Casey Reas speech, I had so many thoughts. But one main point I kept thinking about is how the author focuses on artists using randomness to create art that represents history, biology, or society, but none of these works seem to connect with deeper human emotions. So then I kept thinking about this, and I asked myself. Does randomness restrict personal expression?. For example, the artist said, “We used a little bit of randomness and a lot of sort of decision-making based on how we wanted things to feel.” From my point of view, this quote shows how randomness keeps art from being predictable, leading to unexpected results that might move the piece away from personal emotional expression. So, this makes me question whether relying too much on randomness prevents personal expression since the process seems more about handling the chaos of the system than about conveying intentional emotions. The reason I focused on this part of the video is because I always thought of art as a form of personal expression that reflects the artist’s emotions, thoughts, and experiences. So, when too much randomness is involved, it feels like the artist loses control over that emotional connection, letting the system or algorithm take over, which could overshadow the personal meaning and emotional depth behind the work.

However, watching the clip for the second time, I decided to think with an open mind. For example, does the unpredictability in art lead to more innovative pieces that lead to deeper stories?. Casey Reas mentioned a few artists who used randomness to create works with meaningful messages. For example, he talks about a project visualizing cancer cell communication. “Everything is structured and ordered, and randomness is used slightly to determine the position and the scale of these individual clusters.” In the example, randomness plays a role in producing a lifelike representation. The artist used randomness to decide where the protein clusters would go and how big they would be, which reflects the natural unpredictability of real biological processes. As a viewer of this artwork, I felt as though it captured the chaotic nature of cellular interactions. So, in this case, I think that the randomness added depth in the art piece, which in a way made me feel connected with the processes of life in a way that a pure structured system or algorithm might not be able to. Overall, while at first randomness may seem to get in the way of personal expression, it nevertheless lets artists make works that reveal deeper truths about nature, society, and the human experience in ways that pure intention or structured systems might not be able to just by using unpredictability in some part of the art.

REFRENCES:

Reas, C. (2012). Form+Code in Design, Art, and Architecture.

Retrieved from the weekly PowerPoints: https://vimeo.com/45851523

Assignment 2-Reflection

In Casey Reas’ talk on chance operations, one of the most interesting ideas he explores is how randomness can act as a creative form in art. Reas shows how randomness can introduce unpredictability into structured digital systems, giving artificial creations a more natural and organic feel which he stated at the beginning of the video. Hence, made me think about how randomness is used in larger projects, like architectures and so on. For example, the designs of Expo 2020 or the Museum of the Future in Dubai bring together technology and creativity in a way that balances control with randomness, showing how both aspects can work together to create something that feels alive and evolving. Another thought that came to mind was how even small random changes can totally change the result of an algorithm. The “noise” is what keeps the system interesting, as he said without it, it’ll turn homogeneous and basically move in the same direction, which obviously isn’t exciting, and the system pretty much just becomes predictable. However, it also makes me think about things like how much randomness is good before it turns into chaos and when it stops adding to the art and starts taking away from it. One artist, for instance, Jean Tinguely, made “Homage to New York,” a machine that is meant to break itself. Yes, I get the idea of embracing chaos, but this amount of uncertainty makes it harder for me to connect. It’s too random for me to handle, and the lack of structure makes it difficult for me to connect personally with the piece. Still, I can see how some people might connect with Tinguely’s method, enjoying the randomness and how it shows how chaotic life is. In essence, too much chaos makes me feel disconnected, which shows that this type of art might not speak to everyone.

Homage to New York:

Assignment 1: Aysha’s Self-Portrait

Concept:

For this assignment I use my knowledge of drawing basic shapes in p5*js to create a self-portrait. This portrait is a rough sketch of me, wearing my favorite hairstyle—a high ponytail. Since I’m still relatively new to coding, I chose to keep my approach simple by using basic shapes, lines, and curves to create this sketch. Understanding how the order of code affects the final drawing inspired me to stack shapes strategically, allowing me to capture minute details like the facial features in my portrait.

Highlight:

I’m particularly proud of the eyes in this sketch, as I invested a lot of effort into adding detailed elements that took some time to perfect. My biggest challenge was getting the curves right, especially when it came to the eyebrows and eyelids. Positioning them correctly was tricky, but using the mouseX and mouseY variables was incredibly helpful in determining their relative placement. However, achieving consistent curves across the sketch was the most time-consuming aspect of the entire process.

//eyes
fill(250);
ellipse(180,195,15,20);
ellipse(220,195,15,20);

noFill();
curve(175,230,167,190,194,190,200,230);
curve(180,240,206,190,235,190,220,220);
curve(200,170,167,200,194,200,200,150);
curve(200,155,206,200,235,200,200,170);

//eyebrows
curve(200,175,207,175,239,175,250,220);
curve(150,220,165,175,197,175,280,200);

Reflections:

When drawing the eyelids and eyebrows, I initially used the curve() function, but I later realized that combining noFill() with bezier() would have been a quicker and more efficient approach for creating curves. Additionally, I used the width and height variables to center the head on the canvas, which worked well. However, I believe that applying these variables more consistently throughout my sketch would make the portrait scalable for any canvas size in the future.

Self Protrait

CONCEPT:

Creating this self portrait was a tough journey. My concept was to create a cartoonish version of myself whilst including specific features like my curly hair, and other basic features (eye lashes, rosy cheeks, lashes, lips, eyebrows etc) that would make the portrait come together. Anyways I wanted to also keep it simple since by focusing on implementing the basic shapes so I could get familiar with it, since I’m still new to this. Moving on, for the background I wanted to stick to a specific color that would match the aesthetic of my portrait which is bluish-purple. However, as you could tell I couldn’t decide on just one color, so I went with a rainbow background that changes as you move the mouse around the screen which I’ll explain more about below.

HIGHLIGHT:

i have two highlights in my  code. The first one is the background. The background was a bit of an accident. I wanted to do strips of different shades of blue but i kept being indecisive about the color and changing it. So then I went to look for inspirations in peoples blog and that’s when i stumbled on Noura alhosanis idea. However i changed it up by using different shapes, colors and sizes so it could go with my theme. Also, I looked into the class resources and came across a YouTube video, that helped me understand variables and the use of it. Here’s a snippet of the code:

let x, y, r, g, b;

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

function draw() {
    print(mouseX + "," + mouseY)
  
  //background
  background(mouseX,mouseY,100, 7)
  r = random (0,255)
  g = 0
  b = random(0,400)
  x = random(0,700)
  y = random(0,400)
  noStroke();
  fill(r,g,b,100)
  square(x,y,50)

The second thing I’m proud of is my hair. The reason for this is because it was so time consuming to perfect the curly hair look which includes the placing for it, the layers of circles to create volume, and different type of shades to add depth into it. I’m proud of how it turned out because it brought my portrait together.

REFLECTION:

However, as much as I’m proud of the hair, it was still a real challenge. First It was tricky figuring out the placements, but as I kept messing up , I figured out the print mouseX and mouseY function which helped. But with that function it was still really time consuming. So, for next time i would like to find an easier method that won’t make me want to pull my hair out. Now, does this portrait look exactly like me? Well…not quite. It’s my first attempt, so I focused more on capturing basic features by using the basic shapes we used in class. But that’s something I want to work on next time which is making the portrait more realistic. Overall, I had fun with this project, and I’m already thinking about how I can improve.

Here is the overall code:

let x, y, r, g, b;

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

function draw() {
    print(mouseX + "," + mouseY)
//background
  background(mouseX,mouseY,100, 7)
  r = random (0,255)
  g = 0
  b = random(0,400)
  x = random(0,700)
  y = random(0,400)
  noStroke();
  fill(r,g,b,100)
  square(x,y,50)
  
  
  //FACE SHAPE
  strokeWeight(0)
  fill(255,204,153)
  ellipse(200,200,105,150)
  
  //LEFT EYE 
  strokeWeight(1)
  fill(255)
  ellipse(180,180,14,10)
  
  //LEFT EYES LID
  strokeWeight(1)
  fill('#B98B7B')
  arc(180,180,15,25,PI,0)
  
  //LEFT PUPIL
  strokeWeight(1)
  fill('rgb(98,26,26)')
  circle(180,180,8)
  
  //RIGHT EYE
  strokeWeight(1)
  fill(255)
  ellipse(220,180,14,10)
  
  //RIGHT EYES LID
  strokeWeight(1)
  fill('#B98B7B')
  arc(220,180,15,25,PI,0)
  
    //LEFT PUPIL
  strokeWeight(1)
  fill('rgb(98,26,26)')
  circle(220,180,8)
  
  //left eye brows
  stroke('rgb(48,5,5)')
  strokeWeight(4)
  line(173,160,188,160)
  
  
  // Right Eyebrow
  stroke('rgb(48,5,5)')
  strokeWeight(4)
  line(210,160,225,160)
  
  //left eye lashes
  strokeWeight(1)
  stroke('black')
  line(174,178,174,172)
  line(176,177,176,172)
  line(181,176,181,172)
  line(186,178,186,172)
  line(179,177,179,172)
  line(184,177,184,172)
  
  //right eye lashes
  strokeWeight(1)
  stroke('black')
  line(213,178,213,172)
  line(216,177,216,172)
  line(221,176,221,172)
  line(218,176,218,172)
  line(224,176,224,172)
  line(226,176,226,172)
  
  
  //down lip 
  strokeWeight(1)
  fill("rgb(213,67,67)")
  arc(200, 240, 30, 15, 0, PI);
  
  //upper lip
  fill("rgb(195,42,42)")
  arc(200, 240, 30, 15, PI, 0)
  
  //LIP LINE
  arc(200, 240, 27, 1, 0 ,PI)
  
 //NOSE
  noFill()
    arc(200,210,13,10,0,PI)
  line(200,188,194,210)
  
  //body
  fill('rgb(39,39,133)')
  rect(150, 295, 100, 200,40)
  
  //left arm
  noStroke()
  fill(255,204,153)
 rect(140, 320, 20, 120, 40)
  
  //right arm
  noStroke()
  fill(255,204,153)
  rect(240, 320, 20, 120, 40)
  
  //left sleeves
  noStroke()
  fill('rgb(39,39,133)')
 rect(140, 320, 20, 50, 5)
  
  //right sleeves
  noStroke()
  fill('rgb(39,39,133)')
 rect(240, 320, 20, 50, 5)
  
  //neck coller
  noFill()
  strokeWeight(10)
  stroke('rgb(31,31,91)')
  arc(200, 295, 20, 30, 0 ,PI)
  
  //left sleeve coller
  noFill()
  strokeWeight(7)
  stroke('rgb(31,31,91)')
  line(140,370,160,370)
  
  //right sleeve coller
  noFill()
  strokeWeight(7)
  stroke('rgb(31,31,91)')
  line(240,370,258,370)
  
   //neck
  noStroke()
  fill(255,204,153)
  rect(190, 260, 20, 40,10)
  
  //V neck
  strokeWeight(7)
  arc(200, 270, 27, 78, 0 ,PI)
  
  //chin
  noFill()
  strokeWeight(1)
  stroke('black')
  arc(199,265,16,10,0,PI)
  
   //jaw
  strokeWeight(5)
  stroke('#461C0D')
  line(158,247,187,276)
  line(241,247,212,276)
  strokeWeight(3)
  line(188,277,212,277)
  
  //cheeks
  noStroke()
  fill(225,0,0,50)
  circle(169,219,15)
  circle(231,219,15)
  
  // HAIR (Left side)
  strokeWeight(4)
  stroke('rgb(54,7,7)')
  fill('rgb(54,7,7)')
   circle(140, 230, 20)
  circle(135, 225, 20)
  circle(130, 220, 20)
  circle(140, 225, 20)
  circle(140, 240, 20)
  circle(151, 243, 12)
  circle(153, 260, 20)
  circle(140, 260, 20)
  stroke('rgb(71,18,18)')
  fill('rgb(71,18,18)')
  circle(143, 250, 20)
  circle(134, 173, 20)
  circle(135, 135, 20)
  circle(130,130,20)
  circle(136,140,20)
  circle(139,143,20)
  circle(146,149,20)
  circle(146,139,20)
  circle(131,157,20)
  circle(143,162,20)
  circle(137,185,20)
  circle(136,201,20)
  circle(123,173,20)
  stroke('rgb(54,7,7)')
  fill('rgb(54,7,7)')
  circle(126,187,20)
  circle(119,203,20)
  circle(169,126,20)
  circle(161,135,20)
  circle(182,135,20)
  circle(159,149,20)
  circle(118,149,20)
  circle(150,174,20)
  circle(139,211,20)
  circle(142,193,20)
  circle(147,116,20)
  circle(164,273,20)
  circle(178,285,20)
  circle(143,300,20)
  circle(124,269,20)
  circle(137,274,20)
  circle(132,286,20)
  circle(221,289,20)
  circle(233,297,20)
  circle(248,298,20)
  circle(263,292,20)
  circle(268,275,20)
  circle(165,269,20)
  circle(174,301,20)
 stroke('rgb(71,18,18)')
  fill('rgb(71,18,18)')
  circle(153,127,20)
  circle(165,108,20)
  circle(187,120,20)
  circle(182,103,20)
  circle(177,114,20)
  circle(124,248,20)
  circle(121,228,20)
  circle(129,240,20)
  circle(146,279,20)
  circle(160,291,20)
  circle(241,283,20)
  circle(257,278,20)
  circle(233,273,20)
  circle(225,284,20)
  circle(175,276,15)
  circle(238,267,20)
  circle(243,275,25)
  circle(220,278,10)
  circle(158,305,15)

  
  
  
  //HAIR (RIGHT SIDE)
  stroke('rgb(73,24,24)')
  fill('rgb(73,24,24)')
  circle(260, 230, 20)
  circle(265, 225, 20)
  circle(270, 220, 20)
  circle(260, 225, 20) 
  circle(260, 240, 20)
  circle(250, 243, 12)
  circle(248, 260, 20)
  circle(260, 260, 20)
  circle(257, 250, 20)
  circle(272, 173, 20)
   stroke('rgb(67,9,9)')
  fill('rgb(67,9,9)')
  circle(270, 135, 20)
  circle(275, 150, 20)
  circle(280, 165, 20)
  circle(275, 205, 20)
  circle(260, 210, 20)
  circle(270, 210, 20)
  stroke('rgb(54,7,7)')
  fill('rgb(54,7,7)')
  circle(270, 175, 20)
  circle(255, 190, 20)
  circle(265, 205, 20)
  circle(252,263,20)
  circle(254,162,20)
  circle(254,173,20)
  circle(262,149,20)
  circle(246,134,20)
  circle(277,188,20)
  circle(269,259,20)
  circle(243,149,20)
  circle(268,162,20)
  circle(273,233,20)
  circle(275,246,20)
  circle(258,127,20)
  circle(201,127,20)
  circle(219,131,20)
  circle(230,138,20)
  circle(197,106,20)
  circle(208,110,20)
  circle(231,117,20)
  circle(217,119,20)
  circle(244,117,20)
  stroke('rgb(73,24,24)')
  fill('rgb(73,24,24)')
  circle(220,102,20)
  circle(236,102,20)
  circle(236,129,20)
  
}

 

SELF PORTRAIT

For this assignment, I decided to do a simple portrait as it was my first time coding. However, I did want to play around with the background and try new codes, so I decided to do a background that changes color at random when you toggle around the mouse. At first, I thought it was too hard, and it took me time to figure out the codes, but I watched some tutorials on YouTube and went over the presentations in class, and I finally figured it out and was definitely proud of the outcome. I honestly found it difficult to figure out the shapes and where everything goes, but the Mouse X and Y function was a lifesaver, and it was a matter of getting the hang of it. The hair, however, was my least favorite part as it was getting really confusing with the placements of each circle. Maybe there was an easier way instead of just repeating each circle in a different location, so hopefully, I will figure it out in the future. Overall, after a long process of figuring out each shape, size, and code I’m happy with the outcome.

here is the background code which I am most proud of:

let x, y, r, g, b;

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

function draw() {
  // background that changes color
  background(mouseX, mouseY, 100, 7);
  r = random(0, 255);
  g = 0;
  b = random(0, 255);
  x = random(0, 600);
  y = random(0, 400);
  noStroke();
  fill(r, g, b, 100);
  circle(x, y, 24);

here is my portrait!

here is the code:

let eyeRX = 170;
let eyeLX = 225;
let speed = 15;
let x, y, r, g, b;

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

function draw() {
  // background that changes colour
  background(mouseX, mouseY, 100, 7);
  r = random(0, 255);
  g = 0;
  b = random(0, 255);
  x = random(0, 600);
  y = random(0, 400);
  noStroke();
  fill(r, g, b, 100);
  circle(x, y, 24);
  print(mouseX + "," + mouseY);

  //neck
  fill(240, 190, 120);
  stroke(0, 0, 0);
  strokeWeight(1);
  rect(200, 280, 70, 150, 100);

  //   left ear
  fill(240, 190, 120);
  stroke(0, 0, 0);
  strokeWeight(1);
  circle(135, 177, 25);

  //   right ear
  fill(240, 190, 120);
  stroke(0, 0, 0);
  strokeWeight(1);
  circle(264, 177, 25);

  // face
  fill(240, 190, 120);
  stroke(0, 0, 0);
  strokeWeight(1);
  ellipse(200, 160, 125, 185);

  //nose
  noFill();
  arc(199, 183, 20, 15, 270, 90);

  // hair
  noStroke();
  fill(46, 28, 17);

  //  top hair
  circle(140, 60, 40);
  circle(183, 95, 40);
  circle(205, 88, 40);
  circle(160, 100, 40);
  circle(170, 50, 40);
  circle(200, 40, 40);
  circle(230, 50, 40);
  circle(233, 94, 40);
  circle(260, 60, 40);

  // More top curls
  fill(46, 28, 10);
  circle(160, 80, 40);
  circle(190, 70, 40);
  circle(220, 70, 40);
  circle(250, 80, 40);

  // Left side curls
  fill(46, 28, 17);
  circle(130, 90, 40);
  circle(120, 120, 40);
  circle(134, 135, 40);
  circle(142, 113, 40);

  // Right side curls
  fill(46, 28, 17);
  circle(270, 90, 40);
  circle(280, 120, 40);
  circle(262, 134, 40);
  circle(256, 115, 40);

  // eyebrows
  stroke(21, 19, 19);
  strokeWeight(2);
  noFill();
  arc(170, 135, 30, 10, PI, TWO_PI);
  arc(225, 135, 30, 10, PI, TWO_PI);

  //right eye
  fill(255);
  ellipse(eyeRX, 150, 20, 20);

  //left eye
  fill(255);
  ellipse(eyeLX, 150, 20, 20);

  //right pupil
  fill(51, 0, 0);
  ellipse(eyeRX, 150, 10, 10);

  //left pupil
  fill(51, 0, 0);
  ellipse(eyeLX, 150, 10, 10);

  //  mouth up
  strokeWeight(1);
  fill("pink");
  rectMode(CENTER);
  arc(200, 209, 30, 15, PI, 0);

  //   mouth down
  strokeWeight(1);
  fill("pink");
  rectMode(CENTER);
  arc(200, 209, 30, 15, 0, PI);

  //   lip line
  arc(200, 210, 27, 1, 0, PI);

  //shirt
  fill(126, 167, 189);
  noStroke();
  rect(200, 380, 199, 200, 50);

  //   shirt neck line
  fill(240, 190, 120);
  noStroke();
  arc(200, 280, 90, 80, 0, PI, CHORD);

  //   short neck shadow
  noFill();
  strokeWeight(8);
  stroke("#212E2D");
  arc(200, 280, 90, 80, 0, PI);

  noStroke();
  fill(64, 102, 122);
  triangle(128, 315, 135, 400, 150, 400);
  triangle(265, 315, 267, 400, 250, 400);
}

 

Final – SpongBlob

Concept:

When I look at how F1 drivers train their reflexes, one of the machines inspired me to create this game. However, this game is not to train your reflexes but your memory. Inspired by this, my project, “SpongBlob” aims to enhance users’ color memory skills through an interactive game developed using Arduino and p5.js. This game not only serves as an entertaining experience but also as an educational tool to study color perception and memory. The game is also aimed for kids who are at risk of losing their focus span to the developing social media and short video world, which makes this game a tool for them to gain their focus back. This inspired the SpongeBob theme, so that it is more kid friendly.

Game setup:

The game has an interactive set up with 4 buttons replacing the keys on the keyboard. By pressing the buttons, the players are interacting with the game on the P5 sketch. This is shown in the following pictures:

As shown in the picture, the aesthetics of the game are really important especially since this game is also targeting kids. Having a nice set up of “Bikini Bottom” was really important in my opinion to make sure that the game is not just functioning well but also looks appealing to the players.

The use of these buttons makes it so much easier than the keyboard according to the users that tested the game. Moreover, when user testing, most of the students suggested having a less hectic and messy background so that the player can focus and memorize the colors of the circles without getting distracted by the background. There was some debate over whether the users should get to see the score they achieved or not. Since it is a memory game, I thought the players should focus more on memorizing and enhancing their focus span than focus on the score, so I did not make it visible to the players.

P5 Sketch Full Screen Link: https://editor.p5js.org/jana_mohsen/full/Vymgnruow

Code:

// Define pin numbers for buttons
const int leftButtonPin = 2;
const int upButtonPin = 3;
const int downButtonPin = 4;
const int rightButtonPin = 5;

void setup() {
  Serial.begin(9600);
  // Set button pins as inputs
  pinMode(leftButtonPin, INPUT);
  pinMode(upButtonPin, INPUT);
  pinMode(downButtonPin, INPUT);
  pinMode(rightButtonPin, INPUT);
}

void loop() {
  // Read button states and send data over serial
  int leftButton = digitalRead(leftButtonPin);
  int upButton = digitalRead(upButtonPin);
  int downButton = digitalRead(downButtonPin);
  int rightButton = digitalRead(rightButtonPin);

  // Send button states to serial
  Serial.print(leftButton);
  Serial.print(",");
  Serial.print(upButton);
  Serial.print(",");
  Serial.print(downButton);
  Serial.print(",");
  Serial.println(rightButton);

  // Delay to control the rate of data transmission
  delay(100);
}

 

This Arduino sketch manages four buttons connected to the board, using pins 2, 3, 4, and 5 for left, up, down, and right buttons respectively. The setup() function initializes serial communication at 9600 baud and sets the button pins to input mode. The loop() function continuously reads the state of each button using digitalRead() and sends these states over the serial connection using Serial.print(). Each button state is outputted sequentially and separated by commas, with a newline at the end of each set via Serial.println(). A delay(100) is included to control the rate of data transmission, preventing data overflow and ensuring manageable communication speeds.

function drawGamePage() {
  //song.play();
  background(backgroundImage);
  if (currentPage == 2 || currentPage == 3) {
    let selected = -1;

    // Map arrow keys to grid positions
    if (ArrowUp == 1 && ButtonPressed == 0) {
      selected = 0; // Top-left
      ButtonPressed = 1;
    } else if (ArrowDown == 1 && ButtonPressed == 0) {
      selected = 1;
      ButtonPressed = 1; // Top-right
    } else if (ArrowRight == 1 && ButtonPressed == 0) {
      selected = 2;
      ButtonPressed = 1; // Bottom-left
    } else if (ArrowLeft == 1 && ButtonPressed == 0) {
      selected = 3;
      ButtonPressed = 1; // Bottom-right
    } else if (
      ArrowLeft == 0 &&
      ArrowRight == 0 &&
      ArrowUp == 0 &&
      ArrowDown == 0
    ) {
      ButtonPressed = 0;
    }

    // Check if the selected color matches the currentColor
    if (selected != -1) {
      if (gridColors[selected] == currentColor) {
        prepareNextLevel();
      } else {
        currentPage = -1;
      }
    }
  }

  fill(nextColor);
  ellipse(windowWidth / 2, 200, 120, 120);
  let padding1 = windowWidth / 2 - 75;
  let padding2 = windowHeight / 2 - 100;
  for (let i = 0; i < gridSize; i++) {
    for (let j = 0; j < gridSize; j++) {
      fill(gridColors[i * gridSize + j]);
      ellipse(
        padding1 + j * (ellipseSize + 50),
        padding2 + i * (ellipseSize + 50),
        ellipseSize,
        ellipseSize
      );
    }
  }
}

function drawOverPage() {
  //laugh.play();
  background(gameOverImage);
  textAlign(CENTER, CENTER);
  textSize(32);
  fill(0);
  text("Game Over", width / 2, height / 2 - 100);

  // Draw "Again" button
  fill(200); // Light grey button background
  rect(width / 2 - 100, height / 2, 200, 50);
  fill(0); // Black text
  text("Again", width / 2, height / 2 + 25);

  // Draw "Home" button
  fill(200); // Light grey button background
  rect(width / 2 - 100, height / 2 + 70, 200, 50);
  fill(0); // Black text
  text("Home", width / 2, height / 2 + 95);
}

function drawInstructionPage() {
  //song.play();
  background(instructionsImage); 
  fill(0);
  textSize(24);
  textAlign(LEFT, LEFT);
  text("Instructions", width - 810, height - 710);
  textSize(16);
  text("Welcome to the Memory Game! Here's how to play:", width - 840, height - 655);
  text("1. Memorize the colors shown on top of the screen.", width - 840, height - 625);
  text("2. Use the buttons to select the correct color from the grid.", width - 840, height - 595);
  text("3. Match the colors correctly to advance to the next level.", width - 840, height - 565);
  text("Press Space Bar to select Serial Port", width - 840, height - 535);
  textAlign(CENTER, CENTER);
  rect(width - 780, height / 2 - 40, 80, 40);
  fill(200);
  text("Continue", width - 740, height / 2 - 20);
}


function draw() {
  background(220);
  if (!serialActive) {
    text("Press Space Bar to select Serial Port", 20, 30);
  } else {
    text("Connected", 20, 30);
  }

  if (currentPage == 0) {
    drawStartPage();
  } else if (currentPage == 1) {
    drawInstructionPage();
  } else if (currentPage == 2) {
    drawFirstPage();
  } else if (currentPage == 3) {
    drawGamePage();
  } else if (currentPage == -1) {
    drawOverPage();
  }
}

 

The P5 sketch code is maily about the game page which has the conditions that make the game more interesting. The drawGamePage() function in this p5.js code is designed for a memory game, where it handles the game logic and user interactions during gameplay. It first sets the background and checks if the game is on specific pages (like a game level). The function maps arrow key inputs to grid selections, managing state with a ButtonPressed flag to avoid repeated selections. If a selected color from the grid matches a target color (currentColor), the game progresses to the next level; otherwise, it switches to a game over page. It dynamically renders colored ellipses on a grid, representing game elements. Additionally, other functions like drawOverPage() handle the game over screen, displaying buttons for restarting or returning to the home screen, and drawInstructionPage() displays the game instructions. The main draw() function coordinates these pages based on the current game state, updating the display and handling transitions between different parts of the game, such as starting, instructions, gameplay, and game over scenarios.

Aspects of the project I am proud of:

Game Logic Implementation: The effective mapping of user inputs (arrow keys) to game actions and the incorporation of game state management ensures that the gameplay is both challenging and engaging.

Serial Communication: The use of serial communication to connect Arduino inputs to the p5.js game logic demonstrates a robust application of cross-platform communication techniques, vital for interactive media projects.

Areas for Future Improvement:

Complexity and Features: Introducing additional levels of difficulty, more complex game mechanics, or multiplayer capabilities could increase the game’s replay value and appeal to a broader audience. Also having new shapes, characters to memorize and not just the circles can be fun!

Extensive Testing and Debugging: Conducting more thorough testing across different platforms and setups could identify and resolve any existing bugs or issues with user interactions, ensuring a smooth and reliable user experience.

Final Project – Saeed Lootah

Russian Roulette Final Project

To summarize this project was about making a game that was like Russian Roulette but without the gun. Instead two players would stand opposite each other and would have two options. First there is a “gun” (it just exists in the p5js sketch) that is loaded with a certain amount of live rounds and the rest are blank and then like the real Russian Roulette each player would get chances to shoot the other opponent or not or shoot themselves. If a player shoots themselves and its a blank then they get another turn making it more likely that the next round will be live and hence more likely that they will win and if they do shoot the other player and it is blank then their turn ends and they swap and the cycle continues. There is also 3 lives for each player and if one of them loses, i.e. runs out of lives they have to use a shock pen. The purpose of the shock pen was to create some kind of stakes just like the real Russian Roulette but more sane.

To me this project represents more than a fun creation. Genuinely, while I did enjoy it this was a huge learning experience and not just when it came to coding and soldering and whatever other physical things I did. I’ll explain how so as I go from beginning to end.

I originally had this idea before the midterm and I thought about creating a Russian roulette for my midterm. I chose a different path and made a Mexican Standoff game for my midterm. It was arguably slightly harder to make and for me going into it I really had little idea what I was doing. I learned how to change from different scenes (thanks to Pi telling me about a scene management class), but more importantly I learned a lot about planning as halfway through making it I had to sit down and I sketched a diagram of what I wanted the game to be like on a whiteboard in a library study room.

So in the midterm I just began to plan things properly and I wasn’t very efficient at it. So going into this project I also planned but unlike before I also had to consider the physical: Making and wiring all the components that I’ll need. Even planning it was difficult to be honest. Difficult because I didn’t really know where to start but also I didn’t know what was available in the intro to IM lab. I was very fortunate to learn more about what was available through our lessons where we learned about renting equipment but also saw the different sections in the lab, like the consumables or the blue closet (i dont know if it has a name).

Then when I had to start I had to learn serial communication. I think a lot of people can agree when I say it was very confusing and I would argue it remained a problem with my project from beginning to end even though I thought I understood it at the beginning. That was the biggest hurdle by far mentally because it had been bothering me for a couple days before because when I would go to try to understand the code from professor Aaron and change it to fit my project (4 buttons, a potentiometer and 2 LEDs) it wouldn’t work even though just before that it I felt I understood it when working with Snehil and Khalifa on the previous assignment.

  • Include some pictures / video of your project interaction
  • How does the implementation work?
    • Description of interaction design
    • Description of Arduino code and include or link to full Arduino sketch
    • Description of p5.js code and embed p5.js sketch in post
    • Description of communication between Arduino and p5.js
  • What are some aspects of the project that you’re particularly proud of?
  • What are some areas for future improvement?

The way that my project works is first that the Arduino detects the button presses from each of the four buttons as well as the input from the potentiometer. Then using serial communication the Arduino sends the signals received from the buttons and potentiometer to the p5js sketch and in response the sketch relays back a message to create a handshake.

The p5js code is slightly more complex. First I have multiple scenes each of which managed by a scene management class where depending on the number that the class receives it calls on different functions, each of those functions are actually the scenes. For example the disclaimer scene is actually just a function which has all the shapes and so on being drawn and the function itself is only called upon when the scene number is 2. The same goes for the other scenes. As for the game itself: Firstly, after the animation there is an array of booleans totaling 6. If an element is true it means it is a live round and vice versa and then there is another global variable called selected_round and that is added up incrementally to signify which round is being selected from the chamber. As for the players, they have two options represented as two buttons. One button (the red one) means that the player can shoot themselves, and the other (the blue button) means that the player can shoot the other person in front of them. If the player chooses to shoot themselves and it’s a blank then it remains their turn and makes it more likely that the next round will be live and hence more likely that they can win the round. However, if a player chooses to shoot the other but it’s blank they switch turns making the odds in the favor of the other player as its now more likely for them that the next round is live. Furthermore, I added difficulties which means that depending on the difficulty chosen by the player there will be a varying amount of live rounds with the easiest difficulty being “Standard” which means only one live round, and the hardest being “Masochist” which can have between 3 and 5. Each of the players have 3 lives, then at the end of it all, if they choose to the loser has to use a shock pen so that there are stakes and to make the game more interesting.

I’m particularly proud of the UI elements, specifically holding down a button to go to the next page and so on. To me it felt the most satisfying.

For the future there are a lot of things I would change. I would have started by making the game aspect first, as I had made the mistake of starting at the start page ironically. This would have made my life a lot easier but at the same time I am somewhat glad that I did what I did because I felt that the UI was a large part of what I liked and I hope others liked about the game.


The sketch on p5js may be a bit weird because I had coded it on vscode and made it such that the canvas fit my mac screen so it may take a while to load and/or may be hard to use.
 

Final – Shereena AlNuaimi

VR Archery

My game was all about creating a virtual archery game and linking a flex sensor to the bow, so it detects the bending motion and once bent the arrow shoots onto the target. For the theme of the entire game, I decided to link it with my childhood as well and use Minecraft as the main theme. As a 9 year old, I used to go to the archery range and play Minecraft at the time so it brought up a sense of nostalgic feeling.

For the bow, in order to give it a pixelated look, I decided to super glue the tiny wooden square blocks and paint over it. I attached the flex sensor on the top of the bow and attached a string to the flex sensor so when the string is pulled it shoots the arrow.

Future Improvements:

For future improvements, I’d like to add more visuals. I felt like it would’ve been more engaging if I did.

Pictures and Documentation:

Soldering documentation :

https://youtube.com/shorts/Cn9bonfPVeI?feature=share

Game play:

https://youtube.com/shorts/6olYsuVx5k8?feature=share

The Game & Codes:

<iframe src=”https://editor.p5js.org/sa6607/full/wcM5zq8Fr”></iframe>

P5 Code:

const serial = new p5.WebSerial();
let startButton;
let portButton;
let closeButton;
let sensorValue = 0;
let width = 900;
let height = 506;
let arrowSpeed = 5; // Speed at which arrow moves
let arrowDirection = 1;
let score = 0;
let shooting = false; // Indicates whether the arrow is currently being shot
let arrowX = width / 2; // X-coordinate of the arrow
let arrowY = height; // Y-coordinate of the arrow
let arrowScaleX = 1;
let arrowScaleY = 1;
let arrowWidth = 60;
let arrowHeight = 120;
let targetX = width / 2;
let targetY = height / 2;
let targetRadius = 100;

let started = false;

let bgImg;
let arrowImg;
let targetImg;
let sliderX = width - 50;
let sliderY = height / 2;
let lastScore = 0;
let textOpacity = 0;
let sliderHeight = 0;
let startShootFlag = 0;
let sliderIncrementor = 1;
let sliderTotalHeight = 100;
let startShootThreshHold = 40; //set flex sensor value at which we start for targetting

function allSerialStuff() {
  if (!navigator.serial) {
    alert("WebSerial is not supported in this browser. Try Chrome or MS Edge.");
  }
  // check for any ports that are available:
  serial.getPorts();
  // if there's no port chosen, choose one:
  serial.on("noport", makePortButton);
  // open whatever port is available:
  serial.on("portavailable", openPort);
  // handle serial errors:
  serial.on("requesterror", portError);
  // handle any incoming serial data:
  serial.on("data", serialEvent);
  serial.on("close", makePortButton);
  // add serial connect/disconnect listeners:
  navigator.serial.addEventListener("connect", portConnect);
  navigator.serial.addEventListener("disconnect", portDisconnect);
}

function serialEvent() {
  sensorValue = Number(serial.read());
  console.log(sensorValue);
  //if certain value from flex sensor get passed we get prepared for the shoot
  if (!shooting && sensorValue > startShootThreshHold) {
    startShootFlag = 1;
    sliderHeight = sensorValue + 20; // add 20 to elevate the value we need something in between 0-120
  }
  //if that value again crossed then we shoot
  if (!shooting && sensorValue < startShootThreshHold && startShootFlag) {
    startShootFlag = 0;
    if (!shooting && arrowY == height) {
      shooting = true; // Start shooting
    }
  }
}

// if there's no port selected,
// make a port select button appear:
function makePortButton() {
  // create and position a port chooser button:
  portButton = createButton("Choose Port");
  portButton.position(innerWidth / 2, 10);
  portButton.center("horizontal");
  // give the port button a mousepressed handler:
  portButton.mousePressed(choosePort);
}

// make the port selector window appear:
function choosePort() {
  if (portButton) portButton.show();
  serial.requestPort();
}

// open the selected port, and make the port
// button invisible:
// open the selected port, and make the port
// button invisible:
function openPort() {
  // wait for the serial.open promise to return,
  // then call the initiateSerial function
  serial.open().then(initiateSerial);

  // once the port opens, let the user know:
  function initiateSerial() {
    console.log("port open");
  }
  // hide the port button once a port is chosen:
  if (portButton) portButton.hide();
  makeCloseButton();
  if (closeButton) closeButton.show();
}

// pop up an alert if there's a port error:
function portError(err) {
  alert("Serial port error: " + err);
}
// read any incoming data as a string
// (assumes a newline at the end of it):

// try to connect if a new serial port
// gets added (i.e. plugged in via USB):
function portConnect() {
  console.log("port connected");
  serial.getPorts();
}

// if a port is disconnected:
function portDisconnect() {
  serial.close();
  console.log("port disconnected");
}

// if there's no port selected,
// make a port select button appear:
function makeCloseButton() {
  // create and position a port chooser button:
  closeButton = createButton("Close Port");
  closeButton.position(innerWidth / 2, 10);
  closeButton.center("horizontal");
  // give the close port button a mousepressed handler:
  closeButton.mousePressed(closePort);
}

function closePort() {
  serial.close();
  if (closeButton) closeButton.hide();
}

function preload() {
  bgImg = loadImage("/assets/background.jpg");
  targetImg = loadImage("/assets/target.png");
  arrowImg = loadImage("/assets/arrow.png");
}

function setup() {
  createCanvas(width, height);

  startButton = createButton("Start Game");
  startButton.addClass("start-button");
  startButton.position(innerWidth / 2, innerHeight / 2 + 10);
  startButton.center("horizontal");
  startButton.mousePressed(startGame);

  allSerialStuff();
}

function draw() {
  imageMode(CORNERS);
  image(bgImg, 0, 0, width, height);

  if (!started) {
    drawMenu();
  } else {
    // Draw target
    drawTarget();

    if (startShootFlag) {
      drawSlider();
    }

    //Draw score
    textAlign(LEFT, TOP);
    textSize(26);
    fill("red");

    text("Score: " + score, 10, 10);

    makeShooting();
    drawArrow();
    drawAddedScore();
  }
}

function drawMenu() {
  textSize(48);
  fill("#ff0033");
  textStyle(BOLD);
  textAlign(CENTER, BASELINE);
  text("VR Archery", width / 2, height / 2 - 100);
}

function startGame() {
  if (startButton) startButton.hide();
  started = true;
}

function makeShooting() {
  if (shooting) {
    // Calculate the trajectory towards the target
    let deltaY = height - targetY; // Difference in y-coordinates between arrow and target
    arrowY -= deltaY / 50; // Move the arrow towards the target
    if (arrowScaleY > 0.4) {
      arrowScaleY -= 0.004;
    }
    if (arrowScaleX > 0.4) {
      arrowScaleY -= 0.005;
    }
    // Stop shooting when arrow reaches the target
    if (arrowY - (arrowHeight * arrowScaleY) / 2 <= targetY) {
      shooting = false;

      // Check if the arrow hits the target
      let distance = dist(arrowX, arrowY - (arrowHeight * arrowScaleY) / 2, targetX, targetY); // Calculate distance between arrow tip and target center
      if (distance <= targetRadius - 20) {
        console.log("Hit!");
        arrowSpeed = 0;
        textOpacity = 255;
        updateScore(distance);
        //reset arrow after 2 seconds
        setTimeout(() => {
          arrowY = height;
          arrowScaleX = 1;
          arrowScaleY = 1;
          arrowSpeed = 5;
          textOpacity = 0;
        }, 2000);
      } else {
        console.log("Miss!");
        arrowY = height;
        arrowScaleX = 1;
        arrowScaleY = 1;
        shooting = false;
      }
    }
  } else {
    // Move arrow
    arrowX += arrowSpeed * arrowDirection;
    if (arrowX >= width || arrowX <= 0) {
      arrowDirection = -arrowDirection; // Reset arrow when it goes beyond the canvas
    }
  }
}

function drawTarget() {
  imageMode(CENTER);
  image(targetImg, targetX, targetY, targetRadius * 2, targetRadius * 2);
}

function drawArrow() {
  imageMode(CENTER);
  image(arrowImg, arrowX, arrowY, arrowWidth * arrowScaleX, arrowHeight * arrowScaleY);
}

function drawAddedScore() {
  fill(80, textOpacity);
  text("+ " + lastScore, targetX + 70, targetY - 60);
}

function drawSlider() {
  line(sliderX, sliderY, sliderX, sliderY + 120);
  line(sliderX - 10, sliderY, sliderX + 10, sliderY);
  line(sliderX - 10, sliderY + 60, sliderX + 10, sliderY + 60);
  line(sliderX - 10, sliderY + 120, sliderX + 10, sliderY + 120);
  fill(sliderHeight + 100, 200, 0);
  rect(sliderX - 10, sliderY, 20, sliderHeight);
}

//update score
function updateScore(distance) {
  console.log(distance);
  if (distance < 10) {
    score += 100;
    lastScore = 100;
  } else if (distance < 25) {
    score += 80;
    lastScore = 80;
  } else if (distance < 40) {
    score += 60;
    lastScore = 60;
  } else if (distance < 55) {
    score += 40;
    lastScore = 40;
  } else {
    score += 20;
    lastScore = 20;
  }
}

//handle mouse click event
function mouseClicked() {
  if (!shooting && arrowY == height) {
    //If arrow is not currently being shot
    shooting = true; // Start shooting
  }
}

function windowResized() {
  if (startButton) startButton.position(innerWidth / 2, innerHeight / 2 + 10).center("horizontal");
  if (portButton) portButton.center("horizontal");
  if (closeButton) closeButton.center("horizontal");
}

Arduino Code:

void setup() {
  Serial.begin(9600);
}

void loop() {
  int analogValue = analogRead(A0);
  byte byteToSend = map (analogValue, 0, 1023, 0, 255);
  Serial.write(byteToSend);
  delay(50);
}

 

HAPPINESS LEVEL – SHAIKHA ALKAABI

The initial idea of this project is a bit different from what I ended up with. The initial idea was designed for two players, each placing one hand on a heart rate monitor. This game uses heart rate data to measure and display the level of affection or excitement between the participants. The faster the heartbeats, the higher the presumed love connection. But after I heard that we’d have to return the items we borrowed from the IM lab I thought it would be better if I order my own two heart rate monitors and solder them myself, then I wouldn’t have to take apart my hard work. With my not-so-good soldering skills I ruined one heart rate monitor and had only one to work with. I had to improvise and solve this issue working with only one heart rate monitor which led to changing the theme of the game a bit. Professor Aaron helped me with coming up with a new game that measures the players Happiness Level by reading their heart rates through the heart monitor. The game was initially supposed to start by pressing on a yellow button but due to time constrains and many other technical difficulties, such as linking the Arduino and the P5 together, I still managed to make it work fine with the keyboard even though I feel like the yellow button gives it more of a “gamey” feel to it which is what I would’ve much preferred.

Arduino Code:

const int buttonPin = 3;      // Pin where the button is connected
const int heartRatePin = A0;  // Analog pin for heart rate sensor

int heartRateValue = 0;
bool buttonPressed = false;

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);  // Set the button pin as input with internal pull-up resistor
  pinMode(heartRatePin, INPUT);
  Serial.begin(9600);
  while (Serial.available() <= 0) {  // on/blink while waiting for serial data
    Serial.println("0,0");
    delay(50);
  }
}

void loop() {
  heartRateValue = analogRead(heartRatePin);  // Read the value from the heart rate sensor
  int bpm = calculateBPM(heartRateValue);     // Convert the analog reading to BPM

  // Read button state
  int buttonState = 1 - digitalRead(buttonPin);

 

  while (Serial.available()) {
  

    int left = Serial.parseInt();

    if (Serial.read() == '\n') {
      Serial.print(buttonState);
      Serial.print("0,50");
      Serial.println(bpm);
    }
  }


  // Always send the current heart rate and button state
}

// Function to simulate BPM calculation - replace this with your sensor-specific calculation
int calculateBPM(int sensorValue) {
  return sensorValue / 10;  // Simplified calculation for demonstration
}

P5.Js Code:

class MainMenu extends Menu {
  constructor(id) {
    super(id);

    this.pos = createVector(width / 2, height / 2.7);
    this.size = 240;
    this.strokeCol = color(
      random(100, 255),
      random(100, 255),
      random(100, 255)
    );
    this.hearts = [];
    for (let i = 0; i < 20; i++) {
      this.hearts.push({ x: random(width), y: random(height) });
    }
    this.random_seed = random(100, 10000); //use for heard animations in the back
    this.heartPos = { x: width / 2, y: height * 2 };
  }

  render() {
    background("#24182e");
    textAlign(CENTER, CENTER);
    textSize(44);
    textFont(pixel_font);
    fill("#8249c6");
    stroke(this.strokeCol);
    strokeWeight(4);

    text("HAPPINESS  LEVEL", width / 2, 50);
    //change strokcol every 20 farmes
    if (frameCount % 60 == 0) {
      this.strokeCol = color(
        random(100, 255),
        random(100, 255),
        random(100, 255)
      );
      this.random_seed = random(100, 10000);
    }
    textSize(30);
    stroke(200, 100, 100);
    push();
    randomSeed(this.random_seed);
    textFont("arial");
    for (let h of this.hearts) {
      for (let h2 of this.hearts) {
        if (dist(h.x, h.y, h2.x, h2.y) < 10) {
          strokeWeight(2);
          line(h.x, h.y, h2.x, h2.y);
        }
      }
      text("♥", h.x, h.y);
      h.x = lerp(h.x, random(width), 0.01);
      h.y = lerp(h.y, random(height), 0.01);
    }
    pop();
    push();
    textFont("arial");
    textSize(160);
    this.heartPos.y = lerp(this.heartPos.y, height / 1.5, 0.1);
    stroke(255);
    fill("#B28CDEAA");
    text("♥", this.heartPos.x, this.heartPos.y);
    textSize(30);
    noStroke();
    fill(255);
    textFont(pixel_font);
    text(
      "PLACE YOUR FINGER ON THE HEART",
      this.heartPos.x,
      this.heartPos.y + 100
    );
    pop();
    noStroke();
  }
}
class GameMenu extends Menu {
  constructor(id) {
    super(id);

    this.heart = new Heart(createVector(width / 2, height / 2.7), 240);
  }

  render() {
    textAlign(CENTER, CENTER);
    textSize(18); 
    background("#24182e");
    fill("#A88DC7");
    text("CHECKING YOUR LOVE LEVEL!", width / 2, height - 30);
    fill("#8249C67C");
    textFont("arial");
    textSize(34);

    for (let i = 0; i < 12; i++) {
      for (let j = 0; j < 8; j++) {
        let x = map(i, 0, 11, 0, width);
        let y = map(j, 0, 7, 0, height);
        if (frameCount % 40 < 20) {
          if (i % 2 == 0 || j % 2 == 0) {
            text("♥", x, y);
          }
        } else {
          if (i % 2 != 0 || j % 2 != 0) {
            text("♥", x, y);
          }
        }
      }
    }

    this.heart.render();
  }

  update() {
    this.heart.update();
    // Removed the timer decrement and check
  }

  reset() {
    this.heart = new Heart(createVector(width / 2, height / 2.7), 220);
  }
}
class EndMenu extends Menu {
  constructor(id) {
    super(id);

    this.finalScore = null;

    this.hearts = [];
    for (let i = 0; i < 2; i++) {
      this.hearts.push({ x: random(width), y: random(height) });
    }
    this.random_seed = random(100, 10000); //use for heard animations in the back
  }

  render() {
    background("#24182e");
    push();
    stroke(200, 100, 100);
    randomSeed(this.random_seed);
    textFont("arial");
    textSize(34);
    for (let h of this.hearts) {
      for (let h2 of this.hearts) {
        if (dist(h.x, h.y, h2.x, h2.y) < 100) {
          line(h.x, h.y, h2.x, h2.y);
        }
      }
      text("♥", h.x, h.y);
      h.x = lerp(h.x, random(width), 0.01);
      h.y = lerp(h.y, random(height), 0.01);
    }
    if (frameCount % 60 == 0) {
      this.random_seed = random(100, 10000);
    }
    pop();

    fill("#A88DC7");
    stroke(255);
    textFont(pixel_font);
    textSize(60);
    textAlign(CENTER, CENTER);
    text("THANK YOU !", width / 2, 160);
    noStroke();
    textSize(24);
    // text(
    //   `${this.finalScore}\n\nYOUR COMPATIBILITY SCORE`,
    //   width / 2,
    //   height / 1.5
    // );
    //     push();
    //     noStroke();
    //     fill(0);
    //     rect(0, 0, width, height);

    //     fill(255);
    //     textStyle(BOLD);
    //     textAlign(CENTER, CENTER);
    //     textSize(96);
    //     text("GAME OVER", width / 2, height / 4);

    //     textSize(40);
    //     text(`COMPATIBILITY SCORE: ${this.finalScore}`, width / 2, height / 2);

    //     textStyle(NORMAL);
    textSize(16);
    text("TRY AGAIN?", width / 2, height - 60);
    text("Yes", 100, height - 60);
    text("No", width - 100, height - 60);
    push();
    textFont("arial");
    pop();

    //     textSize(40);
    //     text("YES? OR NO?", width / 2, 640);

    //     pop();
  }

  reset() {
    this.finalScore = null;
  }
}

 I’m content with the final product despite getting extremely sick two days before the showcase while having other final projects due the day before it. For future improvements I’d like to incorporate my initial idea and also add a different game mode, Lie Detector Mode, which sounds fun to make with using the heart rate monitor. Overall, I feel like I got exposed to many things in this course which makes me much more comfortable with the things we’ve been working with during the entirety of this semester. I’d also like to thank professor Aaron for being extremely patient and helpful with me 🙂