Midterm project

 Concept

A while ago, I was really into blind boxes with figures. Each time a new collection dropped, I would run from store to store trying to find the exact figure I wanted. But here is the beauty of blind boxes: you never know what you will get. Still, as a teenager with a tight budget but a lot of patience, I found ways to identify the exact figures I needed. The most useful method was simply holding the box and shaking it. Since each figure has its own weight, the feeling in hand and the sound would differ from figure to figure. For my midterm project, I replicated this exact concept, but in a virtual dimension.

A player clicks on the vending machine, also known as the robo-shop, and is then given a choice between nine boxes that look identical from the outside but contain different figures inside. At the top, there is a button that gives an overview of each figure in the collection along with its weight range. The player’s mission is to shake each box and identify their desired figure by the sound it makes.

Making an individual, accurate shaking sound for every single figure with marginal weight differences would be too complicated and difficult to pull off realistically. Because of this, I made one sound per weight range instead (light, medium, heavy). This means it becomes impossible for the player to perfectly identify the exact figure they want just from the sound alone. They know the possible figures and their weights, but do not know which box contains which figure.

This keeps the player more engaged, since they have to rely on careful guessing rather than a guaranteed answer. It also reflects how it actually was in real life, where I would spend hours just shaking and weighing boxes in my hand, trying to figure out which exact box had the figure I wanted, just to find out that both boxes had absolutely different figures than what I wanted, which would send me off to another store, continuing my hunt.

I think this element of luck adds more challenge to the experience and keeps the player more engaged and determined to get their desired figure. It also mirrors the real emotional rollercoaster of blind box shopping, where the uncertainty itself is part of what makes the experience so addictive and exciting in the first place.

Hirono “The Other One” series © Pop Mart

For the figures themselves, I used Popmart’s Hirono “The Other One” series. Hirono is one of the figures I have collected the most myself, and it is also one of the most popular lines among blind box collectors in general. So I thought it would be the most fitting choice for this project. Another reason I went with an existing series is because blind box game needs a whole series of figures and designing an entire original lineup from scratch would take far too much time and level of drawing skill I do not have. Using Hirono’s existing designs let me focus my time on the actual game mechanics.

Parts that went well

Before touching any code, I sketched out the basic flow of the game on paper to work through the core idea first.

 

Paper draft

On the sketch you can see two early screens. On the left is the vending machine itself, with a simple grid representing the boxes inside and a small screen next to it. On the right is the box selection screen, where the player picks a box after choosing from the machine.

And this is how two screens actually turned out. I added more details to the vending machine, and for the touchscreen I used a pixelated image of actual Hirono figures generated by AI to make it feel more realistic and interactive. I kept the box selection page almost the same as the draft, except it looks less messy.

let pickbox = false 
let collection = false
let result = false 

// pickbox popup
  if (pickbox) {
    fill(0, 0, 0, 120);
    rect(0, 0, width, height);

    fill(255, 250, 235);
    stroke('#5a4d33');
    strokeWeight(3);
    rect(520, 150, 500, 670, 20);

    fill(50);
    noStroke();
    textSize(24);
    text("SHAKE AND CHOOSE YOUR BOX!", 600, 195);

// collection popup
  if (collection) {
    fill(0, 0, 0, 150);
    rect(0, 0, width, height);

    fill(255, 250, 235);
    stroke(90, 77, 51);
    strokeWeight(3);
    rect(350, 120, 750, 680, 20);

    fill(50);
    noStroke();
    textSize(30);
    text("COLLECTION", 650, 170);

// result popup
  if (result) {
  //very grey overlay
    fill(0, 0, 0, 150);
    rect(0, 0, width, height);

    fill(255, 250, 235);
    stroke(90, 77, 51);
    strokeWeight(3);
    rect(520, 200, 500, 450, 20);

    fill(50);
    noStroke();
    textSize(26);
    text("YOUR REVEALED FIGURE!", 630, 250);


    if (selectedBox) {
      image(selectedBox.img, 640, 280, 280, 280);
      textSize(20);
      text("Weight: " + selectedBox.weight, 705, 570);
    }

Boolean screen states were something I learned to use during this project. It is an interesting way to switch between pages, when you click something, the screen state gets triggered and draws the next page. Clicking a button flips a boolean, and the next frame shows the new popup.

figuresData = [
    { img: hirono1, weight: "light", sound: light },
    { img: hirono2, weight: "medium", sound: medium },
    { img: hirono3, weight: "light", sound: light },
    { img: hirono4, weight: "medium", sound: medium },
    { img: hirono5, weight: "heavy", sound: heavy },
    { img: hirono6, weight: "light", sound: light },
    { img: hirono7, weight: "medium", sound: medium },
    { img: hirono8, weight: "heavy", sound: heavy },
    { img: hirono9, weight: "heavy", sound: heavy }
  ];
 if (selectedBox) {
      image(selectedBox.img, 640, 280, 280, 280);
      textSize(20);
      text("Weight: " + selectedBox.weight, 705, 570);
    }

I used AI to help me find a way to assign each figure its specific weight range and shake sound. The solution was to store every figure as an object in an array called figuresData, with three properties: its image, its weight category (light, medium, or heavy), and the shake sound that matches that category. I needed this because the boxes all look identical, so the game has to remember which figure is inside each one and what it should sound like.

Sketch

(open in fullscreen for proper experience)

Areas for improvement

I think I did a pretty good job recording the shake sound effects, but I realized that for someone who has no experience with shaking blind boxes, it is slightly confusing to tell the difference between light and medium. From my understanding, the lighter ones have more room for movement inside the box, and you can tell that from the sound. Without this kind of context knowledge, though, the difference may be challenging to notice, since the sounds are similar to each other.
I would like to add more figure series to the game, or design my own collection using AI. More series would give the game more variety and replay value, and an original collection would let me avoid depending on existing figures while keeping full control over how each figure looks and how heavy it is.

References

Figures and artwork
Pop Mart, Hirono “The Other One” series

AI tools
Google Gemini to create touchscreen image, also helped to create an array to assign weight and shake sounds to figures

Code and libraries
https://p5js.org/reference/p5/shuffle/
https://p5js.org/reference/p5/mousePressed/
https://p5js.org/reference/p5.sound/
https://p5js.org/reference/p5/fullscreen/

Sounds and music
Background music: Matsuoka, Daisuke, and Asuka Ito. “Map (Night).” Tomodachi Life, Nintendo, 2013.
Tada fanfare sound: https://pixabay.com/sound-effects/musical-tada-fanfare-a-6313/
Shake sound effects: recorded by me

Fonts
Galano Grotesque
Alegreya Sans

Assignment 5: Midterm progress

Concept

A while ago, I was really into blind boxes with figures. Each time a new collection dropped, I would run from store to store trying to find the exact figure I wanted. But here is the beauty of blind boxes: you never know what you will get. Still, as a teenager with a tight budget but a lot of patience, I found ways to identify the exact figures I needed. The most useful method was simply holding the box and shaking it. Since each figure has its own weight, the feeling in hand and the sound would differ from figure to figure. For my midterm project, I want to replicate this exact concept, but in a virtual dimension.


A player clicks on the vending machine, also known as the robo-shop, and is then given a choice between six or more boxes that look identical from the outside but contain different figures inside. At the top, there is a button that gives an overview of each figure in the series along with its weight. The player’s mission is to shake each box and identify their desired figure by the sound it makes.

Challenging parts

1. A blind box game needs a whole series of figures and designing all of that from scratch would take far too much time and a level of drawing skill I do not have.

Hirono “The Other One” series © Pop Mart

So I am considering two solutions. The first is to use Hirono figures from Popmart, since Hirono is one of my most collected figures and it is also very popular so it would very accurately convey my and most blind box lovers’ experiences. The second is to generate a completely new series of figures with AI, which gives me original designs and full creative control.

 

2. Making an individual, accurate shaking sound for every single figure with marginal weight differences would be too complicated and difficult to pull off realistically.

Because of this, I am probably going to make one sound per weight range instead. But this means it becomes impossible for the player to perfectly identify the exact figure they want just from the sound alone. They know the possible figures and their weights, but do not know which box contains which figure. However, I think this exact element of luck adds more challenge to the game and keeps the player more engaged and determined to get their desired figure. It also reflects how it actually was in real life for me, where I would spend hours just shaking and weighing boxes in my hand, trying to figure out which exact box had the figure I wanted. Only to find out, that after all that effort neither box actually had the figure I was looking for, sending me off to another store to continue the hunt.

 

3. Also, I am still yet to find a way to make smooth animations for the box shake and openings. Since shake and listen is the main mechanic of the game, I would like animations to look smooth enough that the game feels pleasant to play. That said, I am not aiming for anything too fancy. I want to keep the animations simple and just make sure they feel good enough to support the game.

Week 5: Reading Reflection

After reading this, I realized the way computers see things is pure math and calculations. They do not have an understanding of things like we humans do. Computers are like blank slates and for them to process anything, we have to program every single step. A computer cannot detect a person unless a programmer writes math rules to help it spot changes in pixels. I was surprised at how a seemingly simple concept like vision and tracking is actually so much more complex.

I really liked that a lot of the detection in these systems does not come from the code alone, but also from controlling the physical environment. Things like using a plain white background, strong lighting, or high contrast clothing make it way easier for simple algorithms to work well. So it is a process that requires precision both inside and outside of the code. Even a simple change in lighting or shadows can completely confuse the computer.

Also, the duality of this technology is interesting. Computer vision was originally developed primarily for tracking and surveillance. However, it later became a popular tool that artists use in interactive art. Depending on whose hands this technology is in, it can transform from an entertaining experience into something far more invasive or dangerous. It made me realize that as someone who might work with tracking tech in the future, I need to use it carefully and be mindful of where and how I use it.

Week 4: Reading Reflection

Two weeks ago, I got really bored of the food in the D2 dining hall and knew it was time to finally explore new horizons, so I made my way to D1. But unlike D2, where you just pick your food and pay at the end, the system in D1 was totally different and a lot more complicated. First, you walk through the different food sections looking at the menus and decide what you want to order. Then you go to the payment place at the end, where you place your order, pay, get a pager, and receive your receipt. But it turns out that is not the end. You also have to bring that receipt back to the food station that cooks your meal and hand it over, and only then do they start cooking. I had no idea about any of this, and nothing in the place made it obvious, so I was sitting there with my pager waiting for my bao buns that nobody had started making. Later, I found out that many of my friends went through a similar experience during their first time at D1.

The problem is that there were no signifiers to guide me through the ordering process. Adding some arrows or signs that show where to go would be a good start. For the payment and receipt part, there are two possible solutions. The first is easier to implement, while the second would make things more convenient. My first solution is to add a sign at the cashier that tells customers to hand their receipt over to the food station. The second solution is to automate the process by having the cashier send the order directly to the kitchen. Of course, I have no clue how the system works behind the scenes in dining halls, so these are just my simple suggestions.

Also, looking back at my sketches in p5.js, I realize that I barely give any signifiers in them. In my Beatles vinyl record sketch, I don’t think I gave a strong signifier that the disc is spinning, since the hoops are all symmetric and because of this the rotation is barely visible. The viewer cannot even tell that the whole disc is spinning. Even a simple visible indicator, like a small dot placed near the edge of the disc, could work as a signifier. Since the viewer could watch that dot move around the center and understand that the whole record is rotating. The same problem is in my fireflies sketch, where nothing tells the viewer that they can interact with the fireflies by moving the mouse over the canvas. Someone opening the sketch might just watch the fireflies float around and never discover that they react to the mouse cursor.

Assignment 4: Loading Data, Displaying text

Sketch

Concept

Since I am a huge Beatles fan, for this assignment I decided to use a dataset of Beatles songs popularity on Spotify and visualize it in p5. Each song is represented as a hoop, and the more popular the song is on Spotify, the bigger its hoop is. The hoops are all stacked on top of each other, and the colors show how many songs share that popularity range. A brighter, more solid white hoop means more songs fall into that range, while a more transparent, greyish hoop means fewer songs are in there. For the visual style, I designed it to resemble a vinyl record, using the Abbey Road album cover as the center label.

The Beatles, Abbey Road (1969)

Code Highlight

let popularity = float(songs.getColumn("Popularity"));
let minPop = min(popularity);
let maxPop = max(popularity);
let minRadius = 82;
let maxRadius = 240;

for (let i = 0; i < rowsNum; i++) {
    let radius = map(popularity[i], minPop, maxPop, minRadius, maxRadius);
    noFill();
    stroke(255, 30);
    strokeWeight(2);
    circle(0, 0, radius * 2);
  }

This part of the code is one of the main things that allows me to visualize the numbers from my .csv file on my actual sketch canvas. It basically converts the raw popularity numbers into a range that fits the canvas, scaling each song’s popularity into a radius between 82 and 240.

push();
translate(300, 300);
rotate(angle);

angle += 0.01;
pop();

I used translate to move the origin from the default top left corner (0,0) to (300, 300) which is the center of my canvas. I did this so the vinyl record spins properly around its own center, instead of spinning from the upper left corner and taking the whole thing off the canvas.

How this was made

This sketch loads a Beatles songs dataset and turns it into a spinning vinyl record visualization. In setup, the .csv file with songs and Spotify popularity values is loaded using loadTable, and the Abbey Road logo is loaded using loadImage.

Then I used min and max to find the largest and smallest numbers in the popularity column, and used the map function to stretch those values so they fit properly within my canvas. Using these mapped values as the radius, I created white hoops with slight transparency, where each song gets its own hoop sized according to its popularity. Every hoop uses no fill and a low opacity white stroke, so they visibly stack on top of each other and kind of display the quantity of songs within a specific popularity range.

In the middle of my vinyl record, I added the Abbey Road logo image that I loaded in setup. Then, to make the vinyl record rotate, I used the rotate function paired with translate, changing the origin point to the center of my canvas so the disc spins around its own center instead of the corner.

Reflection

The main issue my current sketch has is that the hoops are all perfectly symmetric circles, and because of that, it is actually impossible to see that the record is rotating at all. To fix this I think I could add some kind of texture to the hoops, or maybe add a light glare effect or small visual markers that would make the spinning motion noticeable.

Another problem I ran into was a performance difference between operating systems. The sketch runs completely smooth on macOS, but when I tried running it on a Windows laptop, it became very laggy, even though the Windows laptop has much stronger specs than the MacBook I initially built and tested this sketch on. This came as quite a surprise to me, and I was not able to find a fix for it. Most likely, the issue lies in how the two operating systems handle rendering differently.

Week 3: Reading Reflection

Before this reading, I had never had a clear definition of what interactivity means. Now I realize that one cannot call something interactive just because it reacts to input. If we consider the superficial meaning of interactivity that many people use, then even turning on a light is considered interactive. You press a button, and in response the light bulb turns on. Using this definition of interactivity is meaningless, since it makes almost everything interactive. For something to be truly interactive, it has to involve two sides that alternately listen, think, and speak.

It has to be like a dialogue between two sides, where each action shapes and changes future inputs. This made me rethink my last assignment, where I created a sketch with fireflies that fly away when the mouse cursor gets close. I used to think of this as interactivity, but it is really just a simple reaction to input. It is not an interactive dialogue, since only one side is speaking while the other is just listening without giving any response back.

I think I could make the fireflies respond differently depending on how the user moves the mouse. For example, if the user moves the mouse slowly, the fireflies could drift away calmly, but if the cursor moves faster, the fireflies could fly away more dramatically. This would at least make the interaction feel like the fireflies are thinking before reacting, rather than just responding the same way every time.

Assignment 3: Functions, Arrays, and OOP

Sketch

Concept

The idea for this sketch came to me after recently watching the animated film “Grave of the Fireflies” and I decided to make a sketch with fireflies that move in random directions. To imitate actual firefly behavior, I made them fly away when you bring the cursor closer to them, just like how they avoid human touch. I always thought of fireflies as truly magical creatures, so this idea felt the closest to me.

Grave of the fireflies (1998)

Code Highlight

 this.transp = random(50, 255);
 this.fadeSpeed = random(1, 3);

this.transp += this.fadeSpeed;
    if (this.transp > 255 || this.transp < 50) {
      this.fadeSpeed *= -1;

The most interesting part of this code is the fading fireflies effect. This part creates the pulsing fade effect for each firefly using two values, transp and fadeSpeed. When a firefly is created, transp starts as a random value between 50 and 255, which sets how visible the firefly looks at first. fadeSpeed starts as a random value between 1 and 3, which sets how fast the firefly light fades. Every frame, fadeSpeed is added to transp, making it slowly rise or fall. Once transp goes above 255 or below 50, the fadeSpeed flips direction so it fades back the other way.

Also, a problem I ran into was when the center of a firefly’s circle reached the edge of the canvas, it would just disappear and teleport straight to the opposite side, which looked abrupt and ruined the whole sketch. To fix this, I adjusted the restart function to give the firefly a small buffer of 14 pixels past the edge before it teleported to the other side.

restart() {
    if (this.x > width+14) {
      this.x = 0-14;
    }
    if (this.x < 0-14) {
      this.x = width+14;
    }
    if (this.y > height+14) {
      this.y = 0-14;
    }
    if (this.y < 0-14) {
      this.y = height+14;
    }
  }

 

How this was made

This sketch was built using two separate classes. The first class, called fli, handles the fireflies, and the second class, called star, handles the background stars. The stars are very simple, as they are just small ellipses with random positions in the background without any movement. Their sizes and transparency or brightness are also random between certain values to create variety. The fli class, on the other hand, is a little more complicated. Each firefly gets random values for position, speed, size, transparency, and fade speed when it is created. In the move function, the firefly drifts slowly across the screen, and its transparency rises and falls between 50 and 255 to create a glow effect. When the mouse comes within 70 pixels, the firefly flies away from the cursor. The restart function makes fireflies wrap around to the opposite edge instead of disappearing when they leave the canvas.

Reflection

While making this sketch I used a lot of randomness across all sorts of parameters. This approach definitely made the whole picture look more natural. Since the main focus of my sketch was fireflies, I had to make sure their behavior felt believable and alive. Adding randomness into parameters like size, speed, and fade timing made the fireflies move and glow at their own pace instead of all acting the same, which created a harmonic, natural looking scene overall.
If I worked on this again I would try to make the glowing effect more realistic, maybe by making a color transition between circles smoother. I would also change how the fireflies avoid the mouse to make that interaction feel more realistic, since the movement feels kind of stiff right now.

Week 2: Reading Reflection

As a perfectionist, the word “chaos” would always evoke negative emotions in me. It is a word that feels like the direct antonym of my favorite word, “perfection,” and so it never quite sat right with me. However, listening to Casey Reas’ speech made this word more tolerable, as the examples of work he showed made me see a certain beauty in randomness.

For me, the most meaningful takeaway was that adding elements of randomness can sometimes help keep a work alive and dynamic. Previously, when I tried to incorporate randomness into my work, it would usually result in a messy, overwhelming chaos. But now I realize that to avoid that kind of mess, you need to set certain limits and keep a balance, something like 75% control and 25% chaos. My mistake was that I overemphasized and exaggerated the chaos, turning my work into an uncontrollable mess.

In a recent assignment for this class, I ran into something similar. I created a perfect grid of squares, but that perfection made the sketch look dead and homogenous. Then I experimented with spacing and movement, and eventually found my ideal ratio of randomness to order, something I’d call “controlled mess.” This change made the sketch look more alive and visually interesting.

In my future projects, I plan to expand my use of chaotic elements, but with the same rules in mind. I am eager to experiment and discover ways in which I can add random elements to parameters like size, rotation, or even frameRate to create unorthodox projects that still feel ordered and meaningful.

Assignment 2: Animation, Conditionals, Loops

Sketch

Concept

Fun fact: my work was unintentionally inspired by Georg Nees’ “Schotter“.
My first Idea was to recreate colorful bathroom tiles, like the ones in old style houses, and I built a grid of squares in perfect order. But as I worked through the project, that perfect order started to feel boring to me. So I decided to add some chaos by making the squares move and rotate. Later, I realized that I had subconsciously taken inspiration from one of the artworks I had reviewed earlier before starting my sketch. Once I noticed that, I began taking inspiration from that work on purpose. Obviously, it is not a 1 to 1 copy of Georg Nees’ work, but my own version with animation that resembles the flow of a waterfall.

George Nees “Schotter”

Code Highlight

for (let x = 0; x <= width; x += 25) {
  for (let y = 0; y <= height; y += 25) {
    
    push();
    //The lower it gets, the more it moves
    translate(x + random(-y / 30, y / 30), y + random(-y / 30, y / 30));

    // The lower it gets, the more it rotates
    rotate(radians(random(-y / 10, y / 10)));
    
    stroke(random(150, 255), 100, 220);
    strokeWeight(random(1, 2.5));
    rect(0, 0, 22, 22);
    pop();
  }
}

The hardest part was creating a falling effect where the movement increases as the squares go down. To do that I used:

translate(x + random(-y / 30, y / 30), y + random(-y / 30, y / 30)) rotate(radians(random(-y / 10, y / 10)));

So as the Y axis value increases, the range of movement gets larger too. As you can see the first row squares are not moving at all. But the squares further down drift and rotate randomly.

How was this made

First, I set up a basic grid with two nested for loops, one for x and one for y, and spaced the squares 25 pixels apart across the whole canvas. Next, to create the progressive breakdown effect, I used translate() and rotate(), and divided my y value inside the random() functions. Because y value is small at the top of the canvas, those squares barely move at all. But as y increases going down the canvas, the range passed into random() gets wider, so the lower squares shift and rotate more. To accomplish this I had to look up translate() and rotate() functions on p5js reference page.

Reflection

Looking back, this project turned out quite different from what I initially planned. At the start, my idea was simply to make an artwork similar to colorful bathroom tiles. But as I kept working on my code, my plan swerved in a direction I wasn’t expecting. If I had more time, I would definitely experiment more with the color palette and add some sort of fading effect as the squares “fall down.” It would also be interesting to add elements of interactivity, like letting the mouse position affect how the squares fall apart.

Assignment 1: Pikachu portrait

For my first assignment in Intro to Interactive Media, I decided to recreate Pikachu using p5.js. To build the sketch, I combined basic geometric shapes like circles, triangles, and quads with a few custom shapes for details like Pikachu’s unique tail.

How this was made

I started by making Pikachu’s body using a simple rectangle shape. Next, I added a circle for the head. I used smaller circles and arc function to draw the eyes, nose, mouth, and bright red cheeks. For the ears, I combined long arcs with sharp triangles at the tips to imitate Pikachu’s pointed look. I also used AI to get an explanation on how to use the rotate function properly so I could angle the ears correctly. The main problem was Pikachu’s tail, which has a unique zigzag shape that is hard to recreate with basic shapes. I explain more on how I managed to draw the tail in the next section.


Code highlight

//Tail  
  beginShape();
vertex(235, 216);
vertex(252, 210);
vertex(248, 195);
vertex(262, 190);
vertex(256, 172);
vertex(290, 160);
vertex(275, 126);
vertex(230, 150);
vertex(241, 183);
vertex(234, 186);
vertex(237, 197);
vertex(230, 200);
  endShape(CLOSE);
//Brown part of the tail
  beginShape();
fill('brown');
vertex(248, 199);
vertex(231, 204);
vertex(234,216);
vertex(252, 210);
  endShape(CLOSE);

After finishing this part of the code, I felt very satisfied with my work. At first, I drew the entire tail outline using line() functions. However, after finishing it, I realized that lines cannot be filled with color. I then searched through the p5.js reference page and discovered that I could use the beginShape() function to create any shape I want by plotting coordinate points. I utilized this function, plotting each vertex to finally make a shape that matched that specific zigzag pattern and filled it smoothly with yellow color.

This assignment turned out to be a bit more challenging than I initially expected. Still, I really enjoyed the process of figuring out different ways to draw the specific shapes I had in mind.
For future assignments, I would love to move toward more realistic sketches, and eventually start adding animation into my work.