Midterm Project Plan

Scope of Work
For my midterm project, I am designing a digital version of the classic Whack-a-Mole game, inspired by the attached references. The goal is to create an engaging and interactive game where players use their mouse to “whack” moles as they pop out of holes. The game should challenge players’ reflexes and introduce risk elements like bombs that add complexity.

The game will start with a Start Screen featuring the game title and a simple “Start” button. I also plan to add access settings, like toggling sound on or off. Once the game begins, moles will randomly pop up from a grid of holes, and the player must click or tap on them to score points. Not every hole will be safe. Occasionally, a bomb will pop up, and hitting it will result in losing points or lives. Players will have a limited time (for example, 60 seconds) to score as many points as possible before the timer runs out. As the game progresses, the difficulty will increase, with moles appearing and disappearing faster, making the game more challenging.

Each successful hit of a mole adds 10 points, while mistakenly hitting a bomb will deduct 20 points or reduce a life. The game will display a score counter and a countdown timer to keep players aware of their progress and remaining time. At the end of the game, an End Screen will appear, showing the final score and offering options to “Play Again” or “Quit.”

I want the grid to contain between 9 and 16 holes, depending on the level of complexity I decide to implement. Moles and bombs will randomly pop up in these holes at varying intervals. The randomness is crucial to establish unpredictability. To add to the challenge, the moles will pop up faster as time progresses, requiring quicker reflexes from the player.

Code Structure

For the game’s development, I plan to use an object-oriented approach. The game will be structured around a few core classes:

  • Game Class: Manages the overall game loop, score tracking, and time countdown
  • Mole Class: Controls the mole’s behavior (when it pops up, how long it stays, and how it reacts to player interaction)
  • Bomb Class: Functions similarly to the mole but triggers penalties when a bomb is clicked
  • Hole Class: Represents each position on the grid, randomly spawning moles or bombs
  • UI Class: Manages elements like the start screen, score display, timer, and end screen

The core gameplay loop will rely on these functions:

  • startGame(): Initializes the game and resets scores and timers
  • spawnMole(): Randomly selects holes for moles to appear
  • spawnBomb(): Introduces bombs with a set probability
  • whackMole(): Detects player clicks and updates the score
  • hitBomb(): Triggers penalties for clicking bombs
  • updateTimer(): Counts down and ends the game when time runs out
  • increaseDifficulty(): Speeds up mole appearances as the game progresses

Challenges and Risks

I expect that one of the most complex parts of this project would be ensuring accurate collision detection. In other words, making sure the program properly registers when a player clicks on a mole or bomb.

Timing is also a big concern. Moles need to appear and disappear at unpredictable but balanced intervals to make the pace of the game flawless and not frustrating.

To tackle these challenges, I plan to build a test script focused on collision detection, using simple shapes before applying it to the actual mole and bomb sprites. This will help me adjust the hitboxes and make sure user interactions feel responsive. I might also test randomization algorithms to ensure that mole and bomb appearances are unpredictable yet adequate.

Week5 Reading: “Computer Vision for Artists and Designers”

Reading this article felt like taking a peek behind the scenes of how computers “see” the world, which is a process that’s really different from how we see it. Unlike our eyes that automatically pick up on contexts, depths, and meanings, computers have to break everything down into pixels and simple data to be able to process it. They use techniques like frame differencing, background subtraction, and brightness thresholding to detect movement and distinguish objects from their surroundings. In other words, while we instantly recognize a face or a smile, a computer needs a lot of help to do even simple things like tell the difference between a moving person and a stationary background.

What really stood out to me was how much effort goes into making the computer’s vision work well. It’s much more than just writing code. It’s also about setting up the right physical conditions like using specific lighting, reflective materials, or even special cameras to boost the system’s accuracy. This mix of coding and physical tweaking shows that computer vision is as much an art as it is a science.

I also found it interesting how computer vision’s tracking and surveillance capabilities have shaped interactive media art. On one hand, these systems let artists create installations where your movements become part of the artwork (like in the classic Videoplace) but on the other hand, there’s a darker side: the same technology that can create immersive art experiences can also be used to monitor and profile people. This duality makes me think about the ethical implications and the balance between creating engaging art and respecting personal privacy.

To sum up, the article not only breaks down the technical side of computer vision for beginners but also opens up deeper questions about technology’s role in our lives, both as a creative tool and a way of surveillance.

Week 4 – reading response

Reading Response: The Psychopathology of Everyday Things

Something That Drives Me Crazy: Confusing Shower Knobs

One of the most frustrating design flaws in everyday life is the lack of a standardized, intuitive shower knob system across different countries. If you’ve ever traveled internationally, you’ve likely encountered showers that seem to require a degree in engineering to operate.

In the United States, many showers have a single-knob system where you turn left for hot water and right for cold. However, some models require you to pull or push the knob, which isn’t always obvious. In the United Kingdom, separate hot and cold taps are still common, making us mix water manually. In Norway, for example, some showers have buttons that must be pressed in a particular sequence before water flows. The inconsistency means that travellers like me often accidentally scald themselves, turn on the water at full blast, or get an unexpected cold shock.

The biggest issue is discoverability. There’s often no clear indication of how the system works. Some showers even have extra knobs that control water pressure or temperature separately, adding to more confusion. Without obvious instructions, we are left to experiment, sometimes getting drenched unexpectedly in freezing or boiling water.

Applying Norman’s Principles to Improve Shower Design

Don Norman’s principles of discoverability, affordances, and signifiers could help improve shower designs:

  1. Clear affordances. The shape and placement of knobs should signify their function. A lever-style handle naturally implies to us that it has to be turned, while a button clearly calls for you to press it.
  2. Icons or labels could tell us about temperature directions, with simple red/blue colors universally suggesting hot and cold temp.
  3. Natural mapping. This could be a horizontal sliding control that moves left for hot and right for cold. It would be more intuitive than rotating knobs in random directions.

Another potential hi-tech solution is a digital shower interface with a display which could get rid of confusion entirely.

Week 4 – generative text output

Inspiration for This Project

I wanted to create an interactive and visually engaging experience that merges astrology with generative art. The idea was to provide users with a simple yet immersive way to receive a zodiac-based “psychic reading,” followed by animations and visuals. Astrology is often associated with mysticism and magic, so I aimed to reflect that via changing background colours and adding floating particles. For the visual part, I took my inspiration from this website: https://www.horoscope.com/us/index.aspx

Code Highlight I Am Proud Of

One part of the code I’m particularly proud of is the getZodiacColor function, which assigns a unique background color to each zodiac sign:

function getZodiacColor(sign) {
  let colors = {
    "Aries": color(255, 99, 71), "Taurus": color(107, 142, 35), "Gemini": color(255, 215, 0), "Cancer": color(70, 130, 180),
    "Leo": color(255, 165, 0), "Virgo": color(46, 139, 87), "Libra": color(123, 104, 238), "Scorpio": color(148, 0, 211),
    "Sagittarius": color(255, 140, 0), "Capricorn": color(139, 69, 19), "Aquarius": color(0, 191, 255), "Pisces": color(72, 61, 139)
  };
  return colors[sign] || color(240);
}

This function is simple, but it instantly transforms the visual feel of the project based on the user’s selection, creating some sense of personalization.

Reflection

For future projects, I’d love to explore more complex generative animations, such as constellations that change based on the zodiac sign. Things like integrating sound effects or subtle ambient music could enhance the mystical atmosphere. Another direction could be adding more interactive elements, like having particles respond to mouse movement, making the experience feel even more magical and immersive.

Here is the full code:

let signs = [
  "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo",
  "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces"
];

// Zodiac readings for each sign
let readings = {
  "Aries": ["Today is a day for bold moves.", "A new adventure awaits you.", "Your energy will attract opportunities."],
  "Taurus": ["Stay grounded, but take a leap of faith.", "Patience will bring unexpected rewards.", "A financial opportunity is coming your way."],
  "Gemini": ["A conversation will spark inspiration.", "Your curiosity leads to a surprising discovery.", "Adaptability is your greatest strength today."],
  "Cancer": ["Your emotions will guide you well.", "A nostalgic moment will bring clarity.", "Trust your intuition—it knows the way."],
  "Leo": ["Your confidence will open doors.", "A bold move will lead to admiration.", "Shine your light and others will follow."],
  "Virgo": ["Your keen eye will catch an important detail.", "Organization will bring unexpected rewards.", "A small habit change will lead to a breakthrough."],
  "Libra": ["Balance is key today.", "A relationship will deepen in an unexpected way.", "Harmony will find you when you least expect it."],
  "Scorpio": ["Mystery surrounds you—embrace it.", "Transformation is closer than you think.", "Your passion will lead you to new heights."],
  "Sagittarius": ["An exciting journey is on the horizon.", "Your optimism will inspire someone today.", "The universe is conspiring in your favor."],
  "Capricorn": ["Hard work pays off—stay focused.", "A disciplined approach will yield results.", "Your perseverance will be rewarded soon."],
  "Aquarius": ["Innovation is your ally today.", "A sudden insight will change your path.", "Your unique perspective is your greatest strength."],
  "Pisces": ["Your dreams hold important messages.", "Creativity will flow effortlessly.", "A moment of solitude will bring deep understanding."]
};

let dropdown, button, output;
let bgColor;
let particles = [];

function setup() {
  createCanvas(400, 300);
  textSize(16);
  textAlign(CENTER, CENTER);
  
  // Create dropdown menu for zodiac signs
  dropdown = createSelect();
  dropdown.position(100, 100);
  
  for (let sign of signs) {
    dropdown.option(sign);
  }
  
  // Create button to generate reading
  button = createButton("Get Your Reading");
  button.position(100, 140);
  button.mousePressed(generateReading);
  
  output = "Select your sign and receive your reading";
  bgColor = color(240);
  
  // Create floating particles for magical effect
  for (let i = 0; i < 50; i++) {
    particles.push(new Particle());
  }
}

function draw() {
  background(bgColor);
  fill(50);
  text("Psychic Zodiac Reading", width / 2, 50);
  text(output, width / 2, 80);
  
  // Update and show floating particles
  for (let p of particles) {
    p.update();
    p.show();
  }
}

// Generate random reading based on selected zodiac sign
function generateReading() {
  let selectedSign = dropdown.value();
  let possibleReadings = readings[selectedSign];
  output = possibleReadings[int(random(possibleReadings.length))];
  bgColor = getZodiacColor(selectedSign);
}

// Assign unique background color for each zodiac sign
function getZodiacColor(sign) {
  let colors = {
    "Aries": color(255, 99, 71), "Taurus": color(107, 142, 35), "Gemini": color(255, 215, 0), "Cancer": color(70, 130, 180),
    "Leo": color(255, 165, 0), "Virgo": color(46, 139, 87), "Libra": color(123, 104, 238), "Scorpio": color(148, 0, 211),
    "Sagittarius": color(255, 140, 0), "Capricorn": color(139, 69, 19), "Aquarius": color(0, 191, 255), "Pisces": color(72, 61, 139)
  };
  return colors[sign] || color(240);
}

// Particle class for floating magic effect
class Particle {
  constructor() {
    this.x = random(width);
    this.y = random(height);
    this.vx = random(-1, 1);
    this.vy = random(-1, 1);
    this.alpha = random(100, 255);
  }

  // Update particle movement
  update() {
    this.x += this.vx;
    this.y += this.vy;
    if (this.x > width || this.x < 0) this.vx *= -1;
    if (this.y > height || this.y < 0) this.vy *= -1;
  }

  // Display particle as a glowing dot
  show() {
    noStroke();
    fill(255, this.alpha);
    ellipse(this.x, this.y, 5, 5);
  }
}

 

Object-oriented programming – Week 3

Reflection: Floating Bubbles

This artwork is inspired by the simple joy of blowing soap bubbles we used to do as kids. Just like real bubbles, the circles appear wherever you click, float around randomly, and slowly fade away or pop. I wanted each click to feel like blowing a new batch of bubbles, watching them drift and disappear. The way they change color and move in different directions makes them feel alive, conveying the playful nature of real soap bubbles.

Structure

The Circle class manages each individual bubble’s movement, opacity, and lifespan, ensuring that they appear and disappear naturally over time. Functions like setup(), draw(), and mousePressed() organize different parts of the code, keeping it easy to understand and modify.

Challenges

One issue was finding the right balance between movement and fading, so that that bubbles did not disappear too quickly while still feeling transient. Another challenge was making the interaction feel engaging, which I solved by adjusting the number of bubbles created per click and giving them different speeds and directions. Additionally, I had to optimize performance to prevent the sketch from slowing down over time, so circles are now removed once their lifespan ends.

Overall, I appreciate how this piece captures the lighthearted  beauty of soap bubbles in a digital form. To make it more realistic, I’d try to make the direction that bubbles take more random and add the effect of abrupt popping.

// Array to store circles
let circles = [];
let circleSize = 50;
let numCirclesPerClick = 5; // Number of circles generated per mouse click

function setup() {
  createCanvas(windowWidth, windowHeight);
  noFill(); // No fill for circles, only stroke
}

function draw() {
  background(20, 20, 30, 50); 
  
  // Loop through circles in reverse order 
  for (let i = circles.length - 1; i >= 0; i--) {
    circles[i].update(); 
    circles[i].display(); 
    
    // Remove circle when its lifespan reaches zero
    if (circles[i].lifespan <= 0) {
      circles.splice(i, 1);
    }
  }
}

// Circle class (individual moving and fading circles)
class Circle {
  constructor(x, y, size) {
    this.x = x;
    this.y = y;
    this.vx = random(-2, 2); // Assign random speed in x direction
    this.vy = random(-2, 2); // Assign random speed in y direction
    this.baseSize = size;
    this.size = size;
    this.opacity = random(100, 200); 
    this.growthSpeed = random(0.5, 2); 
    this.color = color(random(255), random(255), random(255)); 
    this.lifespan = 200; // Circle disappears after a set time
  }

  // Update circle properties (position, size, opacity, lifespan)
  update() {
    this.x += this.vx; 
    this.y += this.vy; 
    this.size = this.baseSize + sin(frameCount * 0.05) * 10; // Oscillating size effect
    this.opacity -= 2; 
    this.lifespan -= 2; 
  }

  // Display the objects
  display() {
    stroke(this.color);
    strokeWeight(2);
    fill(this.color.levels[0], this.color.levels[1], this.color.levels[2], this.opacity); // fading effect
    ellipse(this.x, this.y, this.size, this.size);
  }
}

// generates multiple circles at the mouse click location
function mousePressed() {
  for (let i = 0; i < numCirclesPerClick; i++) {
    circles.push(new Circle(mouseX, mouseY, circleSize));
  }
}

 

Reading Response – Week 3

I believe that interactivity is like a conversation. It’s alive, reciprocal, and unpredictable. It thrives on an ongoing exchange where both parties listen, process, exchange ideas. Yet sometimes, we tend to mistake reaction for interaction. Imagine a fridge light turning on when you open the door – that’s a reaction. In this context, a true interaction requires intention, curiosity, and a sense of the unexpected.

When designing for interactivity, I want to create projects that thrive on engagement, not just response. In my p5.js sketches, I want to move beyond the input-output relationships and create something that listens and adapts to what the audience feeds to the “artwork”. This may mean making the visuals morph in response to prolonged interaction or rewarding users for exploration rather than just reacting to a single click. In any case, I want each person who interacts with my projects to have a unique experience.

To sum up, I think that a truly interactive system should feel like a dynamic exchange, where the user isn’t just playing with the system but within it. That’s the kind of interactivity I want to create—something more like a meaningful conversation and less like a fridge light turning on and off.

Reading Reflection – Week 2

The first thing about Casey’s presentation that made me challenge my beliefs is that digital art is not real art. I used to think that things that are ‘generated’ are not authentic enough to be considered a unique piece of art. Yet, seeing how much thought and structure it takes to create an abstract pattern with the help of algorithms made me reevaluate my views. It’s fascinating how a series of flat geometric shapes drawn over and over again can create such a lively picture.  This idea is reflected in the quote by Michael Noll, “The computer is a unique device for the arts, since it can function solely as an obedient tool with vast capabilities for controlling complicated and involved processes. But then again, full exploitation of those unique talents for controlled randomness and detailed algorithms can result in an entirely new medium—a creative, artistic medium.” In my future works, I’d like to work more on thinking creatively within a limited range of functions I know.

I believe that uncontrolled randomness leads to complete chaos, while good digital artwork takes a little bit of randomness and a lot of decision-making. As humans, we are set to think in patters, thus it can be quite challenging to create something inherently random, something that has no beginning or end, or doesn’t follow standard rules of composition. I like how Casey uses randomness as a jumping-off point for his artworks such as rolling a dice to trigger the final composition, which is built based on a series of carefully designed algorithms.  

Looking at Casey’s journey, I admire how meticulous he is about building an algorithm and exploring it to the fullest before moving onto a new concept. It seems like he won’t leave his idea until he has tried visualizing it in all ways possible. This reminds me of how famous artists such as Picasso, who went though different ‘eras’ in his art, ended up creating a series of artworks known as The Blue Period. 

My final takeaway from this talk is that true randomness can not be generated by a computer – it has to be “borrowed” from nature, like the patters that bacteria create while reacting to sunlight, where computer serves as a tool to make randomness make sense. 

Week 2 – artwork

Concept

This artwork explores the balance between structure and unpredictability through a dynamic grid of circles. The grid itself represents order and repetition, while the interaction with the mouse introduces an element of spontaneity. As the user moves their cursor, the circles respond by changing size and color, creating a sense of movement and fluidity. This interaction turns the viewer into an active participant, making the piece feel more alive and engaging. The idea behind it is to show how external influences can transform something predictable into something unexpected.

Code Highlight

One of the most interesting aspects of the code is the way it uses a double for-loop to generate a grid of circles. This helps arrange the shapes evenly on the canvas. The interactive part is driven by the dist() function, which calculates how far each circle is from the mouse pointer. That distance then determines the size and color of each circle, making them look as if they reacted to the user’s mouse movements. The map() function helps smooth out these changes by scaling the distance values into a range that works well for the visual effect.

for (let i = 0; i < cols; i++) {
  for (let j = 0; j < rows; j++) {
    let d = dist(mouseX, mouseY, x, y);
    let circleSize = map(d, 0, width, maxSize, minSize);
    fill(map(d, 0, width, 255, 100), 100, 200);
    ellipse(x, y, circleSize);
  }
}

Reflection & Improvements for Future Work

I believe that my project does a great job of showing how loops and interaction can be used to create something visually engaging in p5.js. The grid of circles reacting to the mouse makes the piece feel dynamic, and the changes in size and color add a nice touch of unpredictability. It’s simple, but it keeps things interesting as you move the mouse around.

That being said, there’s definitely room for improvement. Right now, the transitions feel a bit abrupt, so adding some smoothing or easing effects could make the movement more natural. It would also be fun to experiment with extra elements like rotation, fading effects, or even some subtle animation when the mouse isn’t moving. Another cool idea would be giving users more control—maybe letting them change the colors or grid size with sliders or buttons. And if sound was added, it could create an even more immersive experience where the visuals react to music or voice input.

Overall, this was a fun experiment in generative art.

Week 1 – Self-portrait


Reference: I used a picture of myself for official documents to help me vizualize the self-portrait

 

 

 

 

 


Reflection

This task was a really fun and creative way to dive into p5.js. At first, I was a bit nervous because the idea of creating a portrait entirely through code felt like a challenge. However, as I started working with the basic shapes, I realized how much fun it was to build my self-portrait step by step.

I began with simple shapes like ellipses for the head and hair, and a rectangle for the fringe. It was amazing how easily I could combine these basic elements to create the foundation of the portrait. Once I had the basic structure, I worked on adding details like the eyes, eyebrows, and nose.

My favorite part of the process was creating the blush effect on the cheeks. I used lines to make small strokes under the eyes, which gave the face a lot of character and expression. It felt like a small touch, but it made a big difference in bringing the portrait to life.

By the end of the task, I was surprised by how much I learned about coding and how creative you can be with simple drawing functions. One thing that bothered me is how time-consuming it can be to find the right “coordinates” on the canvas. It would have been helpful if I could hover over a specific spot where I’d want my object to start and see the coordinate number.

function setup() {
  createCanvas(400, 400);
  background(255, 182, 193);

  // Hair
  fill(110, 70, 50);
  ellipse(200, 225, 200, 290);
  
  // Head
  fill(240, 209, 190);
  noStroke();
  ellipse(200, 200, 150, 180);

   //Fringe 
  fill(110, 70, 50); // Same color as hair
  noStroke();
  rect(140, 110, 120, 30); 
  
  // Eyes
  fill(255);
  ellipse(170, 200, 30, 20); // Left eye white
  ellipse(230, 200, 30, 20); // Right eye white
  fill(150, 75, 0);
  ellipse(170, 200, 10, 10); // Left iris
  ellipse(230, 200, 10, 10); // Right iris
  
  // Blush under left eye
  stroke(255, 182, 193); // Light pink color for blush
  strokeWeight(2);
  line(150, 220, 155, 225); // First line
  line(160, 220, 165, 225); // Second line
  line(170, 220, 175, 225); // Third line

  // Blush under right eye
  line(220, 220, 225, 225); // First line
  line(230, 220, 235, 225); // Second line
  line(240, 220, 245, 225); // Third line

  // Eyebrows
  stroke(80, 50, 40);
  strokeWeight(4);
  noFill();
  arc(170, 190, 40, 30, PI + QUARTER_PI, TWO_PI - QUARTER_PI); // Left   eyebrow
  arc(230, 190, 40, 30, PI + QUARTER_PI, TWO_PI - QUARTER_PI); // Right   eyebrow


  // Nose
  noFill();
  stroke(150, 100, 90);
  strokeWeight(2);
  beginShape();
  vertex(200, 205);
  vertex(195, 225);
  vertex(200, 230);
  endShape();

  // Mouth
  noStroke();
  fill(180, 80, 90);
  arc(200, 260, 50, 30, 0, PI);

  // Earrings
  fill(255, 255, 0);
  ellipse(125, 220, 10, 10); // Left earring
  ellipse(275, 220, 10, 10); // Right earring

   // Shirt
  fill(245, 245, 220);
  rect(140, 340, 120, 80);
  arc(200, 340, 120, 80, PI, 0);
  
  // Neck
  fill(240, 209, 190);
  rect(175, 270, 50, 35);

}