Rashed’s Final Project – A S A P B A B Y

 

Concept:

As soon as I heard that I would get to pick a topic again, I decided to go back to my old idea that did not cooperate with me during the midterm period: The Dance Dance Revolution arcade machine. I wanted to do this but in my own way. I’ve been really into looking at different aesthetics and, so far, my favorite aesthetic is the CottageCore aesthetic whic his basically the theme of living in the forest, fantasy, fairies, ..etc.

As I mentioned in my midterm, I have been really into this group named NewJeans and one of their music videos was focused mainly on the cottagecore aesthetic which I was really into. The song’s name is ASAP and I would highly recommend everyone to watch if you want your day to instantly be 1000 times better.

This is what a DDR machine looks like:

 

The game goes as follows:

Arrows spawn from the bottom of the screen and move to the top according to the rhythm of the song picked. The player must click/ step on the corresponding arrow button when it reaches a certain point.

NewJeans’ ASAP:

 

Challenges :

This project was a nightmare to make:

  1. The Circle Incident:

Writing the p5 code, I wanted to first start with ellipses that would spawn in four different x coordinates and that would disappear when I would click the arrow keys as they reach y 100. However, p5 had other plans. It just would not work no matter what I tried. Turns out, I just forgot to use the term “key ==” .

2. Mapping : (

I have a past with creating beat maps for the VR game Beat Saber and I knew that people got that idea from mapping DDR games. So, I looked up what DDR mapping looks like and I created one for the song ASAP. But after countless hours of trying to add the mapping for ASAP to p5, I realized that mapping was not for p5. Is it possible? Probably. Would I have tried even longer if I just had more time? Probably.

What I did to consider time was try to have the arrows spawn according to the song’s BPM(Beats Per Minute) and I just now realized as I’m writing this that I had the arrows SPAWN with the BPM, not hit the targetZone according to the BPM which makes so much sense now but it still is very fun.

 

3. The Horror of Serial Communication :0

For the weekend, I decided to go home and work on this there and that was one of the biggest mistakes I have ever made because I don’t like the serial communication part. I spent the whole weekend working on something that I don’t fully understand. I spent 5 hours my first night home and made zero progress. The following day, I caved and asked my brother who has a good amount of experience with C++ and even he could not figure out a solution.

Sunday evening I’m back on campus and I see Professor Aya who told me that my p5 sketch is only receiving 2 inputs instead of 4…… She fixed it in less than 2 minutes.

 

4. The Forbidden Restart Button :0

After getting familiar and actually understanding what and how Serial communication work, I wanted to add a simple button. So cute, I know :3.

Little did I know that button was made in the deepest, darkest pits of hell. For some reason, it just would not register the 1. I would check my arduino code and everything would work fine on the serial monitor. I spent hours on that and after asking my very nice classmate Marcus, he looked at my arduino code and figured out I added a teensy tiny “ln” in my println function…..

Not my proudest Rashed moment.

 

Game Design:

my game has four states for the title screen, game, gameOver, and YouWin.

I wanted to make my own backgrounds for these states instead of using pictures from the group. I used procreate  and a bunch of different elements from the theme and I just put them all together and I created these:

 

Title:

Game Over:

You Win:

Game:

 

I also designed the arrows myself 🙂

Code:

p5js Sketch:

This code outlines a rhythm-based game where players must synchronize their inputs with arrows moving on the screen to the beat of a background song. The game functions in various states such as “start,” “game,” “gameOver,” and “win,” each presenting unique visuals and interactions.

The preload function loads all necessary assets, including images and sounds, preparing the game for a smooth launch. Following this, the setup function establishes the gaming environment by creating a canvas and configuring text settings.

During gameplay, the draw function operates as the continuous loop that directs the flow of the game. It adjusts what’s displayed based on the game state and also checks the status of the serial port for hardware connectivity, crucial for linking external controllers like buttons.

Player interaction involves using buttons to accurately match onscreen arrows as they align with a designated hit zone. Successful matches increase the player’s score, whereas missed arrows result in a loss of lives. This mechanic tests both rhythm and timing.

The game mechanics are finely tuned; arrows are generated at intervals determined by the song’s BPM, creating a consistent rhythmic challenge. The game monitors for end conditions, either when the player runs out of lives or when the song concludes, leading to different game states like “win” or “gameOver.”

The integration with the Arduino through serial communication allows for the use of specialized controllers.

 

let arrows = [];
let images = {};
let gameState = "start";
let score = 0;
let lives = 5;
const arrowSpeed = 2;
const hitZoneY = 100;
const tolerance = 30;
let leftButton = 0;
let rightButton = 0;
let upButton = 0;
let downButton = 0;
let restartButton = 0;
let buttonispressed = 0;
// Song and BPM (beats per minute)
let song;
let songBPM = 134; // song's Beats Per Minute
// Images
let titleImage, gameOverImage, youWinImage, gameImage;
// Interval ID for arrow spawns
let arrowInterval;

// Preload function to load assets
function preload() {
  song = loadSound("NewjeansASAP.mp3");
  images.left = loadImage("Left.PNG");
  images.up = loadImage("Up.PNG");
  images.down = loadImage("Down.PNG");
  images.right = loadImage("Right.PNG");
  titleImage = loadImage("TitleBG.PNG");
  gameOverImage = loadImage("GameOverBG.PNG");
  youWinImage = loadImage("YouWinBG.PNG");
  gameImage = loadImage("GameBG.JPG");
}

// Setup function
function setup() {
  // Create canvas
  createCanvas(windowWidth, windowHeight);
  textAlign(CENTER, CENTER);
  textSize(32);
}

function draw() {
  // Display connection status if serial port is not active
  if (!serialActive) {
    text("Press Space Bar to select Serial Port", 20, 30);
  } else {
    text("Connected", 20, 30);
  }
  background(0);

  // Draw different game screens based on game state
  switch (gameState) {
    case "start":
      image(titleImage, 0, 0, width, height);
      drawStartScreen();
      break;
    case "game":
      image(gameImage, 0, 0, width, height);
      playGame();
      break;
    case "gameOver":
      image(gameOverImage, 0, 0, width, height);
      drawGameOver();
      break;
    case "win":
      image(youWinImage, 0, 0, width, height);
      drawWin();
      break;
  }
}

// Function to draw start screen
function drawStartScreen() {
  if (gameState === "start" && restartButton == 1 && buttonispressed == 0) {
    startGame(); // Start the game when restart button is pressed
    buttonispressed = 1;
  } else if (restartButton == 0 && buttonispressed == 1) {
    buttonispressed = 0;
  }
}

// Function to play the game
function playGame() {
  //Read data from input

  if (gameState === "game") {
    // Check if the key pressed matches the arrow type
    for (let i = arrows.length - 1; i >= 0; i--) {
      let arrow = arrows[i];
      if (arrow.y >= hitZoneY - tolerance && arrow.y <= hitZoneY + tolerance) {
        if (
          (leftButton == 1 && arrow.type === "left") ||
          (upButton == 1 && arrow.type === "up") ||
          (downButton == 1 && arrow.type === "down") ||
          (rightButton == 1 && arrow.type === "right")
        ) {
          arrows.splice(i, 1); // Successful hit, remove arrow
          score++; // Increment score
        }
      }
    }
  }

  // Draw hit zones
  fill(255, 255, 255, 100);
  ellipse(width * 0.2, hitZoneY, 70);
  ellipse(width * 0.4, hitZoneY, 70);
  ellipse(width * 0.6, hitZoneY, 70);
  ellipse(width * 0.8, hitZoneY, 70);

  // Display score and lives
  fill(255);
  text(`Score: ${score}`, 70, 30);
  text(`Lives: ${lives}`, width - 70, 30);

  // Move and display arrows
  for (let i = arrows.length - 1; i >= 0; i--) {
    let arrow = arrows[i];
    image(images[arrow.type], arrow.x - 35, arrow.y - 35, 70, 70);
    arrow.y -= arrowSpeed;

    // Remove arrow if it goes out of screen and reduce lives
    if (arrow.y < 0) {
      arrows.splice(i, 1);
      lives--;
    }
  }

  // Check for game over or win conditions
  if (lives < 1) {
    gameState = "gameOver";
    song.stop(); // Stop the song if the player loses
  } else if (song.isPlaying() === false) {
    gameState = "win";
  }
}

// Function to draw game over screen
function drawGameOver() {
   if (restartButton == 1 && buttonispressed == 0){
    gameState = "start"
     buttonispressed = 1;
  } else if (restartButton == 0 && buttonispressed == 1){
    buttonispressed = 0;
  }
}

// Function to draw win screen
function drawWin() {
   if (restartButton == 1 && buttonispressed == 0){
    gameState = "start"
     buttonispressed = 1;
  } else if (restartButton == 0 && buttonispressed == 1){
    buttonispressed = 0;
  }
}

// Function to handle key presses
function keyPressed() {
  if (key == " ") {
    // Start the serial connection
    setUpSerial();
  }
}

// Function to start the game
function startGame() {
  gameState = "game";
  restartButton = 0;
  score = 0;
  lives = 5;
  arrows = [];
  clearInterval(arrowInterval); // Clear any existing interval
  initiateArrowSpawns();
  song.play(); // Start playing the music when the game starts
}

// Function to restart the game
function restartGame() {
  gameState = "start"; // Change gameState to 'start' to return to title screen
  score = 0;
  lives = 5;
  arrows = [];
  clearInterval(arrowInterval); // Clear any existing interval
  initiateArrowSpawns();
  restartButton = 0; // Reset restart button state
}


// Function to initiate arrow spawns
function initiateArrowSpawns() {
  let interval = 60000 / (songBPM / 2 ); // Halve the BPM to spawn arrows at a slower rate
  arrowInterval = setInterval(() => {
    let direction = random(["left", "up", "down", "right"]);
    spawnArrow(direction);
  }, interval);
}

// Function to spawn an arrow
function spawnArrow(direction) {
  let xPosition;
  switch (direction) {
    case "left":
      xPosition = width * 0.2;
      break;
    case "up":
      xPosition = width * 0.4;
      break;
    case "down":
      xPosition = width * 0.6;
      break;
    case "right":
      xPosition = width * 0.8;
      break;
  }
  arrows.push({ x: xPosition, y: height, type: direction });
}

// This function will be called by the web-serial library
// with each new *line* of data. The serial library reads
// the data until the newline and then gives it to us through
// this callback function
function readSerial(data) {
  //////////////////////////////////////////////////
  // READ FROM ARDUINO HERE
  //////////////////////////////////////////////////

  if (data != null) {
    // Make sure there is actually a message
    // Split the message
    let fromArduino = split(trim(data), ",");
    // If the right length, then proceed
    if (fromArduino.length == 5) {
      // Only store values here
      // Do everything with those values in the main draw loop

      // We take the string we get from Arduino and explicitly
      // convert it to a number by using int()
      // e.g. "103" becomes 103
      leftButton = int(fromArduino[0]);
      upButton = int(fromArduino[1]);
      downButton = int(fromArduino[2]);
      rightButton = int(fromArduino[3]);
      restartButton = int(fromArduino[4]);
      //console.log("left button is" + leftButton);
      //console.log("right button is" + rightButton);
      //console.log("up button is" + upButton);
      //console.log("down button is" + downButton);
      //console.log("restart is" + restartButton);
    }

    /////////////////////////////////////////////////
    // SEND TO ARDUINO HERE (handshake)
    /////////////////////////////////////////////////
    let sendToArduino = "\n";
    writeSerial(sendToArduino);
  }
}

 

Arduino Code:

This section of code is designed to interface with hardware buttons, specifically set up for a gaming application. It starts by defining pin numbers connected to various buttons on an Arduino board: left, up, down, right, and a restart button. Each button is associated with a specific pin number ranging from 2 to 6.

The setup() function initializes the serial communication at a baud rate of 9600 to enable data transfer between the Arduino and a computer. It also configures each button pin as an input, preparing the Arduino to read the states of these buttons.

In the `oop() function, the code continuously reads the state of each button using the digitalRead() function. It checks whether each button is pressed or not, producing a digital high or low signal. These states are then formatted into a comma-separated string and sent over the serial connection. This allows another system, such as a computer running a game, to receive real-time input from these hardware buttons, integrating physical interactions into digital applications.

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


void setup() {
  Serial.begin(9600);
  // Set button pins as inputs
  pinMode(leftButtonPin, INPUT);
  pinMode(upButtonPin, INPUT);
  pinMode(downButtonPin, INPUT);
  pinMode(rightButtonPin, INPUT);
  pinMode(restartButtonPin, 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);
  int restartButton = digitalRead(restartButtonPin);

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

}

 

The Game:

Here’s a video of my friend playing it:

FullScreen Link

Overall:

I am very proud of myself for pushing myself into an area that I previously left unexplored. Considering how I could not even make the normal game for my midterm, and I was able to do it for my final and even taking it a step further for my final. I am truly happy I was given the freedom to do this. I would like to particularly mention that without the help and support I got from professor Aya, I would have given up a long time ago.

I feel like one thing I wanted to do with this project that I could not was upload my own custom map for the songs and have different difficulties and  songs (just like a normal DDR arcade machine). I think that would be important for me as it allows to input even more of my creativity into this project.

Week 12 Assignment – Rashed and Jana

Exercise 1: 

P5 code:

let rVal = 0;
let alpha = 255;
let left = 0; // True (1) if mouse is being clicked on left side of screen
let right = 0; // True (1) if mouse is being clicked on right side of screen

function setup() {
createCanvas(640, 480);
textSize(18);
}

function draw() {
// one value from Arduino controls the background's red color
background(255)

// the other value controls the text's transparency value
fill(255, 0,0)

if (!serialActive) {
text("Press Space Bar to select Serial Port", 20, 30);
} else {
text("Connected", 20, 30);
// Print the current values
text('rVal = ' + str(rVal), 20, 50);
text('alpha = ' + str(alpha), 20, 70);
}

// click on one side of the screen, one LED will light up
// click on the other side, the other LED will light up
if (mouseIsPressed) {
if (mouseX > rVal-50 && mouseX < rVal+50 && mouseY > height/2-50 && mouseY < height/2+50) {
right = 1;
}
} else {
right = 0;
}
ellipse(rVal, height/2, 50,50)
}

function keyPressed() {
if (key == " ") {
// important to have in order to start the serial connection!!
setUpSerial();
}
}

// This function will be called by the web-serial library
// with each new line of data. The serial library reads
// the data until the newline and then gives it to us through
// this callback function
function readSerial(data) {
////////////////////////////////////
//READ FROM ARDUINO HERE
////////////////////////////////////

if (data != null) {
// make sure there is actually a message
// split the message
let fromArduino = split(trim(data), ",");
// if the right length, then proceed
if (fromArduino.length == 2) {
// only store values here
// do everything with those values in the main draw loop

// We take the string we get from Arduino and explicitly
// convert it to a number by using int()
// e.g. "103" becomes 103
rVal = int(fromArduino[0]);
alpha = int(fromArduino[1]);
}

//////////////////////////////////
//SEND TO ARDUINO HERE (handshake)
//////////////////////////////////
let sendToArduino = left + "," + right + "\n";
writeSerial(sendToArduino);
}
}

 

We did not face any big issue in this part. We just added the ellipse and changed its position to rVal, height/2.

Video:

Exercise 2: 

P5 code:

let rVal = 0;
let alpha = 255;
let left = 0; // True (1) if mouse is being clicked on left side of screen
let right = 0; // True (1) if mouse is being clicked on right side of screen

function setup() {
createCanvas(640, 480);
textSize(18);
}

function draw() {
// one value from Arduino controls the background's red color
background(map(rVal, 0, 1023, 0, 255), 255, 200);

// the other value controls the text's transparency value
fill(255, 0, 255, map(alpha, 0, 1023, 0, 255));

if (!serialActive) {
text("Press Space Bar to select Serial Port", 20, 30);
} else {
text("Connected", 20, 30);
// Print the current values
text('rVal = ' + str(rVal), 20, 50);
text('alpha = ' + str(alpha), 20, 70);
}



function keyPressed() //built in function
{
if (key == " ") //if space is pressed then
{
setUpSerial(); //setup the serial.
}
else if (keyCode == DOWN_ARROW)
{
if (right != 0)
{
right = right - 20;
}
}
else if (keyCode == UP_ARROW)
{
if (right != 250)
{
right = right + 20;
}
}
}
}

// This function will be called by the web-serial library
// with each new *line* of data. The serial library reads
// the data until the newline and then gives it to us through
// this callback function
function readSerial(data) {
////////////////////////////////////
//READ FROM ARDUINO HERE
////////////////////////////////////

if (data != null) {
// make sure there is actually a message
// split the message
let fromArduino = split(trim(data), ",");
// if the right length, then proceed
if (fromArduino.length == 2) {
// only store values here
// do everything with those values in the main draw loop

// We take the string we get from Arduino and explicitly
// convert it to a number by using int()
// e.g. "103" becomes 103
rVal = int(fromArduino[0]);
alpha = int(fromArduino[1]);
}

//////////////////////////////////
//SEND TO ARDUINO HERE (handshake)
//////////////////////////////////
let sendToArduino = left + "," + right + "\n";
writeSerial(sendToArduino);
}
}

 

Arduino code for Exercise 1 and 2:

int leftLedPin = 2;
int rightLedPin = 5;

void setup() {
// Start serial communication so we can send data
// over the USB connection to our p5js sketch
Serial.begin(9600);

// We'll use the builtin LED as a status output.
// We can't use the serial monitor since the serial connection is
// used to communicate to p5js and only one application on the computer
// can use a serial port at once.
pinMode(LED_BUILTIN, OUTPUT);

// Outputs on these pins
pinMode(leftLedPin, OUTPUT);
pinMode(rightLedPin, OUTPUT);

// Blink them so we can check the wiring
digitalWrite(leftLedPin, HIGH);
digitalWrite(rightLedPin, HIGH);
delay(200);
digitalWrite(leftLedPin, LOW);
digitalWrite(rightLedPin, LOW);



// start the handshake
while (Serial.available() <= 0) {
digitalWrite(LED_BUILTIN, HIGH); // on/blink while waiting for serial data
Serial.println("0,0"); // send a starting message
delay(300); // wait 1/3 second
digitalWrite(LED_BUILTIN, LOW);
delay(50);
}
}

void loop() {
// wait for data from p5 before doing something
while (Serial.available()) {
digitalWrite(LED_BUILTIN, HIGH); // led on while receiving data

int left = Serial.parseInt();
int right = Serial.parseInt();
if (Serial.read() == '\n') {
digitalWrite(leftLedPin, left);
digitalWrite(rightLedPin, right);
int sensor = analogRead(A0);
delay(5);
int sensor2 = analogRead(A1);
delay(5);
Serial.print(sensor);
Serial.print(',');
Serial.println(sensor2);
}
}
digitalWrite(LED_BUILTIN, LOW);
}

 

Challenges:

We tried to get the light to decrease its brightness every time the person would click on the left side of the screen and for the brightness to increase whenever the person clicks on the right side of the screen. The code was correct, but for some reason it was not working. So, it might be something with the bulb or the Arduino itself. However, the light does take around 4 clicks on both sides of the screen to turn off or on so, we would consider that a success.

Video:

Exercise 3: 

P5 code:

let dragForce = 0.99;
let mass = 20;
let ledState = 0;
let velocity;
let gravity;
let position;
let acceleration;
let wind;
let force;
let bounced = false;


function setup() {
createCanvas(640, 480);
textSize(18);
position = createVector(width / 2, 0);
velocity = createVector(0, 0);
acceleration = createVector(0, 0);
gravity = createVector(0, 0.3 * mass);
wind = createVector(0, 0);
}

function draw() {

background(255);

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

noStroke();
force = p5.Vector.div(wind, mass);
acceleration.add(force);
force = 0;
force = p5.Vector.div(gravity, mass);
acceleration.add(force);
force = 0;
velocity.add(acceleration);
velocity.mult(dragForce);
position.add(velocity);
acceleration.mult(0);

ellipse(position.x, position.y, mass, mass);
if (position.y > (height - mass / 2)-30 ) {
velocity.y *= -0.9;
position.y = (height - mass / 2)-30;
ledState= 1;

if (!bounced) {
fill('blue'); // Red when just bounced
bounced = true; // Update bounce state
} else {
fill('red'); // White otherwise
bounced = false; // Reset bounce state
}
} else {
ledState = 0;
}


}

rect(0,height-30,width,30);

}

function keyPressed() {
if (key == " ") {
// important to have in order to start the serial connection!!
setUpSerial();
}
}
function readSerial(data) {
if (data != null) {
// make sure there is actually a message
// split the message
let fromArduino = split(trim(data), ",");
// if the right length, then proceed
if (fromArduino.length == 1) {
let windCurrent = int(fromArduino[0]);
wind.x = map(windCurrent, 0, 1023, -1, 1);
}

//////////////////////////////////
//SEND TO ARDUINO HERE (handshake)
//////////////////////////////////
let sendToArduino = ledState + "\n";
writeSerial(sendToArduino);
}
}

 

Arduino code:

int ledPin = 10;
int potPin = A0;

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

pinMode(LED_BUILTIN, OUTPUT);

// Outputs on these pins
pinMode(ledPin, OUTPUT);

// start the handshake
while (Serial.available() <= 0) {
digitalWrite(LED_BUILTIN, HIGH); // on/blink while waiting for serial data
Serial.println("0"); // send a starting message
delay(300); // wait 1/3 second
digitalWrite(LED_BUILTIN, LOW);
delay(50);
}
}

void loop() {
// wait for data from p5 before doing something
while (Serial.available()) {
digitalWrite(LED_BUILTIN, HIGH); // led on while receiving data

int right = Serial.parseInt();
if (Serial.read() == '\n') {
digitalWrite(ledPin, right);
int potValue = analogRead(potPin);
delay(5);
Serial.println(potValue);
}
}
}

 

Challenges:

Everything went smoothly when writing the code. However, when we would click the play button to see the sketch, it would give us the error: “setUpArduino is not defined”. So, we spent countless minutes searching for something that was not there. After looking at the professors week 12 example on serial connection, we realized that we have forgotten the tab that would connect P5 to Arduino. So we just added that and it worked.

Video:

very very cute :3

Week #11 Assignment – Orquid by Jana & Rashed

Concept:

For this assignment, we wanted to go with something simple. Rashed is known for his love for music so I was excited to see what we would come up with. During our brainstorming session, Rashed was doing his hobby – which is music production- on his phone on GarageBand and he noticed how he had to click on the “arrow” button to change the pitch of the piano and we decided we wanted to do something like that but with the potentiometer.

The pitch-changing arrow I was referring to on GarageBand:

Materials used:

  • x11 Wires
  • x4 Buttons
  • x4 10k Resistors
  • x1 Buzzer
  • x1 Potentiometer

Production:

Writing this code was pretty challenging for us as both of us are still adapting to this new language.

We used the toneMelody example from the examples folder that we used in class to take the “pitches.h” tab from it in order to give a specific range. We collectively decided to have the buttons, by default, be the most basic notes “Do, Re, Me, Fa” also known as “NOTE_C4, NOTE_D4, NOTE_E4, NOTE_F4” which was pretty simple to implement.

However, we faced one major issue:

For some reason, the potentiometer would not cooperate at all. The buttons were working fine, but the pitch won’t budge. We, unfortunately, had to use ChatGPT to help us but it was still very helpful in helping us understand what we were doing wrong.

Here’s the code:

#include "pitches.h"

const int speakerPin = 9;  // Speaker connected to pin 9
int buttonPins[] = {2, 3, 4, 5};  // Button pins for notes
int potPin = A0;  // Potentiometer connected to A0

// Initialize an array of notes that will be used depending on the button press
int noteIndices[] = {NOTE_C4, NOTE_D4, NOTE_E4, NOTE_F4};  // Starting notes for each button

void setup() {
  for (int i = 0; i < 4; i++) {
    pinMode(buttonPins[i], INPUT_PULLUP);  // Setup button pins as input with pull-up resistor
  }
  pinMode(speakerPin, OUTPUT);  // Set speaker pin as output
}

void loop() {
  int potValue = analogRead(potPin);  // Read the current value from the potentiometer
  int noteRange = NOTE_C6 - NOTE_C4;  // Define the range of notes to span
  int noteOffset = map(potValue, 0, 1023, 0, noteRange);  // Map potentiometer value to note range
  
  for (int i = 0; i < 4; i++) {
    if (digitalRead(buttonPins[i]) == LOW) {  
      int noteToPlay = noteIndices[i] + noteOffset;  
      tone(speakerPin, noteToPlay);  // Play the note on the speaker
      delay(200);  // A short delay to help debounce the button
      while (digitalRead(buttonPins[i]) == LOW);  // Wait for button release
      noTone(speakerPin);  // Stop the note
    }
  }
}

 

 

Here’s the video of us using it:

 

Reflection:

I think, despite chatGPT’s involvement in this, we did pretty well; the concept was there, the energy and effort that was put into this is there and we’re both proud of our little creation. However, we both do with we took it a step further and maybe added more buttons or tried to use the sensor instead of the potentiometer. We really hope to get used to and adapt to this language and I think after doing

Week 10 Assignment – “Who’s First?”

For this assignment, I had a hard time coming up with a creative idea. My twin nephews were over at the time, and I could hear them arguing about something. When I checked up on them, they were fighting over whoever was faster than the other, so I decided to make something to settle that (Love you, Ahmed and Jassim).

The concept is pretty simple:

It’s a two-player game. One player gets the photoreceptor, and the other gets the button. Whoever can cause their light to flash first proves that they’re faster than the other.

Materials Used:

  • 2x 330 resistors
  • 2x 10k resistors
  • 8x wires
  • 2x LEDs (one Red, one Yellow)
  • 1x button
  • 1x photoresistor

Setup:

I used examples from previous classes to piggyback on. The slides were very helpful to me.

The website I used will NOT be used again. Although I faced many problems with the resistors, it got the job done.


Code:

 

int photoSensorPin = A1;
int buttonPin = 2;
int ledPin1 = 3;
int ledPin2 = 4;
void setup() {
  pinMode(photoSensorPin, INPUT);
  pinMode(buttonPin, INPUT);
  pinMode(ledPin1, OUTPUT);
  pinMode(ledPin2, OUTPUT);
}
void loop() {
  int sensorValue = analogRead(photoSensorPin);
  int buttonState = digitalRead(buttonPin);
  if (buttonState == HIGH) {
    digitalWrite(ledPin1, LOW);
    digitalWrite(ledPin2, HIGH);
  } else if (sensorValue < 450) {
    digitalWrite(ledPin1, HIGH);
    digitalWrite(ledPin2, LOW);
  } else {
    digitalWrite(ledPin1, LOW);
    digitalWrite(ledPin2, LOW);
  }
}

 

Demo:

IMG_0577

Reflection:

Making this game was a pretty fun challenge for me. This language is very new to me, and I’m still having issues getting used to it. I would like to take this a step further and possibly use other forms of input devices to accurately guess who’s faster (Ahmed won BTW).

 

 

 

Week 10 – Reading Response

“Physical Computing’s Greatest Hits (and Misses)”

The review of recurrent project themes in educational settings captures the fine line between redundancy and innovation. Igoe’s encouragement for students to breathe new life into old ideas resonates with me, as it emphasizes the importance of creativity and personal expression in learning which is something that I feel strongly about. Personally, I find the discussion about the longevity and adaptability of simple projects like the Theremin and video mirrors particularly engaging. These projects, while simple, serve as a testament to the foundational skills being taught and the creativity they can unlock. The idea that a simple gesture, like moving your hand over a sensor, can create music or alter images is both magical and empowering. The section on video mirrors struck a chord with me; it’s described as the “screen-savers of physical interaction,” which is both amusing and apt. It underlines how some projects may lean more towards aesthetic value than interaction depth, but still hold significant educational value in terms of the skills they teach and their ability to engage viewers.

Moreover, Igoe’s mention of projects that have become almost iconic within the community, like the Drum Gloves and Mechanical Pixels, highlights an interesting aspect of physical computing: its blend of technology and tangible, interactive art. This intersection is where I see a lot of personal and academic growth potential, bridging the gap between technical skills and artistic expression.

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

This article also resonates with me because it champions the idea that art is not just an artist’s expression but a dynamic interaction. The artist’s role shifts from being a narrator to a facilitator who sets up the environment and then steps back to let the audience interact and derive their own meanings. The author persuasively argues against the traditional view of art as a static expression, pointing out that interactive art should be more like a conversation or a performance that evolves. The analogy of the artist as a director who does not strictly dictate but rather guides actors (or participants, in this case) allows for a genuine, emergent experience. This perspective is enlightening as it underlines the shift from passive consumption to active engagement. I appreciate Igoe’s emphasis on the importance of simplicity and clarity in setting up interactive environments. The directive to “arrange the space” thoughtfully and to “remove anything extraneous” serves as a crucial guideline for creating an immersive and intuitive experience. This approach ensures that each participant’s interaction is meaningful and personal, rather than overshadowed by the artist’s own interpretations or expectations.

Moreover, the concept of listening to how people interact with the artwork—and the notion that these interactions might change over time—adds a layer of complexity to the creation and exhibition of interactive art. It suggests that the artwork is not complete upon its initial display but continues to evolve and resonate differently as people engage with it.

 

Reading Response 8A

Norman,“Emotion & Design: Attractive things work better”

My personal view of design when it comes to applications/ websites goes deep into the particular balance of design and practicality and I think the author does an amazing job with explaining that concept. I believe the whole conceot of desin is completely objective where it depends on a person’s preference. One recent debate I have seen spread throughout social media is whether makeup brands fit their brand’s aesthetic or not on their website. The main brand that is talked about is Ariana Grande’s R.E.M. Beauty where many people argue that the website lacks aesthetic, whereas it is compared to other beauty brands like Apothecary 87 and KKW Beauty. This just shows the importance of design and how its existence is crucial.

Midterm Project – NewJeans’ Escape

Concept

Something I always love talking about is my obsession with music. I love collecting cassette tapes, vinyl records, and CDs. I ensure everyone knows about this because I find it so cool. Among the thousands of artists I listen to, only a couple stick out, and among these artists is NewJeans, a group that is made up of 5 members. in 2023’s July, NewJeans made a comeback after almost six months of releasing no music. With that came my all-time favorite music video of theirs, where they collaborated with Cartoon Network’s PowerPuffGirls, called NewJeans by NewJeans. This song is a testament to the creativity and unique musical style that NewJeans is known for. Its distinctive arrangement and innovative lyrics, along with the exciting collaboration, made “New Jeans” another successful hit for the group.

In this music video, there’s a particular scene where the girls are turned into a video game. When I heard that I would be able to make a game-like sketch for my midterm project, my mind immediately went to that scene. Of course, at first, I wanted to make more of something that resembles one of my favorite arcade games, Dance Dance Revolution, but after I spent countless hours trying my hardest to make it work, I just stumbled into too many obstacles that made me realize that I would not have the time to figure it out. So, I went back to my previous idea: To use the concept from the music video. However, I wanted to get creative with the structure of it. I decided to go with a “Choose your Character” title screen, which seemed to be the most crucial part for me. Then for the actual playing part, I wanted their song to be a part of it, so I made it so that the player could win solely by surviving the flying obstacles for the entirety of the song.

Check out the music video!!

 

This is the part of the music video I was inspired by:


Design

Since the character designs were already avaliable, I did not have a hard time picking the character design. However, I could not find a high quality PNG of the charactes so I had to retrce it but it was not challenging at all.

Challenges

Putting NewJeans’ Escape together forced me to face many challenges:
  1. The “Choose Your Character” Disaster: This was primarily the most significant part of the whole game for me. I was really passionate about this, and I would not continue if it had not worked. After countless hours of searching online and watching useless YouTube videos, I turned to ChatGPT, who taught me to use an Index, which I am very, very proud of myself for getting the hang of. I used the mouseClicked(); function to check the position of the mouse and which character it corresponds to and I stored that character into a variable i named “characterIndex” which displays in the ‘play’ state and once the player chooses the character, the game state will change to ‘play’.

I’m very proud of myself for getting it figured out.

// Function to handle mouse clicks
function mouseClicked() {
  if (gameState === "start") {
    // Check if mouse click is on a character
    for (let i = 0; i < 5; i++) {
      if (mouseX > (width / 5) * i && mouseX < (width / 5) * (i + 1)) {
        characterIndex = i; // Set selected character
        gameState = "play"; // Change game state to play
        break;
      }
    }
  }

 

2. The Time Loss Incident: “I just wanted a timer to be displayed at the top right of the screen. Is that too much to ask?” said Rashed as he was on the brink of quitting. Having a timer that would countdown with the song, for some reason, was not cooperating with what I wanted to do. I wanted to directly connect it to the music to have it automatically calculate when the music would start and countdown till its end, but I could not figure it out. I decided to code the timer separately instead of figuring that out and that’s what I did. It turns out this was the most straightforward way, and I wish I had thought of this before where I had two variables, “timeLeft” and “long duration,” which both equal 108 (The duration of the song in seconds), and had them decrease by the current time of the song.

Very proud of myself for not giving up.

// Display remaining time
timeLeft = songDuration - song.currentTime();
let minutes = Math.floor(timeLeft / 60);
let seconds = Math.floor(timeLeft % 60);
let timeString = nf(minutes, 2) + ":" + nf(seconds, 2);
textAlign(RIGHT);
text("Time: " + timeString, width - 10, 30);

 

3. The Collision Colliding Collision: The only thing I wanted was for when the objects hit the player, lives would decrease, but in so many tries, for some reason, it would not work well. No matter hoe many times I try, nothing would work. until I found the miracle of the dist(); function. The function calculates the distance between two points. The coordinates(obj.x + 75, obj.y + 50) represent the center of the current object (adjusted for its size, this was torture). The coordinates(80, characterY + 30) represent the center of the character (adjusted for its size, torture part. 2). This means that if the distance is less than 30 pixels (which represents a collision radius), it means the object and the character have collided.

 

objects.push(createObject());
 }
 for (let obj of objects) {
 obj.x -= objectSpeed; // Move object from right to left
 // Draw the object (evil bunny)
 image(evilBunny, obj.x, obj.y, 150, 100);

 // Check collision with character
 if (dist(obj.x + 75, obj.y + 50, 80, characterY + 30) < 30) { 
   lives--; // If collision happens, decrease lives
   objects.splice(objects.indexOf(obj), 1); // Remove object

 

The Game

FullScreen Link

 

Overall

I am very, very proud of myself and very happy with how this turned out. I hope to continue working on my skills, and one day, I could code a rhythm game.

Week 5 : Midterm Progress

When I heard how the midterm would be to create a game, my mind immediately went to my favorite type of game: Rhythm Games. I wanted to try and recreate one of my favorite arcade games called Dance Dance Revolution where the player would have to stand on the arrows on the ground that would correspond to the rhythm of the background music.

This is the DDR Arcade Machine:

I successfully created three lives that would get lost when the player would miss a ball. I also managed to create a “Game Over” state which I have to modify. However, I need to work on the interactive part of it. The mechanics that I have currently are Mouse click based, meaning that a player would have to click on the target area in order to gain points. When I tried to play it using a mouse, it felt very awkward so I will probably change it to the arrow keys which would be way more convenient.

Update: Okay, turns out that It does not even work properly with the mouse no matter how hard I try. I will be switching the strategy to the arrow keys.  I found the exact arrows that I would want to use which are the following. Now I have to figure out how I could code the arrow keys and assign them to each of the targets.

This is what I would like it to look like:

Reading Reflection #4

Don Norman presents a key idea in “The Design of Everyday Things,” which is feedback in design. According to Norman, feedback is a means of informing the user of the outcomes of an action. He uses commonplace examples to demonstrate this, such as sitting at a traffic light without seeing any indication that the system has spotted your automobile, or pushing buttons on an elevator without knowing if the elevator is on its way. This idea emphasizes how crucial it is to provide users with prompt, understandable feedback so they may be assured that their action has been recorded and is being handled. Ineffective feedback can cause consumers to feel doubtful, angry, or confused, which can make them dislike the system or product.

When I think about it, this idea has really changed the way I think about user interface and design. I can think of several instances where I kept pressing buttons on gadgets or user interfaces and got no reaction. I’ve always found this experience disconcerting because I’m not sure if my activities were acknowledged. I completely agree with Norman’s assessment on the importance of receiving feedback that is both clear and fast. It draws attention to a prevalent problem found in many designs and emphasizes how crucial it is to include transparent feedback methods in order to increase usability and user happiness. This understanding highlights the vital role feedback plays in enabling successful human-machine interactions, and it motivates me to examine designs more closely, both those I work with on a daily basis and those I might develop.

Assignment #4 – New Jeans by NewJeans

For this assignment, I wanted to do something involving my favorite musical band called NewJeans. I decided to use my favorite quote of theirs “New hair, New tee, NewJeans. You and me.” Which they repeat twice. The repetition of the phrase “New hair New tee / NewJeans” is followed by “You & me”, suggesting that the speaker is extending an invitation to the listener to join them in this journey of transformation and self-discovery. I decided to put my everlasting love for musical games into use; I wanted to create something simple. I wanted simply a phrase where I could play the song and click on this at the same time.

For my code, I faced issues with getting the phrase to repeat after it’s done, but chatGPT helped me figure that out by using % in the mouseClicked(); function.

function mouseClicked() {
    currentIndex = (currentIndex + 1) % phrases.length;
}

 

Reflection:

I am honestly very proud of this piece but I would like to someday develop a rhythm game that I could play using my favorite songs.