For this weeks production assignment of creating an artwork using animation, conditionals, loops, I wanted to create a gamified artwork based on a slice of life of my cat Piku.
This is Piku! A cat that loves showers more than anything(strangely)!
So I quickly sketched up my plan for today’s artwork which roughly looked like this-
I wanted to implement animation, conditionals, and loops to bring everything to life. I imagined:
-Window blinds that could open and close smoothly, setting the scene.
-A background with clouds and a sun, all randomly generated to make things feel dynamic. Also, day to night transition.
-Foam filling up the bathtub, to give it some depth.
-Dirt appearing on the cat, to make it interactive
-A sponge that could move around as a cursor and detect collision when it touched the dirt, making it disappear.
-Bubbles floating around animation to show that the dirt splashes are being cleaned
At first, it all sounded doable in my head. But once I started coding, I quickly realized just how many moving parts there were. Making sure the sponge actually removed the dirt was tricky. Getting the animations to feel smooth took a lot of tweaking. And sometimes, the whole thing would just break for no reason, forcing me to backtrack and figure out what went wrong.
The Part that took the longest: Collision detection!
The dirt and sponge interaction was by far the toughest challenge. At first, I thought it’d be simple—just check if the sponge touches the dirt and remove it. Spoilers- it wasnt. What Went Wrong?
-Frames: The dirt sometimes disappeared too fast or not at all because collisions were checked every frame, making it inconsistent.
-Layering: Dirt kept reappearing because new frames kept redrawing it, even after “removal.”
-Messed-up logic: I tried so many broken approaches—checking collisions wrong, deleting dirt before drawing it, and even accidentally scrubbing the whole screen at once.
The Fix
I stored dirt in an array and only removed pieces when they were properly scrubbed:
let dirtParticles = Array.from({ length: 20 }, () => ({ x: random(width), y: random(height), scrubbed: false })); function draw() { background(220); let sponge = { x: mouseX, y: mouseY }; fill(255, 204, 0); ellipse(sponge.x, sponge.y, 50, 30); dirtParticles = dirtParticles.filter(dirt => { let scrubbing = dist(sponge.x, sponge.y, dirt.x, dirt.y) < 30; if (!scrubbing) { fill(139, 69, 19); ellipse(dirt.x, dirt.y, 15, 15); } return !scrubbing; }); }
And after hours and hours of googling every function ever, here’s the final result:
What I’d Do Differently
This project was a huge learning experience. If I had to do it again, I’d plan my code structure better from the start. I ran into so many issues with layers, frame updates, and collision logic, which could have been avoided if I had mapped out how each element would interact beforehand.Also, I’d put more focus on the aesthetic aspects of the project. This project forced me to dive deep into some tricky p5.js functions and concepts, including:
filter()
push()
/pop()
frameRate()
and frame-based logic- Arrays and Objects
- Background Gradient
- Animation Timing
This project had its frustrating moments, especially when things wouldn’t behave the way I expected. But in the end, seeing everything finally come together was incredibly rewarding.