hehehe

This sketch creates a fast-paced bug-squashing game where cockroaches scurry across the screen and players race against a 30-second timer to click and splat as many bugs as possible. The game combines interactive collision detection, real-time scoring, and increasing difficulty as spawn rates accelerate.

🧪 Try This!

Experiment with the code by making these changes:

  1. Change the game duration to 60 seconds — Players will have twice as long to squash bugs, making the game easier and less frantic
  2. Make bugs purple instead of brown — Changes the bug color to purple, making them pop more against the green background
  3. Award 10 points per squash instead of 1 — Each bug squashed instantly boosts the score more dramatically, making the score feel more rewarding
  4. Make the background dark blue instead of green — Shifts the mood of the game with a cooler color palette and makes brown bugs stand out differently
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch is a complete, playable game where roaches appear randomly on screen and you click to squash them before time runs out. The visuals are charming: each bug is drawn with a body, head, legs, and antennae, and when squashed it explodes into a messy splat made of overlapping ellipses. The code uses p5.js classes to organize bugs and splats as objects, the draw loop to animate and spawn enemies, mouse event handlers to detect clicks, and the millis() timer function to track elapsed game time and difficulty scaling.

The code is split into three main pieces: a Bug class that stores position and draws itself, a Splat class that creates explosion effects, and a main game loop in draw() that spawns bugs, detects clicks, tracks score, and manages game state. By reading this sketch you will learn how to structure a game with classes, how to detect mouse clicks on moving objects, how to use timestamps for timing in games, and how to scale difficulty by changing spawn rates over time.

⚙️ How It Works

  1. When setup() runs, it creates a full-window canvas, records the current time in gameStartTime, and sets up text display for the UI.
  2. Every frame, draw() clears the background with a greenish color, checks if enough time has passed to spawn a new bug, and displays all splats and living bugs.
  3. Bugs are spawned at random positions using spawnBug(), which creates a Bug object with a random size and adds it to the bugs array.
  4. When you click the mouse, mousePressed() checks each bug to see if your cursor is inside its bounding box using isClicked().
  5. If a bug is clicked while alive, it calls squash() to mark it as squashed, increments the score, and creates a new Splat at that position.
  6. The game tracks elapsed time using millis() and displays remaining seconds in the UI; when time runs out, gameOver is set to true and a restart prompt appears.

🎓 Concepts You'll Learn

ES6 ClassesObject-oriented programmingCollision detectionMouse events and interactionGame state managementTimer and countdown mechanicsArray manipulationParticle effects

📝 Code Breakdown

setup()

setup() runs exactly once when the sketch starts. It is the best place to initialize the canvas, set initial variable values, and configure text/color/stroke properties that stay constant throughout the game.

function setup() {
  createCanvas(windowWidth, windowHeight); // Create a full-window canvas
  gameStartTime = millis(); // Record the game start time
  lastBugSpawnTime = millis(); // Record the last bug spawn time
  textSize(24); // Set text size for UI
  textAlign(LEFT, TOP); // Align text to the top-left
}
Line-by-line explanation (5 lines)
createCanvas(windowWidth, windowHeight);
Creates a canvas that fills the entire browser window; windowWidth and windowHeight are built-in p5.js variables
gameStartTime = millis();
Stores the current time in milliseconds since the sketch started; used later to calculate elapsed time
lastBugSpawnTime = millis();
Records when the last bug was spawned; used in draw() to check if enough time has passed for the next spawn
textSize(24);
Sets the font size for all text drawn after this line to 24 pixels
textAlign(LEFT, TOP);
Anchors text so that the x,y coordinates you provide are the top-left corner of the text, not the center

draw()

draw() is the game's heartbeat—it runs 60 times per second and handles everything: spawning, rendering, timers, and game state. Every interactive p5.js sketch revolves around what happens inside draw().

🔬 This block controls how often bugs appear. What happens if you change bugSpawnRate - 50 to bugSpawnRate - 200? Will bugs spawn faster or slower?

  // Spawn bugs if the game is not over and enough time has passed
  if (!gameOver && millis() - lastBugSpawnTime > bugSpawnRate) {
    spawnBug(); // Call function to create a new bug
    lastBugSpawnTime = millis(); // Update last spawn time
    // Gradually decrease spawn rate to make the game harder (minimum 200ms)
    bugSpawnRate = max(200, bugSpawnRate - 50);

🔬 These two loops draw every splat first, then every bug. What happens visually if you swap them so bugs are drawn before splats?

  for (let splat of splats) {
    splat.display();
  }

  // Draw all alive bugs
  for (let bug of bugs) {
    bug.display();
  }
function draw() {
  background(100, 150, 100); // Dirty green/brown background

  // Spawn bugs if the game is not over and enough time has passed
  if (!gameOver && millis() - lastBugSpawnTime > bugSpawnRate) {
    spawnBug(); // Call function to create a new bug
    lastBugSpawnTime = millis(); // Update last spawn time
    // Gradually decrease spawn rate to make the game harder (minimum 200ms)
    bugSpawnRate = max(200, bugSpawnRate - 50); 
  }

  // Draw all existing splats
  for (let splat of splats) {
    splat.display();
  }

  // Draw all alive bugs
  for (let bug of bugs) {
    bug.display();
  }

  // --- Game UI and Timer ---
  let elapsedTime = millis() - gameStartTime; // Time elapsed since game start
  let remainingTime = max(0, gameDuration - elapsedTime); // Time left in the game

  fill(255); // White text color
  noStroke(); // No stroke for text
  text(`Score: ${score}`, 10, 10); // Display current score
  text(`Time Left: ${nf(remainingTime / 1000, 1, 1)}s`, 10, 40); // Display remaining time

  // Check if game duration has passed
  if (elapsedTime >= gameDuration) {
    gameOver = true; // Set game over flag
  }

  // --- Game Over Screen ---
  if (gameOver) {
    fill(255, 0, 0); // Red text color for game over
    textAlign(CENTER, CENTER); // Center text on the screen
    text(`GAME OVER! Final Score: ${score}`, width / 2, height / 2); // Display final score
    text(`Click to Restart`, width / 2, height / 2 + 40); // Instruction to restart
  }
}
Line-by-line explanation (16 lines)

🔧 Subcomponents:

conditional Bug Spawn Check if (!gameOver && millis() - lastBugSpawnTime > bugSpawnRate) {

Tests whether enough time has passed since the last bug spawn to create a new bug

calculation Difficulty Acceleration bugSpawnRate = max(200, bugSpawnRate - 50);

Decreases the time between spawns by 50ms each spawn, making the game progressively harder

for-loop Draw All Splats for (let splat of splats) { splat.display(); }

Loops through the splats array and calls display() on each one to render all squashed bug marks

for-loop Draw All Bugs for (let bug of bugs) { bug.display(); }

Loops through the bugs array and calls display() on each bug to draw all living roaches

calculation Time Remaining let remainingTime = max(0, gameDuration - elapsedTime);

Subtracts elapsed time from total game duration; max() prevents negative numbers

conditional Game Over Detection if (elapsedTime >= gameDuration) { gameOver = true; }

Sets the gameOver flag to true when the timer reaches zero

background(100, 150, 100);
Fills the entire canvas with a greenish color (RGB: 100, 150, 100); this erases the previous frame so bugs don't leave trails
if (!gameOver && millis() - lastBugSpawnTime > bugSpawnRate) {
Checks two conditions: the game must not be over (!gameOver) AND enough time must have passed since the last spawn
spawnBug();
Calls the spawnBug() function to create a new Bug object and add it to the bugs array
lastBugSpawnTime = millis();
Records the current time so the next spawn check knows when the last bug was created
bugSpawnRate = max(200, bugSpawnRate - 50);
Subtracts 50 milliseconds from the spawn rate, but never lets it drop below 200ms; this speeds up bug spawning each frame
for (let splat of splats) { splat.display(); }
Loops through every Splat object in the splats array and calls its display() method to draw it on screen
for (let bug of bugs) { bug.display(); }
Loops through every Bug object in the bugs array and calls its display() method to draw it on screen
let elapsedTime = millis() - gameStartTime;
Calculates how many milliseconds have passed since the game started by subtracting the start time from the current time
let remainingTime = max(0, gameDuration - elapsedTime);
Calculates time left by subtracting elapsed time from the total game duration; max() ensures it never goes below zero
fill(255);
Sets the fill color to white (RGB: 255, 255, 255) for all text drawn after this line
text(`Score: ${score}`, 10, 10);
Draws the current score in the top-left corner; the backticks allow the ${score} variable to be inserted into the string
text(`Time Left: ${nf(remainingTime / 1000, 1, 1)}s`, 10, 40);
Displays remaining time converted to seconds (divide by 1000); nf() formats it to show 1 digit before and 1 digit after the decimal
if (elapsedTime >= gameDuration) { gameOver = true; }
When elapsed time exceeds the game duration (30 seconds by default), the gameOver flag is set to true
if (gameOver) {
If the game is over, the next lines display the game over screen with the final score and restart instruction
fill(255, 0, 0);
Changes the text color to pure red (RGB: 255, 0, 0) for the game over message
textAlign(CENTER, CENTER);
Shifts text alignment so that coordinates mark the center of text instead of the top-left

spawnBug()

spawnBug() is called repeatedly by draw() to generate bugs at random locations. It demonstrates array manipulation (push) and object instantiation (new Bug).

function spawnBug() {
  let x = random(width * 0.1, width * 0.9); // Random X position (avoid edges)
  let y = random(height * 0.1, height * 0.9); // Random Y position (avoid edges)
  bugs.push(new Bug(x, y)); // Add the new bug to the bugs array
}
Line-by-line explanation (3 lines)
let x = random(width * 0.1, width * 0.9);
Picks a random X position between 10% and 90% of the canvas width, avoiding the left and right edges
let y = random(height * 0.1, height * 0.9);
Picks a random Y position between 10% and 90% of the canvas height, avoiding the top and bottom edges
bugs.push(new Bug(x, y));
Creates a new Bug object at the random position and adds it to the bugs array using push()

mousePressed()

mousePressed() is called every time the mouse is clicked. It handles both game restart logic and bug squashing by checking game state first, then iterating through bugs to detect collisions.

🔬 The loop ends with 'break' which stops after squashing one bug. What happens if you remove the break statement? Can you squash multiple bugs with one click?

  // Check if any bug was clicked
  // Iterate through bugs array in reverse for easier state change/removal (if needed)
  for (let i = bugs.length - 1; i >= 0; i--) {
    let bug = bugs[i];
    // If the bug is alive and the mouse clicked on it
    if (bug.state === 'alive' && bug.isClicked(mouseX, mouseY)) {
      bug.squash(); // Squash the bug
      score++; // Increment score
      splats.push(new Splat(bug.x, bug.y)); // Create a splat at the bug's position
      break; // Only squash one bug per click
function mousePressed() {
  // If the game is over, restart it on click
  if (gameOver) {
    bugs = []; // Clear all bugs
    splats = []; // Clear all splats
    score = 0; // Reset score
    gameStartTime = millis(); // Reset game timer
    lastBugSpawnTime = millis(); // Reset bug spawn timer
    bugSpawnRate = 1000; // Reset bug spawn rate
    gameOver = false; // Set game over flag to false
    textAlign(LEFT, TOP); // Reset text alignment for UI
    return; // Exit the function to prevent squashing bugs immediately
  }

  // Check if any bug was clicked
  // Iterate through bugs array in reverse for easier state change/removal (if needed)
  for (let i = bugs.length - 1; i >= 0; i--) {
    let bug = bugs[i];
    // If the bug is alive and the mouse clicked on it
    if (bug.state === 'alive' && bug.isClicked(mouseX, mouseY)) {
      bug.squash(); // Squash the bug
      score++; // Increment score
      splats.push(new Splat(bug.x, bug.y)); // Create a splat at the bug's position
      break; // Only squash one bug per click
    }
  }
}
Line-by-line explanation (16 lines)

🔧 Subcomponents:

conditional Game Restart Handler if (gameOver) {

When the game is over, clicking resets all game state and arrays to start a fresh game

for-loop Bug Click Detection for (let i = bugs.length - 1; i >= 0; i--) {

Iterates backward through the bugs array to check each bug for a collision with the mouse click

conditional Squash Condition if (bug.state === 'alive' && bug.isClicked(mouseX, mouseY)) {

Checks if the bug is alive AND the click is inside the bug's bounding box

if (gameOver) {
Checks if the game has ended; if so, the following code restarts the game instead of squashing bugs
bugs = [];
Clears the bugs array completely by assigning it to an empty array, removing all bugs from the screen
splats = [];
Clears the splats array to remove all blood/guts marks from the previous game
score = 0;
Resets the score to zero
gameStartTime = millis();
Records the current time as the new game start time so the timer begins fresh
lastBugSpawnTime = millis();
Resets the bug spawn timer so bugs start spawning from the beginning
bugSpawnRate = 1000;
Resets spawn rate to 1 second (1000 milliseconds), making bugs appear at the original slower rate
gameOver = false;
Sets the gameOver flag back to false so the game loop resumes spawning bugs and accepting clicks
textAlign(LEFT, TOP);
Resets text alignment back to top-left for the score/timer UI (it was changed to CENTER during the game over screen)
return;
Exits the mousePressed() function immediately so the code below (bug squashing) doesn't run during a restart
for (let i = bugs.length - 1; i >= 0; i--) {
Loops backward through the bugs array from the last bug to the first; this is safer when modifying arrays during iteration
if (bug.state === 'alive' && bug.isClicked(mouseX, mouseY)) {
Checks two conditions: the bug must be alive AND the click coordinates must fall within the bug's bounding box
bug.squash();
Calls the squash() method on the Bug object, which changes its state from 'alive' to 'squashed' so it no longer displays
score++;
Increments the score variable by 1 point for each successful squash
splats.push(new Splat(bug.x, bug.y));
Creates a new Splat object at the bug's position and adds it to the splats array to show a messy mark
break;
Exits the for-loop immediately after squashing one bug, preventing multiple bugs from being squashed in a single click

windowResized()

windowResized() is a p5.js event function that runs automatically whenever the browser window is resized. Without it, the canvas would stay the original size and become misaligned.

function windowResized() {
  resizeCanvas(windowWidth, windowHeight); // Resize the canvas to match the new window size
}
Line-by-line explanation (1 lines)
resizeCanvas(windowWidth, windowHeight);
Resizes the p5.js canvas to match the current browser window dimensions when the window is resized

Bug (class)

The Bug class encapsulates all bug behavior: the constructor initializes properties, display() draws it, squash() changes state, and isClicked() handles collision detection. Using classes keeps related data and methods organized together.

🔬 These three lines draw the left legs. What happens if you change strokeWeight(2) to strokeWeight(5)? Will the legs get thicker or thinner?

      // Legs (simple lines)
      stroke(this.color);
      strokeWeight(2);
      line(this.x - this.size * 0.6, this.y - this.size * 0.3, this.x - this.size * 0.9, this.y - this.size * 0.5);
      line(this.x - this.size * 0.5, this.y, this.x - this.size * 0.8, this.y);
      line(this.x - this.size * 0.6, this.y + this.size * 0.3, this.x - this.size * 0.9, this.y + this.size * 0.5);
class Bug {
  constructor(x, y, size) {
    this.x = x;
    this.y = y;
    this.size = size || random(20, 50); // Random size for variety
    this.state = 'alive'; // 'alive' or 'squashed'
    this.color = color(50, 30, 0); // Dark brown for the bug
  }

  // Draw the bug on the canvas
  display() {
    if (this.state === 'alive') {
      noStroke();
      fill(this.color);

      // Body (large oval)
      ellipse(this.x, this.y, this.size * 1.5, this.size);

      // Head (smaller oval)
      ellipse(this.x + this.size * 0.7, this.y, this.size * 0.4, this.size * 0.3);

      // Legs (simple lines)
      stroke(this.color);
      strokeWeight(2);
      line(this.x - this.size * 0.6, this.y - this.size * 0.3, this.x - this.size * 0.9, this.y - this.size * 0.5);
      line(this.x - this.size * 0.5, this.y, this.x - this.size * 0.8, this.y);
      line(this.x - this.size * 0.6, this.y + this.size * 0.3, this.x - this.size * 0.9, this.y + this.size * 0.5);

      line(this.x + this.size * 0.6, this.y - this.size * 0.3, this.x + this.size * 0.9, this.y - this.size * 0.5);
      line(this.x + this.size * 0.5, this.y, this.x + this.size * 0.8, this.y);
      line(this.x + this.size * 0.6, this.y + this.size * 0.3, this.x + this.size * 0.9, this.y + this.size * 0.5);

      // Antennae
      line(this.x + this.size * 0.7, this.y - this.size * 0.1, this.x + this.size * 1.0, this.y - this.size * 0.3);
      line(this.x + this.size * 0.7, this.y + this.size * 0.1, this.x + this.size * 1.0, this.y + this.size * 0.3);
    }
  }

  // Change the bug's state to squashed
  squash() {
    this.state = 'squashed';
  }

  // Check if the mouse click is within the bug's area
  isClicked(mx, my) {
    // Approximating the bug's bounding box with a rectangle for click detection
    return mx > this.x - this.size * 0.75 &&
           mx < this.x + this.size * 0.75 &&
           my > this.y - this.size * 0.5 &&
           my < this.y + this.size * 0.5;
  }
}
Line-by-line explanation (15 lines)

🔧 Subcomponents:

calculation Constructor (Initialization) constructor(x, y, size) {

Initializes each Bug with position, size, state, and color when a new Bug is created

conditional Display Alive Check if (this.state === 'alive') {

Only draws the bug if it hasn't been squashed; squashed bugs don't render

calculation Bounding Box Collision return mx > this.x - this.size * 0.75 &&

Tests if the mouse click falls within a rectangular area around the bug

constructor(x, y, size) {
The constructor is a special method that runs when a new Bug is created with 'new Bug(x, y)'; it sets up initial properties
this.x = x;
Stores the x position passed into the constructor as a property of this Bug object
this.size = size || random(20, 50);
Uses the size parameter if provided, otherwise picks a random size between 20 and 50; the || operator provides a default
this.state = 'alive';
Sets the bug's state to 'alive' initially; this changes to 'squashed' when the bug is clicked
if (this.state === 'alive') {
Only draws the bug if its state is 'alive'; once squashed, this entire block is skipped
fill(this.color);
Sets the fill color for all shapes drawn after this line to the bug's color (dark brown)
ellipse(this.x, this.y, this.size * 1.5, this.size);
Draws the bug's body as an ellipse 1.5 times the bug's size wide and 1 time the size tall, centered at (x, y)
ellipse(this.x + this.size * 0.7, this.y, this.size * 0.4, this.size * 0.3);
Draws the bug's head offset 0.7 times the size to the right; it is smaller and rounder than the body
stroke(this.color);
Switches from fill mode to stroke mode, so the next lines draw outlines instead of filled shapes
strokeWeight(2);
Sets the thickness of the lines used to draw legs and antennae to 2 pixels
line(this.x - this.size * 0.6, this.y - this.size * 0.3, this.x - this.size * 0.9, this.y - this.size * 0.5);
Draws a leg on the left side of the body, starting at the body and extending outward and upward
return mx > this.x - this.size * 0.75 &&
Checks if the mouse x is to the right of the bug's left edge; this is the first condition in a compound check
mx < this.x + this.size * 0.75 &&
Checks if the mouse x is to the left of the bug's right edge
my > this.y - this.size * 0.5 &&
Checks if the mouse y is below the bug's top edge
my < this.y + this.size * 0.5;
Checks if the mouse y is above the bug's bottom edge; if all four conditions are true, the click is inside the bug

Splat (class)

The Splat class creates a particle effect when a bug is squashed. The constructor stores state, and display() uses two loops and randomization to generate a unique, organic-looking mess every time it renders.

🔬 This loop draws the main splat blob by layering 5 random ellipses. What happens if you change the 5 to 10 or 3? Will the splat look more detailed or simpler?

    // Draw an irregular splat shape using multiple overlapping ellipses
    for (let i = 0; i < 5; i++) {
      let ox = random(-this.size * 0.2, this.size * 0.2);
      let oy = random(-this.size * 0.2, this.size * 0.2);
      let os = random(this.size * 0.6, this.size * 1.2);
      ellipse(this.x + ox, this.y + oy, os, os * random(0.5, 1.5));
    }
class Splat {
  constructor(x, y, size, color) {
    this.x = x;
    this.y = y;
    this.size = size || random(40, 80); // Random size for the splat
    this.color = color || color(100, 150, 50, 200); // Greenish-brown, semi-transparent
  }

  // Draw the splat on the canvas
  display() {
    noStroke();
    fill(this.color);
    
    // Draw an irregular splat shape using multiple overlapping ellipses
    for (let i = 0; i < 5; i++) {
      let ox = random(-this.size * 0.2, this.size * 0.2);
      let oy = random(-this.size * 0.2, this.size * 0.2);
      let os = random(this.size * 0.6, this.size * 1.2);
      ellipse(this.x + ox, this.y + oy, os, os * random(0.5, 1.5));
    }
    
    // Add some smaller speckles around the main splat
    for (let i = 0; i < 10; i++) {
      let ox = random(-this.size * 0.5, this.size * 0.5);
      let oy = random(-this.size * 0.5, this.size * 0.5);
      let os = random(5, 15);
      ellipse(this.x + ox, this.y + oy, os, os);
    }
  }
}
Line-by-line explanation (12 lines)

🔧 Subcomponents:

calculation Constructor (Initialization) constructor(x, y, size, color) {

Initializes a Splat with position, size, and a semi-transparent greenish color

for-loop Main Splat Shape for (let i = 0; i < 5; i++) {

Draws 5 overlapping ellipses at random offsets to create an irregular splat blob

for-loop Speckle Details for (let i = 0; i < 10; i++) {

Adds 10 smaller circular speckles around the main splat for a messy, organic look

constructor(x, y, size, color) {
The constructor runs when a new Splat is created; it accepts position, size, and color parameters
this.color = color || color(100, 150, 50, 200);
Uses the passed color if provided, otherwise defaults to a greenish-brown with alpha (transparency) of 200 out of 255
noStroke();
Turns off strokes so all splat shapes are filled with no outlines
fill(this.color);
Sets the fill color for all ellipses to the splat's color (greenish-brown with transparency)
for (let i = 0; i < 5; i++) {
Loops 5 times to draw the main blob of the splat using randomly positioned and sized ellipses
let ox = random(-this.size * 0.2, this.size * 0.2);
Picks a random x offset between -20% and +20% of the splat size to randomize the position of each blob piece
let oy = random(-this.size * 0.2, this.size * 0.2);
Picks a random y offset for the same reason—this creates the irregular, organic shape
let os = random(this.size * 0.6, this.size * 1.2);
Picks a random size for this blob piece between 60% and 120% of the splat's base size
ellipse(this.x + ox, this.y + oy, os, os * random(0.5, 1.5));
Draws an ellipse at the randomized position; the second size is multiplied by a random value to create stretched, squished shapes
for (let i = 0; i < 10; i++) {
Loops 10 times to draw small speckles around the main splat for added detail and realism
let os = random(5, 15);
Picks a random speckle size between 5 and 15 pixels; much smaller than the main splat
ellipse(this.x + ox, this.y + oy, os, os);
Draws a circular speckle (os is used for both width and height) at the randomized position

📦 Key Variables

bugs array

Stores all active Bug objects currently on the screen; used to draw them and check clicks

let bugs = [];
splats array

Stores all Splat objects (squashed bug marks) currently visible; used to render messy effects

let splats = [];
score number

Tracks the player's current score; incremented by 1 each time a bug is squashed

let score = 0;
gameStartTime number

Stores the millisecond timestamp when the game started; used to calculate elapsed time and countdown

let gameStartTime; // Set in setup()
gameDuration number

The total length of the game in milliseconds; currently set to 30 seconds (30000ms)

let gameDuration = 30000;
gameOver boolean

Flag indicating whether the game has ended; controls whether new bugs spawn and whether the game over screen displays

let gameOver = false;
bugSpawnRate number

The number of milliseconds that must pass before the next bug spawns; decreases each spawn to increase difficulty

let bugSpawnRate = 1000;
lastBugSpawnTime number

Stores the millisecond timestamp of the last bug spawn; used to check if enough time has passed for the next one

let lastBugSpawnTime; // Set in setup()

🔧 Potential Improvements (5)

Here are some ways this code could be enhanced:

BUG Bug.isClicked()

Click detection uses a rectangular bounding box, but the bug shape is curved and irregular. Players may click near the antennae or legs and not register a hit, causing frustration.

💡 Use distance-based detection instead: calculate the distance from the click to the bug's center using dist() and check if it's less than the bug's radius (size/2). This creates a circular hit zone that better matches the visual shape.

BUG Splat.display()

Every time a splat is drawn, the random() function generates new offsets, causing the splat shape to change and shimmer every frame rather than staying still.

💡 Generate the random offsets and sizes once in the constructor and store them as properties (this.offsets, this.sizes), then use those stored values in display(). This makes splats appear stable on screen.

PERFORMANCE draw()

Squashed bugs remain in the bugs array forever, so the loop that checks all bugs gets slower as the game progresses and more bugs are squashed.

💡 Remove squashed bugs from the array after they've been marked. After creating a splat, splice the squashed bug out of the bugs array with bugs.splice(i, 1).

FEATURE Bug class

All bugs spawn at the same position and don't move; they just sit static waiting to be clicked. This makes the game less dynamic and less challenging over time.

💡 Add velocity properties (vx, vy) to the Bug constructor and update position in display() before drawing. Make bugs scurry across the screen, making them harder to hit and more true to the sketch's description.

STYLE global

Magic numbers like 0.75, 0.5, 0.6 are scattered throughout Bug.display() and make it hard to tweak proportions consistently or understand the design intent.

💡 Define constants at the top of the file like const BODY_WIDTH_RATIO = 1.5; const HEAD_OFFSET = 0.7; to make the code more readable and maintainable.

🔄 Code Flow

Code flow showing setup, draw, spawnbug, mousepressed, windowresized, bug, splat

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

graph TD start[Start] --> setup[setup] setup --> draw[draw loop] draw --> bug-spawn-logic[Bug Spawn Check] draw --> difficulty-scaling[Difficulty Acceleration] draw --> timer-calculation[Time Remaining] draw --> game-over-check[Game Over Detection] draw --> bug-rendering[Draw All Bugs] draw --> splat-rendering[Draw All Splats] draw --> mousepressed[mousePressed] mousepressed --> game-restart-logic[Game Restart Handler] mousepressed --> bug-click-loop[Bug Click Detection] bug-click-loop --> squash-logic[Squash Condition] bug-click-loop --> display-condition[Display Alive Check] bug-click-loop --> click-detection[Bounding Box Collision] bug-rendering --> bug-constructor[Constructor (Initialization)] bug-constructor --> display-condition splat-rendering --> splat-constructor[Constructor (Initialization)] splat-constructor --> main-splat-loop[Main Splat Shape] main-splat-loop --> speckle-loop[Speckle Details] click setup href "#fn-setup" click draw href "#fn-draw" click bug-spawn-logic href "#sub-bug-spawn-logic" click difficulty-scaling href "#sub-difficulty-scaling" click timer-calculation href "#sub-timer-calculation" click game-over-check href "#sub-game-over-check" click bug-rendering href "#sub-bug-rendering" click splat-rendering href "#sub-splat-rendering" click mousepressed href "#fn-mousepressed" click game-restart-logic href "#sub-game-restart-logic" click bug-click-loop href "#sub-bug-click-loop" click squash-logic href "#sub-squash-logic" click display-condition href "#sub-display-condition" click click-detection href "#sub-click-detection" click bug-constructor href "#sub-bug-constructor" click splat-constructor href "#sub-splat-constructor" click main-splat-loop href "#sub-main-splat-loop" click speckle-loop href "#sub-speckle-loop"

❓ Frequently Asked Questions

What visual elements are featured in the hehehe p5.js sketch?

The sketch creates a lively scene where cockroaches scurry across the screen, represented by animated ovals and lines, showcasing a playful and chaotic environment.

How can players interact with the hehehe sketch?

Users can click on the roaches to squash them, earning points for each successful hit before the timer runs out.

What creative coding concepts does the hehehe sketch illustrate?

This sketch demonstrates object-oriented programming through the Bug class and utilizes randomization for bug sizes and spawn timing to enhance gameplay dynamics.

Preview

hehehe - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of hehehe - Code flow showing setup, draw, spawnbug, mousepressed, windowresized, bug, splat
Code Flow Diagram