Sketch 2026-04-16 13:24

This interactive game lets players control a brown dog character using arrow keys and drag colorful food items to feed it. As the dog eats, a 'Poo Power' meter fills up, and pressing 'P' triggers an animated poo release with synthesized sound effects—a quirky, playful experience combining movement, drag-and-drop interaction, and Web Audio API synthesis.

🧪 Try This!

Experiment with the code by making these changes:

  1. Change the dog color — Modify the dog's fill color to make it a different breed; try red, white, or even cyan
  2. Make the poo power fill faster — Increase the amount of poo power gained per food item so you can poo more frequently
  3. Speed up the dog — Increase the dog's movement speed so it darts around the canvas faster
  4. Make food items bigger — Increase the food size so they're easier to see and drag
  5. Change the sky color — Replace the light blue background with a different color—try orange, green, or pink
  6. Make the poo animation longer — Increase the poo timer so the blob stays visible and the sound plays longer
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch is a complete, playable game where you control a dog with arrow keys and drag food items across the screen to feed it. As the dog eats, a purple power bar fills up; when it's full, press 'P' to release 'Poo Power'—an animated brown blob appears at the dog's position with a synthesized 'plop' sound. The sketch demonstrates several advanced p5.js techniques: object-oriented design with custom Dog and Food classes, mouse interaction (click-and-drag), keyboard input, collision detection, animation state management, and Web Audio API sound synthesis using p5.sound's oscillators and noise generators.

The code is organized into three main classes—Dog, Food, and global game state variables—plus helper methods for sound playback. By studying it, you will learn how to structure a complete interactive game in p5.js: how to handle multiple moving objects, detect when objects collide, synthesize sound procedurally instead of loading audio files, manage animation state with flags and timers, and respond to both mouse and keyboard input simultaneously. The sketch also shows responsive canvas sizing and introduces p5.sound's oscillators, noise generators, and amplitude control for dynamic audio creation.

⚙️ How It Works

  1. When the sketch loads, preload() initializes four sound generators (oscillators and noise nodes) and sets them to silent, and setup() creates a responsive canvas, spawns a dog in the center, and fills the screen with five randomly-positioned and randomly-colored food items.
  2. Every frame, draw() clears the background and redraws the dog at its current position, all food items, and displays game instructions at the bottom of the screen. If the dog is currently pooing, a brown ellipse is drawn and a low-frequency sine tone plays for two seconds, then fades out.
  3. When you press an arrow key, the dog.move() method updates its x or y position and constrains it to stay within the canvas boundaries using constrain().
  4. When you click on a food item, mousePressed() detects which food is under the cursor using dist() and distance-based collision, calls startDrag() to begin tracking the food's offset from the mouse, and plays a soft synthesized bark using the triangle oscillator.
  5. As you drag the food, updateDrag() continuously repositions it to follow your mouse while respecting the offset you started with, keeping the canvas bounds tight using constrain().
  6. When you release the mouse, mouseReleased() checks if the food is now close enough to the dog (another dist() check in collectFood()); if so, the food disappears, pooAmount increases by 25, a crunch sound plays using pink noise, and a new food item spawns randomly. If pooAmount reaches maxPooAmount (100), the 'Press P to POO!' instruction appears.
  7. Pressing 'P' calls dog.poo(), which resets pooAmount to zero, sets isPooing to true, stores the dog's current position in pooX and pooY, starts a 120-frame animation timer, plays a bark sound, and increments the poo counter. The next 120 frames display a brown blob and play a sine-wave 'plop' sound that fades out.

🎓 Concepts You'll Learn

Object-Oriented Programming (Classes and Methods)Mouse Interaction (Click, Drag, Release Detection)Keyboard Input (keyPressed, keyIsDown with Arrow Keys)Collision Detection (dist() function)Animation State Management (Flags and Timers)Web Audio Synthesis (Oscillators, Noise, Amplitude Envelopes)Canvas Responsiveness (windowResized)Game Loop Architecture

📝 Code Breakdown

preload()

preload() is a special p5.js function that runs once at startup, before setup(). It's the right place to initialize expensive resources (like sound generators and images) so they're ready to use instantly. All four sound generators are created, configured, and started here in silent mode (amp 0) so that later methods like playDogBark() can instantly change the amplitude and frequency without waiting for them to initialize.

function preload() {
  dogBarkOsc = new p5.Oscillator();
  dogBarkOsc.setType('triangle');
  dogBarkOsc.freq(200);
  dogBarkOsc.amp(0);
  dogBarkOsc.start();

  dogBarkNoise = new p5.Noise('white');
  dogBarkNoise.amp(0);
  dogBarkNoise.start();

  eatSoundNoise = new p5.Noise('pink');
  eatSoundNoise.amp(0);
  eatSoundNoise.start();

  pooSoundOsc = new p5.Oscillator();
  pooSoundOsc.setType('sine');
  pooSoundOsc.freq(100);
  pooSoundOsc.amp(0);
  pooSoundOsc.start();
}
Line-by-line explanation (11 lines)

🔧 Subcomponents:

calculation Dog Bark Oscillator Setup dogBarkOsc = new p5.Oscillator(); dogBarkOsc.setType('triangle'); dogBarkOsc.freq(200); dogBarkOsc.amp(0); dogBarkOsc.start();

Creates a triangle-wave oscillator at 200 Hz for the dog's bark pitch; starts silent (amp 0) and begins oscillating in the background so it's ready to play instantly when needed

calculation Dog Bark Noise Setup dogBarkNoise = new p5.Noise('white'); dogBarkNoise.amp(0); dogBarkNoise.start();

Creates white noise for bark texture and starts it silent—when mixed with the oscillator, creates a more realistic 'growly' bark sound

calculation Eat Sound Noise Setup eatSoundNoise = new p5.Noise('pink'); eatSoundNoise.amp(0); eatSoundNoise.start();

Initializes pink noise (which sounds more crunchy than white noise) for the food-eating sound effect

calculation Poo Sound Oscillator Setup pooSoundOsc = new p5.Oscillator(); pooSoundOsc.setType('sine'); pooSoundOsc.freq(100); pooSoundOsc.amp(0); pooSoundOsc.start();

Creates a low-frequency sine wave (100 Hz) for the poo 'plop' sound; starts silent and running so it plays instantly on demand

function preload() {
preload() runs once before setup(), making it the perfect place to initialize sound generators that must be ready before the user interacts
dogBarkOsc = new p5.Oscillator();
Creates a new oscillator object that will generate a continuous sine wave by default (we'll change it to triangle next)
dogBarkOsc.setType('triangle');
Changes the waveform from sine to triangle, which sounds slightly more 'bark-like' than a pure sine wave
dogBarkOsc.freq(200);
Sets the oscillator's base frequency to 200 Hz—a reasonable pitch for a dog bark (lower than human speech, higher than a rumble)
dogBarkOsc.amp(0);
Sets amplitude to 0, making the oscillator silent—this is essential because the oscillator is already running in the background
dogBarkOsc.start();
Starts the oscillator running in the background at zero volume—when we call amp(0.3) later, the sound plays immediately without a startup delay
dogBarkNoise = new p5.Noise('white');
Creates a white noise generator (random static-like sound) that will be layered with the oscillator to add texture to the bark
eatSoundNoise = new p5.Noise('pink');
Creates pink noise (which emphasizes lower frequencies and sounds more like rustling or crunching than white noise)
pooSoundOsc = new p5.Oscillator();
Creates another oscillator, this time for the poo sound
pooSoundOsc.setType('sine');
Keeps the sine wave (default)—a pure sine at low frequency will sound like a 'plop' or 'thud'
pooSoundOsc.freq(100);
Sets the poo sound to an even lower frequency (100 Hz) than the bark (200 Hz), making it sound deep and resonant

setup()

setup() initializes your entire game world. Here we create the canvas, spawn the main character (dog), populate the world with food objects, and prepare audio. Every global variable that needs a starting value should be initialized here.

function setup() {
  createCanvas(windowWidth, windowHeight);

  dog = new Dog();

  for (let i = 0; i < 5; i++) {
    foodItems.push(new Food());
  }

  textSize(16);
  textAlign(CENTER, CENTER);

  userStartAudio();
}
Line-by-line explanation (8 lines)

🔧 Subcomponents:

calculation Canvas Setup createCanvas(windowWidth, windowHeight);

Creates a canvas that fills the entire browser window—later, windowResized() will keep it responsive when the window is resized

calculation Dog Object Creation dog = new Dog();

Instantiates the Dog class, which initializes the dog at the center of the canvas with starting values for position, size, speed, and poo power

for-loop Initial Food Spawning for (let i = 0; i < 5; i++) { foodItems.push(new Food()); }

Loops 5 times, creating 5 Food objects at random positions and colors and adding them to the foodItems array

function setup() {
setup() runs once when the sketch first loads—it's where you initialize the game state
createCanvas(windowWidth, windowHeight);
Creates a canvas that matches your browser window size; windowWidth and windowHeight are p5.js built-in variables
dog = new Dog();
Creates a new Dog instance and stores it in the global 'dog' variable so draw() can access it every frame
for (let i = 0; i < 5; i++) {
Loops 5 times (i goes from 0 to 4); this is how we spawn multiple food items at startup
foodItems.push(new Food());
Creates a new Food object and adds it to the foodItems array; each Food gets a random position and color
textSize(16);
Sets the text size to 16 pixels for all text drawn later (instructions, poo counter, poo power bar)
textAlign(CENTER, CENTER);
Aligns text to the center horizontally and vertically—without this, text would draw from its top-left corner
userStartAudio();
Required by modern browsers to enable Web Audio playback; must be called after a user gesture (click/tap). p5.js automatically treats the canvas load as a gesture in most cases

draw()

draw() is the heartbeat of every p5.js sketch. It runs every frame and contains the game logic: updating positions, checking collisions, rendering visuals, and managing state transitions. Notice how we check isPooing as a state flag to control which code runs—this is a common pattern in games. Also notice the backward loop for food items; this prevents array index errors if we removed items during iteration.

function draw() {
  background(173, 216, 230);

  dog.draw();
  dog.move();

  for (let i = foodItems.length - 1; i >= 0; i--) {
    foodItems[i].draw();
    foodItems[i].updateDrag();
  }

  if (isPooing) {
    fill(101, 67, 33);
    ellipse(pooX, pooY, dog.size * 0.6, dog.size * 0.4);
    pooSoundOsc.amp(0.5, 0.1);

    pooTimer--;
    if (pooTimer <= 0) {
      isPooing = false;
      pooSoundOsc.amp(0, 0.5);
    }
  } else {
    pooSoundOsc.amp(0);
  }

  fill(0);
  text("Use ARROW KEYS to move the dog.", width / 2, height - 80);
  text("CLICK AND DRAG food to the dog.", width / 2, height - 60);
  text("Collect food to fill Poo Power.", width / 2, height - 40);

  if (dog.pooAmount >= dog.maxPooAmount) {
    text("Press 'P' to POO!", width / 2, height - 20);
  }

  text("Successful Poos: " + pooCounter, width / 2, 30);
}
Line-by-line explanation (21 lines)

🔧 Subcomponents:

calculation Background Clear background(173, 216, 230);

Clears the canvas and fills it with light blue (RGB 173, 216, 230) every frame, erasing the previous frame's drawings

calculation Dog Update dog.draw(); dog.move();

Redraws the dog at its current position, then updates its position based on held arrow keys

for-loop Food Item Loop for (let i = foodItems.length - 1; i >= 0; i--) { foodItems[i].draw(); foodItems[i].updateDrag(); }

Loops backward through all food items, drawing each one and updating its position if it's being dragged; looping backward allows safe removal during iteration

conditional Poo Animation State if (isPooing) { fill(101, 67, 33); ellipse(pooX, pooY, dog.size * 0.6, dog.size * 0.4); pooSoundOsc.amp(0.5, 0.1); pooTimer--; if (pooTimer <= 0) { isPooing = false; pooSoundOsc.amp(0, 0.5); } }

While isPooing is true, draw a brown poo blob and play the poo sound; decrement pooTimer each frame and end the animation when it reaches 0

conditional Poo Readiness Prompt if (dog.pooAmount >= dog.maxPooAmount) { text("Press 'P' to POO!", width / 2, height - 20); }

Only show the 'Press P to POO!' instruction when the dog has collected enough food to be ready to poo

function draw() {
draw() is the main game loop, called 60 times per second by p5.js by default
background(173, 216, 230);
Fills the canvas with light blue and erases everything drawn in the previous frame—essential for animation
dog.draw();
Calls the Dog object's draw() method, which renders its body, head, tail, and poo power bar at its current position
dog.move();
Calls the Dog object's move() method, which checks which arrow keys are held and updates the dog's x and y position
for (let i = foodItems.length - 1; i >= 0; i--) {
Loops backward through the foodItems array (from the last item to the first); backward iteration is safer if items are removed mid-loop
foodItems[i].draw();
Draws the current food item at its position
foodItems[i].updateDrag();
Updates the food's position to follow the mouse if it's currently being dragged
if (isPooing) {
Checks if the dog is currently in a pooing animation (isPooing flag is true)
fill(101, 67, 33);
Sets the fill color to brown (RGB 101, 67, 33) for the poo blob
ellipse(pooX, pooY, dog.size * 0.6, dog.size * 0.4);
Draws a brown ellipse at the stored poo position (pooX, pooY) with a width of dog.size * 0.6 and height of dog.size * 0.4
pooSoundOsc.amp(0.5, 0.1);
Sets the poo sound's amplitude to 0.5 with a 0.1-second fade-in, making the 'plop' sound play
pooTimer--;
Decrements the poo animation timer by 1 each frame; when it reaches 0, the animation ends
if (pooTimer <= 0) {
Checks if the poo animation timer has expired
isPooing = false;
Sets the isPooing flag to false, stopping the animation next frame
pooSoundOsc.amp(0, 0.5);
Fades out the poo sound amplitude to 0 over 0.5 seconds, creating a smooth sound tail-off
} else {
If the dog is not currently pooing...
pooSoundOsc.amp(0);
Ensures the poo sound is silent when not pooing, preventing unwanted background sound
text("Use ARROW KEYS to move the dog.", width / 2, height - 80);
Draws instruction text at the bottom of the screen (80 pixels up from the bottom)
if (dog.pooAmount >= dog.maxPooAmount) {
Checks if the dog's poo power has reached the maximum needed to release poo
text("Press 'P' to POO!", width / 2, height - 20);
Only displays this instruction when the poo meter is full, cueing the player to press P
text("Successful Poos: " + pooCounter, width / 2, 30);
Displays the poo counter at the top of the screen (30 pixels down), showing how many times the player has successfully released poo

mousePressed()

mousePressed() is called once per mouse click. It's perfect for detecting when the user clicks on something (like a draggable object). Notice how we loop backward through the array and break immediately after finding a match—this ensures only one food item responds to the click, even if they overlap visually. The bark sound happens instantly, giving audio feedback for the click.

function mousePressed() {
  for (let i = foodItems.length - 1; i >= 0; i--) {
    if (foodItems[i].isMouseOver()) {
      foodItems[i].startDrag();
      dogBarkOsc.freq(random(150, 180), 0.05);
      dogBarkOsc.amp(0.2, 0.05);
      setTimeout(() => {
        dogBarkOsc.amp(0, 0.3);
      }, 100);
      break;
    }
  }
}
Line-by-line explanation (10 lines)

🔧 Subcomponents:

for-loop Food Click Detection Loop for (let i = foodItems.length - 1; i >= 0; i--) { if (foodItems[i].isMouseOver()) { foodItems[i].startDrag(); break; } }

Loops through food items backward to find which (if any) has the mouse over it; when one is found, starts dragging it and breaks out of the loop so only one food is dragged at a time

calculation Bark Sound on Click dogBarkOsc.freq(random(150, 180), 0.05); dogBarkOsc.amp(0.2, 0.05); setTimeout(() => { dogBarkOsc.amp(0, 0.3); }, 100);

Randomizes the bark pitch slightly, fades in the amplitude, then uses setTimeout to fade it back out after 100 milliseconds

function mousePressed() {
mousePressed() is a built-in p5.js function that runs once every time the mouse button is pressed
for (let i = foodItems.length - 1; i >= 0; i--) {
Loops backward through all food items looking for one under the mouse cursor
if (foodItems[i].isMouseOver()) {
Calls the Food object's isMouseOver() method, which uses dist() to check if the mouse is within the food's radius
foodItems[i].startDrag();
Calls the Food object's startDrag() method, which sets isDragging to true and stores the offset between the mouse and the food's center
dogBarkOsc.freq(random(150, 180), 0.05);
Changes the bark oscillator's frequency to a random value between 150–180 Hz with a 0.05-second fade-in time—randomizing the pitch makes it sound more natural
dogBarkOsc.amp(0.2, 0.05);
Sets the bark oscillator's volume to 0.2 (20% of max) with a 0.05-second fade-in, making the bark audible
setTimeout(() => {
Schedules a JavaScript callback to run after a delay (in milliseconds)
dogBarkOsc.amp(0, 0.3);
Fades out the bark sound to silence over 0.3 seconds, creating a natural tail-off instead of an abrupt stop
}, 100);
The callback runs after 100 milliseconds (0.1 seconds), so the bark plays for about 100ms then fades out over the next 300ms
break;
Breaks out of the loop so only one food item is selected; without this, the last food in the array under the cursor could also be dragged

mouseReleased()

mouseReleased() is called once per mouse button release. Here, it detects which food was being dragged, stops the drag, and checks if the dog ate it. If the dog is close enough, the food is removed and replaced with a new random one. Notice the backward loop and break statement again—this pattern (backward loop + break) appears throughout the code to handle one action per event safely.

function mouseReleased() {
  for (let i = foodItems.length - 1; i >= 0; i--) {
    if (foodItems[i].isDragging) {
      foodItems[i].stopDrag();
      if (dog.collectFood(foodItems[i])) {
        foodItems.splice(i, 1);
        foodItems.push(new Food());
      }
      break;
    }
  }
}
Line-by-line explanation (8 lines)

🔧 Subcomponents:

for-loop Dragging Food Detection for (let i = foodItems.length - 1; i >= 0; i--) { if (foodItems[i].isDragging) { foodItems[i].stopDrag(); break; } }

Loops backward to find the food currently being dragged, stops dragging it, and breaks so only one is processed

conditional Food Collection Check and Respawn if (dog.collectFood(foodItems[i])) { foodItems.splice(i, 1); foodItems.push(new Food()); }

Calls the dog's collectFood() method (which checks distance and gives the dog poo power); if true, removes the collected food from the array and spawns a new one

function mouseReleased() {
mouseReleased() is a built-in p5.js function that runs once every time the mouse button is released
for (let i = foodItems.length - 1; i >= 0; i--) {
Loops backward through food items to find the one currently being dragged
if (foodItems[i].isDragging) {
Checks if this food's isDragging flag is true (meaning the user was holding this food)
foodItems[i].stopDrag();
Calls the Food's stopDrag() method, which sets isDragging to false so it no longer follows the mouse
if (dog.collectFood(foodItems[i])) {
Calls the dog's collectFood() method, which checks if the food is close enough to the dog; returns true if collected, false otherwise
foodItems.splice(i, 1);
If the food was collected, removes it from the foodItems array at index i (splice removes 1 element starting at i)
foodItems.push(new Food());
Adds a new Food object (at a random position with random color) to the end of the foodItems array, replacing the collected one
break;
Breaks out of the loop so only one food item's release is processed per mouse release

keyPressed()

keyPressed() runs once per key press. This is where we detect the 'p' key and check if pooing is allowed (not already animating and power is full). The three-condition AND logic ensures the player can't spam poos or poo while an animation is running. For continuous key detection (like holding a key), use keyIsDown() instead, which is what dog.move() uses for arrow keys.

function keyPressed() {
  if (key === 'p' && !isPooing && dog.pooAmount >= dog.maxPooAmount) {
    dog.poo();
  }
}
Line-by-line explanation (3 lines)

🔧 Subcomponents:

conditional Poo Action Trigger if (key === 'p' && !isPooing && dog.pooAmount >= dog.maxPooAmount) {

Checks three conditions: the 'p' key was pressed, the dog is not already pooing, and the poo power meter is full; only if all three are true does the poo happen

function keyPressed() {
keyPressed() is a built-in p5.js function that runs once every time any key is pressed
if (key === 'p' && !isPooing && dog.pooAmount >= dog.maxPooAmount) {
Checks three conditions with AND logic (&&): the key pressed is 'p', isPooing is false (! means NOT), and the dog's poo power is at or above maxPooAmount
dog.poo();
Calls the dog's poo() method, which resets poo power to 0, starts the animation, stores the current position, and plays a bark sound

windowResized()

windowResized() ensures the sketch stays responsive when the window is resized. By default, if you don't define this function, the canvas stays at its original size. Here, we simply resize it to match the current window. (The sketch doesn't reposition the dog, but you could add 'dog.x = width / 2' here to center it again.)

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
}
Line-by-line explanation (2 lines)
function windowResized() {
windowResized() is a built-in p5.js function that runs automatically whenever the browser window is resized
resizeCanvas(windowWidth, windowHeight);
Resizes the canvas to match the new window dimensions, keeping the sketch responsive

Dog constructor()

The constructor() method initializes a new Dog object with starting values for all its properties. Every time you write 'new Dog()', this constructor runs. By storing values like this.x, this.y, this.size, etc., we keep all dog-related data grouped together, making the code organized and easy to manage.

class Dog {
  constructor() {
    this.x = width / 2;
    this.y = height / 2;
    this.size = 50;
    this.speed = 5;
    this.pooAmount = 0;
    this.maxPooAmount = 100;
  }
Line-by-line explanation (8 lines)
class Dog {
Defines a Dog class, which is a blueprint for creating dog objects with properties and methods
constructor() {
The constructor() method runs automatically when a new Dog is created (like 'new Dog()' in setup())
this.x = width / 2;
Sets the dog's starting x position to the center of the canvas
this.y = height / 2;
Sets the dog's starting y position to the center of the canvas
this.size = 50;
Sets the dog's visual size to 50 pixels—used for drawing body parts and collision detection
this.speed = 5;
Sets how many pixels the dog moves per frame when arrow keys are held
this.pooAmount = 0;
Initializes the poo power meter to 0 (empty)
this.maxPooAmount = 100;
Sets the maximum poo power to 100; the dog can only poo when pooAmount reaches this value

Dog.move()

move() is called every frame by draw(). It uses keyIsDown() (not keyPressed()) because we want continuous movement while a key is held, not just one movement per keypress. The four separate if statements allow the dog to move diagonally when two arrow keys are pressed simultaneously. The constrain() calls at the end keep the dog from leaving the screen—a common and essential practice in games.

🔬 These four lines handle the four arrow keys separately using keyIsDown(). What happens if you comment out (delete) the UP_ARROW and DOWN_ARROW lines? The dog won't move vertically anymore—try it!

    if (keyIsDown(LEFT_ARROW)) this.x -= this.speed;
    if (keyIsDown(RIGHT_ARROW)) this.x += this.speed;
    if (keyIsDown(UP_ARROW)) this.y -= this.speed;
    if (keyIsDown(DOWN_ARROW)) this.y += this.speed;
  move() {
    if (keyIsDown(LEFT_ARROW)) this.x -= this.speed;
    if (keyIsDown(RIGHT_ARROW)) this.x += this.speed;
    if (keyIsDown(UP_ARROW)) this.y -= this.speed;
    if (keyIsDown(DOWN_ARROW)) this.y += this.speed;

    this.x = constrain(this.x, this.size / 2, width - this.size / 2);
    this.y = constrain(this.y, this.size / 2, height - this.size / 2);
  }
Line-by-line explanation (7 lines)

🔧 Subcomponents:

conditional Arrow Key Movement if (keyIsDown(LEFT_ARROW)) this.x -= this.speed; if (keyIsDown(RIGHT_ARROW)) this.x += this.speed; if (keyIsDown(UP_ARROW)) this.y -= this.speed; if (keyIsDown(DOWN_ARROW)) this.y += this.speed;

Four separate if statements check which arrow keys are currently held and move the dog in that direction; four separate ifs allow diagonal movement when two keys are held at once

calculation Boundary Constraint this.x = constrain(this.x, this.size / 2, width - this.size / 2); this.y = constrain(this.y, this.size / 2, height - this.size / 2);

Uses constrain() to prevent the dog from moving beyond the canvas edges; the min/max values account for the dog's size so it doesn't half-disappear

move() {
Defines the move() method, which is called every frame from draw() to update the dog's position
if (keyIsDown(LEFT_ARROW)) this.x -= this.speed;
keyIsDown() checks if a key is currently held (not just pressed once); if LEFT_ARROW is held, subtract speed from x, moving the dog left
if (keyIsDown(RIGHT_ARROW)) this.x += this.speed;
If RIGHT_ARROW is held, add speed to x, moving the dog right
if (keyIsDown(UP_ARROW)) this.y -= this.speed;
If UP_ARROW is held, subtract speed from y, moving the dog up (remember: y=0 is the top)
if (keyIsDown(DOWN_ARROW)) this.y += this.speed;
If DOWN_ARROW is held, add speed to y, moving the dog down
this.x = constrain(this.x, this.size / 2, width - this.size / 2);
constrain() clamps x between a minimum (size/2) and maximum (width - size/2), preventing the dog from leaving the left or right edge
this.y = constrain(this.y, this.size / 2, height - this.size / 2);
constrain() clamps y between a minimum (size/2) and maximum (height - size/2), preventing the dog from leaving the top or bottom edge

Dog.draw()

Dog.draw() is called every frame from the main draw() function. It renders the dog's body, head, legs, and a wagging tail using basic shapes. The wagging tail demonstrates push/pop (saving/restoring state) and rotate() with a sin-based oscillation—a classic animation pattern. The power bar at the top-left shows the player's progress toward being able to poo, providing clear visual feedback.

  draw() {
    fill(139, 69, 19);
    ellipse(this.x, this.y, this.size, this.size * 0.8);
    ellipse(this.x + this.size / 4, this.y - this.size / 4, this.size / 3, this.size / 3);
    rect(this.x - this.size / 2, this.y + this.size / 4, this.size, this.size / 8);

    push();
    translate(this.x - this.size / 2, this.y);
    rotate(sin(frameCount * 0.1) * PI / 8);
    rect(0, 0, this.size / 3, this.size / 8);
    pop();

    fill(50);
    rect(10, 10, this.maxPooAmount * 2, 20);
    fill(72, 61, 139);
    rect(10, 10, this.pooAmount * 2, 20);
    fill(255);
    textAlign(LEFT, CENTER);
    textSize(14);
    text("Poo Power: " + floor(this.pooAmount) + "/" + this.maxPooAmount, 15, 20);
  }
Line-by-line explanation (18 lines)

🔧 Subcomponents:

calculation Dog Body Parts fill(139, 69, 19); ellipse(this.x, this.y, this.size, this.size * 0.8); ellipse(this.x + this.size / 4, this.y - this.size / 4, this.size / 3, this.size / 3); rect(this.x - this.size / 2, this.y + this.size / 4, this.size, this.size / 8);

Draws the dog's body (brown ellipse), head (smaller ellipse to the upper right), and legs (brown rectangle below)

calculation Wagging Tail push(); translate(this.x - this.size / 2, this.y); rotate(sin(frameCount * 0.1) * PI / 8); rect(0, 0, this.size / 3, this.size / 8); pop();

Uses push/pop to save the drawing state, translates to the dog's left side, rotates a tail rectangle back and forth using sin(frameCount) for oscillation, then restores the state

calculation Poo Power Bar fill(50); rect(10, 10, this.maxPooAmount * 2, 20); fill(72, 61, 139); rect(10, 10, this.pooAmount * 2, 20); fill(255); textAlign(LEFT, CENTER); textSize(14); text("Poo Power: " + floor(this.pooAmount) + "/" + this.maxPooAmount, 15, 20);

Draws a dark gray background bar, then a dark purple foreground bar proportional to pooAmount, and labels it with text

draw() {
The draw() method of the Dog class is called every frame from the main draw() function
fill(139, 69, 19);
Sets the fill color to brown (RGB 139, 69, 19) for the dog's body
ellipse(this.x, this.y, this.size, this.size * 0.8);
Draws the dog's body as a brown ellipse at (this.x, this.y) with width=size and height=size*0.8 (slightly squished vertically)
ellipse(this.x + this.size / 4, this.y - this.size / 4, this.size / 3, this.size / 3);
Draws the dog's head as a smaller circle to the upper-right of the body
rect(this.x - this.size / 2, this.y + this.size / 4, this.size, this.size / 8);
Draws the dog's legs as a wide, short brown rectangle below the body
push();
Saves the current drawing state (position, rotation, fill color, etc.) so we can restore it after drawing the tail
translate(this.x - this.size / 2, this.y);
Moves the coordinate system to the dog's left side, which becomes the rotation pivot point for the tail
rotate(sin(frameCount * 0.1) * PI / 8);
Rotates around the pivot point by an angle that oscillates: sin(frameCount * 0.1) produces a value between -1 and 1, multiplied by PI/8 (about 22 degrees), creating a wagging motion
rect(0, 0, this.size / 3, this.size / 8);
Draws a small brown rectangle (the tail) starting at the new origin (0,0), which is the dog's left side after translate()
pop();
Restores the drawing state to what it was before push(), so the rotation doesn't affect the power bar
fill(50);
Sets fill color to dark gray (RGB 50, 50, 50) for the power bar background
rect(10, 10, this.maxPooAmount * 2, 20);
Draws the power bar background: a dark gray rectangle at (10, 10) with width = maxPooAmount * 2 (200 pixels) and height 20
fill(72, 61, 139);
Sets fill color to dark purple (RGB 72, 61, 139) for the power bar foreground
rect(10, 10, this.pooAmount * 2, 20);
Draws the power bar foreground on top, with the same position and height but width = pooAmount * 2, so it grows as pooAmount increases
fill(255);
Sets fill color to white for the text label
textAlign(LEFT, CENTER);
Aligns text to the left horizontally and center vertically
textSize(14);
Sets the font size to 14 pixels
text("Poo Power: " + floor(this.pooAmount) + "/" + this.maxPooAmount, 15, 20);
Draws the label text at (15, 20) showing current poo power and max; floor() rounds down pooAmount to remove decimals

Dog.collectFood()

collectFood() is called from mouseReleased() when the player releases a dragged food item. It uses dist() to check if the food is within the dog's collision radius. If yes, it increases pooAmount (clamped to stay within bounds), plays a sound, and returns true so mouseReleased() knows to remove the food and spawn a new one. This is the core of the game loop: drag food to dog → dog eats → poo power increases.

  collectFood(foodItem) {
    let d = dist(this.x, this.y, foodItem.x, foodItem.y);
    if (d < this.size / 2 + foodItem.size / 2) {
      this.pooAmount = constrain(this.pooAmount + 25, 0, this.maxPooAmount);
      this.playEatSound();
      return true;
    }
    return false;
  }
Line-by-line explanation (7 lines)

🔧 Subcomponents:

conditional Distance-Based Collision let d = dist(this.x, this.y, foodItem.x, foodItem.y); if (d < this.size / 2 + foodItem.size / 2) {

Calculates the distance between the dog and food using dist(); if less than the sum of their radii, they're touching (collision detected)

calculation Poo Power Increase this.pooAmount = constrain(this.pooAmount + 25, 0, this.maxPooAmount);

Adds 25 to pooAmount but clamps it between 0 and maxPooAmount so it never goes negative or exceeds the max

collectFood(foodItem) {
collectFood() takes one parameter: a Food object. It returns true if the food is collected, false otherwise
let d = dist(this.x, this.y, foodItem.x, foodItem.y);
Calculates the distance between the dog and the food using dist() (distance formula), storing it in variable d
if (d < this.size / 2 + foodItem.size / 2) {
Checks if the distance is less than the sum of both objects' radii (size/2); if so, they're overlapping (collision)
this.pooAmount = constrain(this.pooAmount + 25, 0, this.maxPooAmount);
Adds 25 to pooAmount, then uses constrain() to clamp it between 0 and maxPooAmount so it stays in bounds
this.playEatSound();
Calls the playEatSound() method to play the eating sound effect
return true;
Returns true, signaling that the food was successfully collected
return false;
If the distance is too large (no collision), returns false without collecting the food

Dog.poo()

poo() is called from keyPressed() and manages the poo action. It checks if the dog has enough power, then sets up the global animation state (isPooing flag, pooTimer, pooX, pooY) so that the main draw() function can animate the poo each frame. By storing the position before the animation starts, the poo stays in one place rather than following the moving dog—a nice visual detail. The method returns true/false to indicate success, a common pattern for action methods.

  poo() {
    if (this.pooAmount >= this.maxPooAmount) {
      this.pooAmount = 0;
      isPooing = true;
      pooTimer = 120;
      pooX = this.x;
      pooY = this.y + this.size / 2;
      this.playDogBark();
      pooCounter++;
      return true;
    }
    return false;
  }
Line-by-line explanation (11 lines)

🔧 Subcomponents:

conditional Poo Power Validation if (this.pooAmount >= this.maxPooAmount) {

Checks if pooAmount is full; only if true does the poo happen

calculation Poo Animation State Setup this.pooAmount = 0; isPooing = true; pooTimer = 120; pooX = this.x; pooY = this.y + this.size / 2;

Resets pooAmount to 0, sets the isPooing flag to trigger the animation, initializes the animation timer, and stores the poo position (offset down from the dog's center)

poo() {
poo() is called from keyPressed() when the player presses 'P' and meets the conditions
if (this.pooAmount >= this.maxPooAmount) {
Checks if the dog has enough poo power (at or above maxPooAmount); if not, nothing happens and the function returns false
this.pooAmount = 0;
Resets pooAmount to 0 so the player must collect more food to poo again
isPooing = true;
Sets the global isPooing flag to true, which tells draw() to render the poo animation next frame
pooTimer = 120;
Sets the animation timer to 120 frames; draw() will decrement this each frame and end the animation when it reaches 0
pooX = this.x;
Stores the dog's current x position as the poo's x position (the poo appears at the dog's location)
pooY = this.y + this.size / 2;
Stores the dog's y position offset down by size/2, so the poo appears below the dog's center
this.playDogBark();
Calls playDogBark() to play a bark sound, giving audio feedback for the poo action
pooCounter++;
Increments the global pooCounter, tracking successful poos for the score display
return true;
Returns true, indicating the poo was successful
return false;
If pooAmount is not full, returns false and doesn't poo

Dog.playDogBark()

playDogBark() is a helper method that plays a realistic bark sound by layering an oscillator (pitch) with noise (texture). The randomized frequency makes barks sound more natural—identical barks would sound robotic. setTimeout() is a JavaScript timer that schedules the fade-out after a delay, allowing the bark to ring out before diminishing. This is more sophisticated than simply playing and stopping a pre-recorded sound.

  playDogBark() {
    dogBarkOsc.freq(random(180, 220), 0.05);
    dogBarkOsc.amp(0.3, 0.05);
    dogBarkNoise.amp(0.2, 0.05);
    setTimeout(() => {
      dogBarkOsc.amp(0, 0.5);
      dogBarkNoise.amp(0, 0.5);
    }, 150);
  }
Line-by-line explanation (8 lines)

🔧 Subcomponents:

calculation Bark Frequency Randomization dogBarkOsc.freq(random(180, 220), 0.05);

Randomizes the oscillator's frequency between 180–220 Hz with a 0.05-second fade-in, making the pitch vary slightly for a more natural sound

calculation Bark Amplitude Control with setTimeout setTimeout(() => { dogBarkOsc.amp(0, 0.5); dogBarkNoise.amp(0, 0.5); }, 150);

Uses setTimeout to schedule a callback 150 milliseconds later; the callback fades both the oscillator and noise amplitude to 0 over 0.5 seconds

playDogBark() {
playDogBark() is a helper method that synthesizes a quick bark sound for the poo action
dogBarkOsc.freq(random(180, 220), 0.05);
Changes the bark oscillator's frequency to a random value between 180–220 Hz, with the frequency change smoothed over 0.05 seconds (fade-in time for the frequency sweep)
dogBarkOsc.amp(0.3, 0.05);
Sets the oscillator's amplitude (volume) to 0.3 (30% of max) with a 0.05-second fade-in, making it audible smoothly
dogBarkNoise.amp(0.2, 0.05);
Sets the noise generator's amplitude to 0.2 (20% of max) with a 0.05-second fade-in, adding texture to the bark
setTimeout(() => {
setTimeout is a JavaScript function that schedules a callback function to run after a specified delay (in milliseconds)
dogBarkOsc.amp(0, 0.5);
Fades the oscillator's amplitude to 0 over 0.5 seconds, creating a natural tail-off
dogBarkNoise.amp(0, 0.5);
Fades the noise amplitude to 0 over 0.5 seconds
}, 150);
The callback runs 150 milliseconds (0.15 seconds) after playDogBark() is called, so the bark plays for roughly 150ms before fading out over the next 500ms

Dog.playEatSound()

playEatSound() creates a crunching sound using pink noise (which is grainier and lower-frequency than white noise, mimicking food sounds). The pan() method adds stereo variety—each crunch sound comes from a slightly different speaker position, making multiple eating sounds less repetitive. Both playEatSound() and playDogBark() follow the same pattern: fade in immediately, then use setTimeout to schedule a fade-out later.

  playEatSound() {
    eatSoundNoise.amp(0.4, 0.05);
    eatSoundNoise.pan(random(-0.5, 0.5));
    setTimeout(() => {
      eatSoundNoise.amp(0, 0.3);
    }, 100);
  }
Line-by-line explanation (6 lines)

🔧 Subcomponents:

calculation Stereo Panning eatSoundNoise.pan(random(-0.5, 0.5));

Randomizes the stereo pan position from left (-0.5) to right (0.5), adding a subtle spatial variety to eating sounds

playEatSound() {
playEatSound() is called from collectFood() when the dog eats food
eatSoundNoise.amp(0.4, 0.05);
Sets the pink noise amplitude to 0.4 (40% of max) with a 0.05-second fade-in
eatSoundNoise.pan(random(-0.5, 0.5));
Randomizes the stereo panning: -1 is hard left, 0 is center, 1 is hard right; here it ranges from -0.5 to 0.5, a subtle effect
setTimeout(() => {
Schedules a callback 100 milliseconds later
eatSoundNoise.amp(0, 0.3);
Fades the noise amplitude to 0 over 0.3 seconds, creating a natural crunch tail-off
}, 100);
The callback runs after 100ms, so the crunch sound plays for 100ms then fades out over 300ms

Food constructor()

The Food constructor creates a new food item at a random position with a random warm color. Each food object is independent, storing its own position, size, color, and drag state. By initializing offsetX and offsetY to 0, we're ready to calculate them when dragging starts; this prevents the food from jumping to the cursor.

class Food {
  constructor() {
    this.x = random(50, width - 50);
    this.y = random(50, height - 50);
    this.size = 20;
    this.color = color(random(200, 255), random(100, 150), random(50));
    this.isDragging = false;
    this.offsetX = 0;
    this.offsetY = 0;
  }
Line-by-line explanation (9 lines)
class Food {
Defines the Food class, a blueprint for food items
constructor() {
constructor() runs when a new Food is created (e.g., 'new Food()' in setup() or mouseReleased())
this.x = random(50, width - 50);
Sets the food's x position to a random value between 50 and width-50, keeping it away from the screen edges
this.y = random(50, height - 50);
Sets the food's y position to a random value between 50 and height-50
this.size = 20;
Sets the food's visual size to 20 pixels (diameter)
this.color = color(random(200, 255), random(100, 150), random(50));
Creates a random color for the food: red 200–255, green 100–150, blue 50 (always low), resulting in warm, varied colors
this.isDragging = false;
Initializes the drag flag to false; becomes true when the player clicks and drags this food
this.offsetX = 0;
Initializes the offset from the mouse; calculated when dragging starts to prevent the food from snapping to the cursor
this.offsetY = 0;
Initializes the vertical offset from the mouse

Food.draw()

Food.draw() renders each food item with a shadow effect for depth. The shadow (a semi-transparent black ellipse offset slightly) is drawn first, then the colorful food circle is drawn on top with a black outline. Finally, the word "Food" is drawn in the center. This layering technique is common in 2D graphics: draw background elements first, then foreground elements on top.

  draw() {
    fill(this.color);
    noStroke();
    fill(0, 0, 0, 50);
    ellipse(this.x + 2, this.y + 2, this.size, this.size);
    
    fill(this.color);
    stroke(0);
    strokeWeight(1);
    ellipse(this.x, this.y, this.size, this.size);
    fill(0);
    noStroke();
    textSize(10);
    textAlign(CENTER, CENTER);
    text("Food", this.x, this.y);
  }
Line-by-line explanation (14 lines)

🔧 Subcomponents:

calculation Shadow Effect fill(0, 0, 0, 50); ellipse(this.x + 2, this.y + 2, this.size, this.size);

Draws a dark, semi-transparent ellipse offset down and to the right, creating a shadow that makes the food appear to float

calculation Food Ellipse fill(this.color); stroke(0); strokeWeight(1); ellipse(this.x, this.y, this.size, this.size);

Draws the main food circle with its random color and a thin black outline

draw() {
Food.draw() is called from the main draw() loop for each food item
fill(this.color);
Sets the fill color to this food's unique color
noStroke();
Disables stroke (outline) for the shadow
fill(0, 0, 0, 50);
Sets the fill to semi-transparent black (alpha 50) for the shadow
ellipse(this.x + 2, this.y + 2, this.size, this.size);
Draws a shadow ellipse offset 2 pixels right and down from the food's position
fill(this.color);
Resets the fill color to the food's original color
stroke(0);
Enables a black stroke (outline) with the default color
strokeWeight(1);
Sets the stroke thickness to 1 pixel
ellipse(this.x, this.y, this.size, this.size);
Draws the main food circle with the random color and black outline
fill(0);
Sets the fill color to black for the text
noStroke();
Disables stroke for the text
textSize(10);
Sets the text size to 10 pixels
textAlign(CENTER, CENTER);
Centers the text horizontally and vertically
text("Food", this.x, this.y);
Draws the label "Food" at the center of this food item

Food.isMouseOver()

isMouseOver() is a simple boolean check using distance-based collision. It returns true if the mouse is within the food's circular bounds, false otherwise. This is called from mousePressed() to detect when the player clicks on a food item.

  isMouseOver() {
    return dist(mouseX, mouseY, this.x, this.y) < this.size / 2;
  }
Line-by-line explanation (2 lines)
isMouseOver() {
isMouseOver() checks if the mouse cursor is over this food item
return dist(mouseX, mouseY, this.x, this.y) < this.size / 2;
Uses dist() to calculate the distance from the mouse to the food's center; returns true if the distance is less than the food's radius (size/2)

Food.startDrag()

startDrag() initializes the drag state by setting the flag to true and calculating the offset. The offset is crucial: if the player clicks on the edge of the food, the offset will be large; if they click the center, the offset will be small. This keeps the food from jumping to the cursor during drag.

  startDrag() {
    this.isDragging = true;
    this.offsetX = this.x - mouseX;
    this.offsetY = this.y - mouseY;
  }
Line-by-line explanation (4 lines)
startDrag() {
startDrag() is called from mousePressed() when the player clicks on this food
this.isDragging = true;
Sets the isDragging flag to true, signaling that this food is being dragged
this.offsetX = this.x - mouseX;
Calculates and stores the horizontal offset between the food's position and the cursor; this prevents the food from snapping to the cursor when dragging starts
this.offsetY = this.y - mouseY;
Calculates and stores the vertical offset

Food.updateDrag()

updateDrag() runs every frame and moves the dragged food to follow the mouse. The offset ensures the food maintains its grab point (the point where the player clicked) rather than jumping to the cursor. The constrain() calls prevent the food from being dragged off-screen, keeping it always visible.

  updateDrag() {
    if (this.isDragging) {
      this.x = mouseX + this.offsetX;
      this.y = mouseY + this.offsetY;
      this.x = constrain(this.x, this.size / 2, width - this.size / 2);
      this.y = constrain(this.y, this.size / 2, height - this.size / 2);
    }
  }
Line-by-line explanation (6 lines)

🔧 Subcomponents:

conditional Drag Position Update if (this.isDragging) { this.x = mouseX + this.offsetX; this.y = mouseY + this.offsetY;

If dragging, updates the food's position to follow the mouse, adding the stored offset so the food maintains its grab point

conditional Drag Boundary Constraint this.x = constrain(this.x, this.size / 2, width - this.size / 2); this.y = constrain(this.y, this.size / 2, height - this.size / 2);

Constrains the food's position to stay within canvas boundaries while dragging, preventing it from leaving the screen

updateDrag() {
updateDrag() is called from the main draw() loop for each food item every frame
if (this.isDragging) {
Only updates position if this food is currently being dragged
this.x = mouseX + this.offsetX;
Sets the food's x position to the current mouse x plus the stored offset, making it follow the mouse while maintaining the grab point
this.y = mouseY + this.offsetY;
Sets the food's y position to the current mouse y plus the stored offset
this.x = constrain(this.x, this.size / 2, width - this.size / 2);
Clamps the food's x position to stay within the canvas (accounting for its size)
this.y = constrain(this.y, this.size / 2, height - this.size / 2);
Clamps the food's y position to stay within the canvas

Food.stopDrag()

stopDrag() is the simplest method: just set the isDragging flag to false. After this, updateDrag() won't move the food anymore (it checks isDragging before updating position). Simple but essential.

  stopDrag() {
    this.isDragging = false;
  }
Line-by-line explanation (2 lines)
stopDrag() {
stopDrag() is called from mouseReleased() when the player releases the food
this.isDragging = false;
Sets isDragging to false, stopping the food from following the mouse

📦 Key Variables

dog object

Stores the Dog instance; contains all dog properties (position, size, poo power) and methods (move, draw, poo, etc.)

let dog;
foodItems array

An array of Food objects currently on the canvas; looped through each frame to draw and update each food item

let foodItems = [];
isPooing boolean

Flag that tracks whether the dog is currently in a poo animation; controls whether the poo blob is drawn and the sound plays

let isPooing = false;
pooTimer number

Countdown timer (in frames) for the poo animation; decremented each frame, animation ends when it reaches 0

let pooTimer = 0;
pooCounter number

Tracks the total number of successful poos; displayed at the top of the screen

let pooCounter = 0;
pooX number

Stores the x position where the poo blob is drawn; fixed at the moment poo() is called so the blob doesn't follow the moving dog

let pooX;
pooY number

Stores the y position where the poo blob is drawn

let pooY;
dogBarkOsc object

A p5.sound Oscillator that generates the bark pitch; started in preload() and controlled by playDogBark()

let dogBarkOsc;
dogBarkNoise object

A p5.sound Noise generator (white noise) that adds texture to the bark sound

let dogBarkNoise;
eatSoundNoise object

A p5.sound Noise generator (pink noise) that creates the crunchy eating sound

let eatSoundNoise;
pooSoundOsc object

A p5.sound Oscillator that generates the poo 'plop' sound; a low-frequency sine wave

let pooSoundOsc;

🔧 Potential Improvements (5)

Here are some ways this code could be enhanced:

STYLE global audio initialization

Sound objects are created in preload() but many are never reassigned once initialized; consider storing them as class properties or using an audio manager

💡 Create a Sound class or manager object to encapsulate all audio generators, making the code more modular and easier to extend with new sounds

FEATURE mouseReleased() and collectFood()

Food collection is based on distance but doesn't account for the dog's mouth or specific body part; feels abstract

💡 Track the dog's head position separately (e.g., headX, headY) and check distance to the head for more intuitive collision; or add visual feedback (like a munching animation) when food is collected

BUG Dog.move()

The boundary constraints keep the dog's center within bounds but don't account for the dog's visual size fully when it's stretched (e.g., by the head and tail)

💡 Increase the buffer in constrain() to use a larger radius, e.g., constrain(this.x, this.size, width - this.size) to account for the head extending further

PERFORMANCE draw() loop and food iteration

The food loop uses 'foodItems.length - 1' backward iteration every frame; while safe, it's slightly less efficient than a forward loop with careful splice handling

💡 For a small array like foodItems (usually <10 items), this is negligible, but consider using filter() or a more functional approach if the array grows

STYLE keyPressed()

Checking 'key === "p"' is case-sensitive, so pressing 'P' (shift + p) won't work; players might expect either to work

💡 Change to 'key.toLowerCase() === "p"' so both 'p' and 'P' trigger poo

🔄 Code Flow

Code flow showing preload, setup, draw, mousepressed, mousereleased, keypressed, windowresized, dog_constructor, dog_move, dog_draw, dog_collectfood, dog_poo, dog_playdogbark, dog_playeatsound, food_constructor, food_draw, food_ismouseover, food_startdrag, food_updatedrag, food_stopdrag

💡 Click on function names in the diagram to jump to their code

graph TD start[Start] --> setup[setup] setup --> preload[preload] preload --> canvas-creation[canvas-creation] preload --> bark-osc-init[bark-osc-init] preload --> bark-noise-init[bark-noise-init] preload --> eat-sound-init[eat-sound-init] preload --> poo-sound-init[poo-sound-init] setup --> dog-init[dog-init] setup --> food-spawn-loop[food-spawn-loop] setup --> windowresized[windowresized] click setup href "#fn-setup" click preload href "#fn-preload" click canvas-creation href "#sub-canvas-creation" click bark-osc-init href "#sub-bark-osc-init" click bark-noise-init href "#sub-bark-noise-init" click eat-sound-init href "#sub-eat-sound-init" click poo-sound-init href "#sub-poo-sound-init" click dog-init href "#sub-dog-init" click food-spawn-loop href "#sub-food-spawn-loop" click windowresized href "#fn-windowresized" setup --> draw[draw loop] draw --> background-clear[background-clear] draw --> dog-update[dog-update] draw --> food-loop[food-loop] draw --> poo-animation[poo-animation] draw --> poo-readiness-prompt[poo-readiness-prompt] click draw href "#fn-draw" click background-clear href "#sub-background-clear" click dog-update href "#sub-dog-update" click food-loop href "#sub-food-loop" click poo-animation href "#sub-poo-animation" click poo-readiness-prompt href "#sub-poo-readiness-prompt" food-loop --> food-click-detection[food-click-detection] food-click-detection --> dragging-food-detection[dragging-food-detection] food-click-detection --> food-collection-check[food-collection-check] click food-click-detection href "#sub-food-click-detection" click dragging-food-detection href "#sub-dragging-food-detection" click food-collection-check href "#sub-food-collection-check" dragging-food-detection --> food-stopdrag[food-stopdrag] food-collection-check --> distance-collision[distance-collision] distance-collision --> poo-increase[poo-increase] distance-collision --> food-draw[food-draw] click food-stopdrag href "#sub-food-stopdrag" click distance-collision href "#sub-distance-collision" click poo-increase href "#sub-poo-increase" click food-draw href "#fn-food_draw" draw --> keypressed[keypressed] keypressed --> poo-trigger-conditional[poo-trigger-conditional] poo-trigger-conditional --> dog_poo[dog_poo] click keypressed href "#fn-keypressed" click poo-trigger-conditional href "#sub-poo-trigger-conditional" click dog_poo href "#fn-dog_poo" dog_poo --> poo-state-setup[poo-state-setup] poo-state-setup --> poo-animation click poo-state-setup href "#sub-poo-state-setup" draw --> mousepressed[mousepressed] mousepressed --> bark-on-click[bark-on-click] click mousepressed href "#fn-mousepressed" click bark-on-click href "#sub-bark-on-click" draw --> mousereleased[mousereleased] mousereleased --> food-collection-check click mousereleased href "#fn-mousereleased" draw --> windowresized[windowresized] click windowresized href "#fn-windowresized" dog-update --> dog_move[dog_move] dog_move --> arrow-key-checks[arrow-key-checks] arrow-key-checks --> boundary-constraint[boundary-constraint] boundary-constraint --> dog_draw[dog_draw] dog_draw --> dog-body-parts[dog-body-parts] dog_draw --> wagging-tail[wagging-tail] dog_draw --> poo-power-bar[poo-power-bar] click dog_move href "#fn-dog_move" click arrow-key-checks href "#sub-arrow-key-checks" click boundary-constraint href "#sub-boundary-constraint" click dog_draw href "#fn-dog_draw" click dog-body-parts href "#sub-dog-body-parts" click wagging-tail href "#sub-wagging-tail" click poo-power-bar href "#sub-poo-power-bar" food-draw --> shadow-ellipse[shadow-ellipse] food-draw --> food-ellipse[food-ellipse] click shadow-ellipse href "#sub-shadow-ellipse" click food-ellipse href "#sub-food-ellipse" food-collection-check --> poo-validation[poo-validation] poo-validation --> poo-increase click poo-validation href "#sub-poo-validation"

❓ Frequently Asked Questions

What visual elements are present in the p5.js sketch?

The sketch features a playful dog character and various food items scattered across the canvas, creating an engaging and dynamic environment.

How can users interact with the sketch during runtime?

Users can interact by controlling the dog to eat food items, which triggers sound effects and animations, enhancing the immersive experience.

What creative coding techniques does this sketch utilize?

This sketch demonstrates sound synthesis using p5.js, incorporating oscillators and noise to create unique auditory feedback for the dog's actions.

Preview

Sketch 2026-04-16 13:24 - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of Sketch 2026-04-16 13:24 - Code flow showing preload, setup, draw, mousepressed, mousereleased, keypressed, windowresized, dog_constructor, dog_move, dog_draw, dog_collectfood, dog_poo, dog_playdogbark, dog_playeatsound, food_constructor, food_draw, food_ismouseover, food_startdrag, food_updatedrag, food_stopdrag
Code Flow Diagram