I think Anonymous is the goat and I hate with all my mind body and soul Stella dev (the temu one)

This is a space shooter game where players control a blue triangle ship to dodge falling enemies and shoot them down. The player moves by touching or clicking, fires projectiles by tapping, and loses when an enemy collides with them or passes the bottom of the screen. Score increases with each enemy destroyed.

🧪 Try This!

Experiment with the code by making these changes:

  1. Make enemies spawn faster — Lower the spawn interval to make enemies appear more frequently, raising difficulty instantly.
  2. Double the points per kill — Higher point rewards make progression feel faster and give more incentive to stay alive longer.
  3. Change the player ship color — Swap the blue ship for a bright red one to change the visual theme instantly.
  4. Make projectiles yellow-green — A vibrant color change that makes your bullets pop more visually.
  5. Slow down enemy movement — Reduce the enemy speed range to make the game easier and give more time to react.
  6. Make the ship larger — A bigger ship is easier to control and harder to hit, but also takes up more of the screen.
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch creates a complete space shooter game with a player-controlled blue triangle ship, falling enemies with random names, and yellow projectiles fired upward. The game uses object-oriented programming with three classes—Player, Enemy, and Projectile—to manage independent game objects and their behaviors. Players move their ship by touching or clicking, shoot by tapping, and the game tracks score while handling collisions and game-over conditions.

The code is organized around three custom classes that handle all game logic, plus a main draw loop that orchestrates movement, collision detection, and rendering. By studying this sketch you will learn how to structure a complete interactive game: how classes encapsulate state and behavior, how nested loops check collisions between multiple object types, how game state variables control flow between playing and game-over screens, and how touch input drives player movement and actions.

⚙️ How It Works

  1. When the sketch loads, setup() creates a canvas sized to the window and instantiates a single Player object positioned at the bottom center.
  2. Every frame, draw() clears the background and updates all game objects: the player moves to follow the mouse or touch position, enemies fall downward, and projectiles move upward.
  3. Every 60 frames (approximately once per second), a new Enemy spawns at a random x position above the canvas with a random speed and name label.
  4. Two nested loops check collisions: the outer loop moves and displays each projectile, and an inner loop tests if that projectile hits any enemy—if so, the enemy and projectile are removed and score increases by 10.
  5. A separate loop checks if any enemy hits the player or passes below the screen—either collision ends the game and plays a sound effect.
  6. The score displays at the top during play; when the game ends, a game-over screen shows the final score and waits for the player to tap to restart.

🎓 Concepts You'll Learn

Object-oriented programming with classesCollision detection using distanceGame state managementTouch and mouse input handlingNested loops for multi-object interactionsDynamic array management (splice)Audio synthesis with p5.sound

📝 Code Breakdown

Player class

Classes bundle data (properties like x, y, size) and behavior (methods like move, show, hits) into a reusable template. Every instance of Player gets its own x, y, and size values, so you could create multiple ships without code duplication. The hits() method demonstrates collision detection—a skill used throughout game development.

🔬 This method binds the ship to your finger or cursor. What happens if you change this.x = touches[0].x to this.x += touches[0].x - this.x scaled by 0.1? Try: this.x += (touches[0].x - this.x) * 0.1 to make the ship smoothly lag behind your input.

  move() {
    // Player follows touch/mouse X position
    if (touches.length > 0) {
      this.x = touches[0].x;
    } else {
      this.x = mouseX;
    }
    this.x = constrain(this.x, this.size / 2, width - this.size / 2);
  }
class Player {
  constructor() {
    this.size = 50;
    this.x = width / 2;
    this.y = height - this.size * 1.5;
    this.speed = 10;
  }

  show() {
    fill(0, 150, 255); // Blue ship
    noStroke();
    // Simple ship shape
    triangle(this.x, this.y - this.size / 2,
             this.x - this.size / 2, this.y + this.size / 2,
             this.x + this.size / 2, this.y + this.size / 2);
  }

  move() {
    // Player follows touch/mouse X position
    if (touches.length > 0) {
      this.x = touches[0].x;
    } else {
      this.x = mouseX;
    }
    this.x = constrain(this.x, this.size / 2, width - this.size / 2);
  }

  // Check collision with an enemy
  hits(enemy) {
    let d = dist(this.x, this.y, enemy.x, enemy.y);
    return (d < this.size / 2 + enemy.size / 2);
  }
}
Line-by-line explanation (14 lines)

🔧 Subcomponents:

calculation Player initialization this.y = height - this.size * 1.5;

Positions the ship near the bottom of the canvas with some margin

conditional Touch versus mouse detection if (touches.length > 0) {

Checks for touch input on mobile, falls back to mouse on desktop

calculation Keep player on screen this.x = constrain(this.x, this.size / 2, width - this.size / 2);

Prevents the ship from moving off the left or right edge

calculation Distance-based collision let d = dist(this.x, this.y, enemy.x, enemy.y);

Calculates distance between ship and enemy to detect a hit

class Player {
Begins the Player class definition, a blueprint for creating the controllable ship
this.size = 50;
Sets the ship's size to 50 pixels—used for drawing and collision detection
this.x = width / 2;
Starts the ship horizontally centered on the canvas
this.y = height - this.size * 1.5;
Positions the ship near the bottom, with some space between it and the screen edge
this.speed = 10;
Stores the responsiveness value—higher means faster response to touch/mouse input
fill(0, 150, 255); // Blue ship
Sets the drawing color to bright blue (RGB: 0, 150, 255)
triangle(this.x, this.y - this.size / 2,
Draws a three-pointed triangle with the top point centered at the ship's position
if (touches.length > 0) {
Checks if the user is touching the screen—true on mobile, false on desktop
this.x = touches[0].x;
Moves the ship to the x position of the first touch
} else {
If no touches detected, use mouse input instead
this.x = mouseX;
Moves the ship to follow the current mouse x position
this.x = constrain(this.x, this.size / 2, width - this.size / 2);
Clamps the x position to keep the ship fully inside the canvas boundaries
let d = dist(this.x, this.y, enemy.x, enemy.y);
Calculates the Euclidean distance between the ship center and enemy center
return (d < this.size / 2 + enemy.size / 2);
Returns true if the distance is smaller than the sum of both radii—they are touching

Enemy class

Enemy encapsulates spawning behavior, visual appearance, and falling logic. By passing a name to the constructor, the game can create many Enemy instances with unique labels. The offscreen() check is a cleanup pattern—removing enemies that leave the screen prevents memory from filling up over time.

🔬 Right now enemies spawn with random speeds between 2 and 5. What happens if you change random(2, 5) to a fixed value like 7? Try 7 to see all enemies fall at the same, faster rate.

  constructor(name) {
    this.name = name;
    this.size = 40;
    this.x = random(this.size / 2, width - this.size / 2);
    this.y = -this.size / 2; // Start above canvas
    this.speed = random(2, 5);
class Enemy {
  constructor(name) {
    this.name = name;
    this.size = 40;
    this.x = random(this.size / 2, width - this.size / 2);
    this.y = -this.size / 2; // Start above canvas
    this.speed = random(2, 5);
    this.color = color(random(200, 255), 50, 50); // Reddish hues
  }

  show() {
    fill(this.color);
    noStroke();
    ellipse(this.x, this.y, this.size); // Simple circle enemy
    fill(255);
    textAlign(CENTER, CENTER);
    textSize(14);
    text(this.name, this.x, this.y);
  }

  move() {
    this.y += this.speed;
  }

  offscreen() {
    return this.y > height + this.size / 2;
  }
}
Line-by-line explanation (14 lines)

🔧 Subcomponents:

calculation Random spawn position this.x = random(this.size / 2, width - this.size / 2);

Places each enemy at a random horizontal position, keeping it fully on-screen

calculation Varied red tones this.color = color(random(200, 255), 50, 50); // Reddish hues

Generates a unique reddish color for each enemy, making them visually distinct

conditional Out-of-bounds detection return this.y > height + this.size / 2;

Returns true when the enemy has fallen completely below the visible canvas

class Enemy {
Begins the Enemy class definition, a blueprint for falling hazards
constructor(name) {
The constructor receives a name parameter to label each enemy uniquely
this.name = name;
Stores the name string (e.g., 'Anonymous is the goat') to display on the enemy
this.size = 40;
Sets the enemy diameter to 40 pixels
this.x = random(this.size / 2, width - this.size / 2);
Chooses a random x position that keeps the enemy fully within the canvas width
this.y = -this.size / 2; // Start above canvas
Places the enemy just above the top edge so it falls into view smoothly
this.speed = random(2, 5);
Assigns each enemy a random falling speed between 2 and 5 pixels per frame
this.color = color(random(200, 255), 50, 50); // Reddish hues
Creates a unique reddish color for visual variety (red channel 200-255, green and blue at 50)
fill(this.color);
Sets the fill color to this enemy's unique color before drawing
ellipse(this.x, this.y, this.size); // Simple circle enemy
Draws a circle at the enemy's current position with its assigned diameter
fill(255);
Changes fill to white for the text label
text(this.name, this.x, this.y);
Displays the enemy's name centered on its circle
this.y += this.speed;
Moves the enemy downward by its speed value each frame
return this.y > height + this.size / 2;
Returns true once the enemy has completely left the bottom of the screen

Projectile class

Projectile reuses the distance-based collision pattern from Player and Enemy, demonstrating how collision detection is a universal pattern. Notice it moves in the opposite direction (up) compared to enemies (down)—a simple inversion of the speed math.

🔬 Projectiles move upward by subtracting speed from y. What if you changed this to this.y -= this.speed / 2 to make bullets travel at half speed?

  move() {
    this.y -= this.speed;
  }
class Projectile {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this.size = 10;
    this.speed = 8;
  }

  show() {
    fill(255, 255, 0); // Yellow projectile
    noStroke();
    ellipse(this.x, this.y, this.size);
  }

  move() {
    this.y -= this.speed;
  }

  offscreen() {
    return this.y < -this.size / 2;
  }

  // Check collision with an enemy
  hits(enemy) {
    let d = dist(this.x, this.y, enemy.x, enemy.y);
    return (d < this.size / 2 + enemy.size / 2);
  }
}
Line-by-line explanation (11 lines)

🔧 Subcomponents:

calculation Projectile spawning this.x = x;

Spawns each projectile at the ship's current position

calculation Upward movement this.y -= this.speed;

Moves projectiles upward by decreasing their y position each frame

calculation Projectile-enemy collision let d = dist(this.x, this.y, enemy.x, enemy.y);

Calculates distance to detect when a projectile hits an enemy

class Projectile {
Begins the Projectile class definition for bullets fired by the player
constructor(x, y) {
Constructor receives the ship's x and y position as spawn coordinates
this.x = x;
Stores the x position where the projectile is fired
this.y = y;
Stores the y position, adjusted slightly above the ship's nose
this.size = 10;
Sets projectile diameter to 10 pixels—smaller than the ship
this.speed = 8;
Projectiles move 8 pixels per frame, faster than most enemies
fill(255, 255, 0); // Yellow projectile
Sets the fill color to bright yellow (pure red + green, no blue)
this.y -= this.speed;
Decreases y (moves upward) instead of increasing it like enemies
return this.y < -this.size / 2;
Returns true once the projectile has fully left the top of the screen
let d = dist(this.x, this.y, enemy.x, enemy.y);
Calculates distance between projectile and enemy centers for collision testing
return (d < this.size / 2 + enemy.size / 2);
Returns true if the distance is less than both radii combined—a hit

preload()

preload() is p5.js's way of ensuring files load before the sketch starts—without it, you might try to use sounds that haven't finished loading yet. The code uses p5.Oscillator (a synthesis technique) instead of pre-recorded sounds, generating tones mathematically on the fly. The commented-out loadSound lines show how to load actual audio files instead.

function preload() {
  // Load sounds
  soundFormats('wav', 'ogg');
  // You can replace these with your own sound URLs if you have them
  // For now, I'll use placeholder comments.
  // shootSound = loadSound('assets/shoot.wav');
  // gameOverSound = loadSound('assets/gameOver.wav');
  // bgMusic = loadSound('assets/bgMusic.ogg');

  // Placeholder for sounds if not loaded
  shootSound = new p5.Oscillator('sine');
  shootSound.freq(440);
  shootSound.amp(0); // Start silent
  gameOverSound = new p5.Oscillator('triangle');
  gameOverSound.freq(220);
  gameOverSound.amp(0); // Start silent
}
Line-by-line explanation (8 lines)

🔧 Subcomponents:

calculation Sound file format setup soundFormats('wav', 'ogg');

Tells p5.sound which file formats to accept when loading sounds

calculation Shoot sound oscillator shootSound = new p5.Oscillator('sine');

Creates a sine wave generator for the shooting sound effect

calculation Game-over sound oscillator gameOverSound = new p5.Oscillator('triangle');

Creates a triangle wave generator for the game-over sound

function preload() {
preload() runs before setup—it loads files (images, sounds) that the sketch needs
soundFormats('wav', 'ogg');
Registers accepted sound file formats so p5.sound knows what to look for
shootSound = new p5.Oscillator('sine');
Creates an oscillator that can play a sine-wave tone (smooth, pure sound)
shootSound.freq(440);
Sets the shoot sound frequency to 440 Hz, which is the musical note A4
shootSound.amp(0); // Start silent
Sets amplitude to 0 so the oscillator doesn't play until we trigger it
gameOverSound = new p5.Oscillator('triangle');
Creates an oscillator with a triangle wave shape (slightly harsher than sine)
gameOverSound.freq(220);
Sets the game-over frequency to 220 Hz, exactly half of 440 (one octave lower)
gameOverSound.amp(0); // Start silent
Starts silent until the game ends and we trigger it

setup()

setup() is called exactly once per sketch. Here it establishes the canvas size, creates the player, and configures text rendering. By using windowWidth and windowHeight, the game adapts to any screen size without hardcoding pixel values.

function setup() {
  createCanvas(windowWidth, windowHeight);
  player = new Player();
  textAlign(CENTER, CENTER);
}
Line-by-line explanation (4 lines)

🔧 Subcomponents:

calculation Full-screen canvas createCanvas(windowWidth, windowHeight);

Creates a canvas that fills the entire browser window

calculation Player creation player = new Player();

Instantiates a single Player object that persists throughout the game

function setup() {
setup() runs once when the sketch loads—use it to initialize the canvas and global state
createCanvas(windowWidth, windowHeight);
Creates a canvas as wide and tall as the browser window for a full-screen experience
player = new Player();
Creates a new Player object and assigns it to the global player variable
textAlign(CENTER, CENTER);
Centers all text both horizontally and vertically, used for score and game-over text display

draw()

draw() is the heartbeat of the game—it runs 60 times per second, orchestrating all game logic. The key pattern here is nested loops: the outer loop processes projectiles, and the inner loop tests each projectile against every enemy. This is O(n × m) collision detection, which works fine for small numbers of objects but becomes expensive with thousands. The backward-looping and projectileRemovedThisFrame flag handle a tricky problem: if you remove an array element while iterating forward, you skip the next element. Looping backward and using flags avoid this bug.

🔬 This nested loop tests each projectile against every enemy. What happens if you remove the 'break' statement so that one projectile can destroy multiple enemies? Try: delete the line that says 'break;' and watch projectiles pass through all enemies in their path.

      // Check if projectile hits enemy
      for (let j = enemies.length - 1; j >= 0; j--) {
        if (projectiles[i].hits(enemies[j])) {
          score += 10; // Increase score
          enemies.splice(j, 1); // Remove enemy
          projectiles.splice(i, 1); // Remove projectile
          projectileRemovedThisFrame = true; // Mark that it was removed
          break; // Stop checking for this projectile against other enemies
        }
      }
function draw() {
  background(25, 25, 50); // Dark blue background

  if (gameState === 'playing') {
    player.move();
    player.show();

    // Spawn enemies
    if (frameCount % 60 === 0) { // Every second
      let enemyName = random(['Anonymous is the goat', 'Corbun', 'Ai summary fixes']);
      enemies.push(new Enemy(enemyName));
    }

    // Move and show enemies
    for (let i = enemies.length - 1; i >= 0; i--) {
      enemies[i].move();
      enemies[i].show();

      // Check if enemy hits player
      if (player.hits(enemies[i])) {
        gameState = 'gameOver';
        if (gameOverSound) {
          gameOverSound.start();
          gameOverSound.amp(0.5, 0.1);
          gameOverSound.amp(0, 0.5);
        }
      }

      // Check if enemy goes offscreen
      if (enemies[i].offscreen()) {
        enemies.splice(i, 1);
        // Game over if enemy passes player
        gameState = 'gameOver';
        if (gameOverSound) {
          gameOverSound.start();
          gameOverSound.amp(0.5, 0.1);
          gameOverSound.amp(0, 0.5);
        }
      }
    }

    // Move and show projectiles
    for (let i = projectiles.length - 1; i >= 0; i--) {
      let projectileRemovedThisFrame = false; // Flag to track if the current projectile was removed

      // Check if projectile hits enemy
      for (let j = enemies.length - 1; j >= 0; j--) {
        if (projectiles[i].hits(enemies[j])) {
          score += 10; // Increase score
          enemies.splice(j, 1); // Remove enemy
          projectiles.splice(i, 1); // Remove projectile
          projectileRemovedThisFrame = true; // Mark that it was removed
          break; // Stop checking for this projectile against other enemies
        }
      }

      // If the projectile was removed in the inner loop, skip the rest of this outer loop iteration
      if (projectileRemovedThisFrame) {
        continue; // Move to the next projectile in the outer loop
      }

      // Only move, show, and check offscreen if projectile was NOT removed by collision
      projectiles[i].move();
      projectiles[i].show();

      // Check if projectile goes offscreen
      if (projectiles[i].offscreen()) {
        projectiles.splice(i, 1);
      }
    }

    // Display score
    fill(255);
    textSize(20);
    text('Score: ' + score, width / 2, 30);
  } else if (gameState === 'gameOver') {
    fill(255, 50, 50);
    textSize(48);
    text('GAME OVER', width / 2, height / 2 - 50);
    textSize(24);
    text('Final Score: ' + score, width / 2, height / 2 + 10);
    text('Tap to Restart', width / 2, height / 2 + 60);
  }
}
Line-by-line explanation (37 lines)

🔧 Subcomponents:

conditional Enemy spawning condition if (frameCount % 60 === 0) { // Every second

Creates a new enemy approximately once per second

for-loop Update all enemies for (let i = enemies.length - 1; i >= 0; i--) {

Iterates through each enemy to move, display, and check collisions

conditional Detect ship-enemy collision if (player.hits(enemies[i])) {

Ends the game if an enemy touches the player

conditional Detect enemy escape if (enemies[i].offscreen()) {

Ends the game if an enemy reaches the bottom without being destroyed

for-loop Update all projectiles for (let i = projectiles.length - 1; i >= 0; i--) {

Iterates through each projectile to check collisions and update position

nested-for-loop Check all projectile-enemy pairs for (let j = enemies.length - 1; j >= 0; j--) {

Tests every projectile against every enemy to detect hits

conditional Game over display } else if (gameState === 'gameOver') {

Renders the game-over screen when play ends

background(25, 25, 50); // Dark blue background
Clears the canvas with a dark blue color each frame, preventing trails
if (gameState === 'playing') {
Checks if the game is actively running—only update gameplay if true
player.move();
Updates the player's x position based on current touch or mouse position
player.show();
Draws the blue triangle ship at its current position
if (frameCount % 60 === 0) { // Every second
Tests if the current frame number is evenly divisible by 60 (true every 60 frames at 60 FPS ≈ once per second)
let enemyName = random(['Anonymous is the goat', 'Corbun', 'Ai summary fixes']);
Picks a random name string from the array to assign to the new enemy
enemies.push(new Enemy(enemyName));
Creates a new Enemy with that name and adds it to the enemies array
for (let i = enemies.length - 1; i >= 0; i--) {
Loops through enemies in reverse (from last to first) so splicing during iteration doesn't skip elements
enemies[i].move();
Calls the move() method on each enemy, advancing its y position downward
enemies[i].show();
Calls the show() method to draw the enemy circle and its name label
if (player.hits(enemies[i])) {
Checks if the ship has collided with this enemy
gameState = 'gameOver';
Sets game state to end game, stopping all gameplay updates
gameOverSound.start();
Triggers the game-over sound effect
if (enemies[i].offscreen()) {
Checks if the enemy has fallen below the screen without being destroyed
enemies.splice(i, 1);
Removes that enemy from the array to clean up memory
for (let i = projectiles.length - 1; i >= 0; i--) {
Loops through projectiles in reverse to handle collisions and removal safely
let projectileRemovedThisFrame = false;
Initializes a flag to track whether this projectile has been deleted during this frame's inner loop
for (let j = enemies.length - 1; j >= 0; j--) {
Nested loop: tests the current projectile against every enemy
if (projectiles[i].hits(enemies[j])) {
Checks if projectile and enemy are colliding
score += 10; // Increase score
Awards 10 points for destroying an enemy
enemies.splice(j, 1); // Remove enemy
Deletes the enemy from the array
projectiles.splice(i, 1); // Remove projectile
Deletes the projectile from the array
projectileRemovedThisFrame = true;
Sets the flag so we don't try to move/show this projectile again this frame
break; // Stop checking for this projectile against other enemies
Exits the inner loop because the projectile is already destroyed
if (projectileRemovedThisFrame) {
Checks if we removed the projectile this frame
continue; // Move to the next projectile in the outer loop
Skips the rest of this iteration to avoid accessing a deleted projectile
projectiles[i].move();
Moves the projectile upward by its speed
projectiles[i].show();
Draws the yellow projectile at its current position
if (projectiles[i].offscreen()) {
Checks if the projectile has left the top of the screen
projectiles.splice(i, 1);
Removes the projectile from the array once it's off-screen
fill(255);
Sets text color to white for the score display
textSize(20);
Sets text size to 20 pixels
text('Score: ' + score, width / 2, height / 2, 30);
Displays the current score text at the top center of the canvas
} else if (gameState === 'gameOver') {
Enters this branch if the game is over
fill(255, 50, 50);
Sets text color to red for the game-over message
textSize(48);
Sets text size to 48 pixels for the main game-over text
text('GAME OVER', width / 2, height / 2 - 50);
Displays 'GAME OVER' centered horizontally and slightly above vertical center

touchStarted()

touchStarted() is p5.js's event handler for touch input. It fires once per tap. The function handles two different situations: during gameplay it fires a projectile, but after game over it resets everything for a new round. This dual-purpose design is common in games—the same button (tap) serves different actions depending on game state.

🔬 Projectiles spawn at the ship's x position, but slightly above it (y - size/2). What happens if you change this to player.y so they spawn at the ship's center, or player.y + player.size / 2 below the ship?

    projectiles.push(new Projectile(player.x, player.y - player.size / 2));
function touchStarted() {
  userStartAudio(); // Required for mobile audio playback

  if (gameState === 'playing') {
    projectiles.push(new Projectile(player.x, player.y - player.size / 2));
    if (shootSound) {
      shootSound.start();
      shootSound.amp(0.3, 0.05); // Play a short sound
      shootSound.amp(0, 0.2);
    }
  } else if (gameState === 'gameOver') {
    // Restart the game
    gameState = 'playing';
    score = 0;
    enemies = [];
    projectiles = [];
    if (gameOverSound) {
      gameOverSound.stop();
    }
  }
  return false; // Prevent default browser behavior (e.g., scrolling)
}
Line-by-line explanation (16 lines)

🔧 Subcomponents:

calculation Mobile audio permission userStartAudio();

Requests audio playback permission on mobile devices (required by browsers)

calculation Fire projectile on tap projectiles.push(new Projectile(player.x, player.y - player.size / 2));

Creates a new projectile at the ship's nose when the player taps

calculation Play shoot sound shootSound.start();

Triggers the shooting sound effect with a brief envelope

conditional Restart on game over gameState = 'playing';

Resets all game state when the player taps to restart

function touchStarted() {
Called automatically whenever the user touches the screen
userStartAudio();
Requests permission to play audio on mobile—many browsers block audio until user interaction
if (gameState === 'playing') {
If the game is active, handle shooting
projectiles.push(new Projectile(player.x, player.y - player.size / 2));
Creates a new projectile slightly above the ship's center and adds it to the projectiles array
if (shootSound) {
Checks that shootSound exists before trying to play it
shootSound.start();
Begins playback of the oscillator
shootSound.amp(0.3, 0.05);
Ramps amplitude to 0.3 over 0.05 seconds (fade in very quickly)
shootSound.amp(0, 0.2);
Ramps amplitude back to 0 over 0.2 seconds (fade out), creating a brief 'beep'
} else if (gameState === 'gameOver') {
If the game is over, handle the restart tap
gameState = 'playing';
Switches back to playing mode
score = 0;
Resets the score to zero
enemies = [];
Clears the enemies array, removing all on-screen hazards
projectiles = [];
Clears the projectiles array, removing all on-screen bullets
if (gameOverSound) {
Checks that gameOverSound exists
gameOverSound.stop();
Stops the game-over sound that may be playing
return false;
Tells the browser not to handle this touch event (prevents unwanted scrolling or zooming)

touchMoved()

touchMoved() prevents the browser's default touch behavior (scrolling, zooming). By returning false, you're telling the browser 'I've handled this event, don't apply your defaults.' This is essential for game controls—without it, dragging your finger would scroll the page instead of moving the ship.

function touchMoved() {
  return false;
}
Line-by-line explanation (2 lines)
function touchMoved() {
Called continuously while the user's finger drags across the screen
return false;
Prevents the browser from handling touch movement (stops scrolling, zooming, and other defaults)

windowResized()

windowResized() ensures the game adapts gracefully to screen changes—crucial for mobile devices that rotate between portrait and landscape. Without this function, the canvas would not resize, leaving black bars on the screen. Notice that we only update the player's y position; the x position adjusts automatically in the move() method every frame.

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
  player.y = height - player.size * 1.5; // Reposition player
}
Line-by-line explanation (3 lines)

🔧 Subcomponents:

calculation Update canvas dimensions resizeCanvas(windowWidth, windowHeight);

Expands or shrinks the canvas to match the browser window size

calculation Keep player at bottom player.y = height - player.size * 1.5;

Moves the player to stay at the bottom of the resized canvas

function windowResized() {
Called automatically whenever the browser window size changes (rotation on mobile, resizing on desktop)
resizeCanvas(windowWidth, windowHeight);
Updates the canvas size to match the new window dimensions
player.y = height - player.size * 1.5;
Repositions the player so it stays at the bottom even after the canvas resizes

📦 Key Variables

player object (Player instance)

Stores the single Player object that the user controls throughout the game

let player;
enemies array of Enemy objects

Tracks all currently active enemy objects; enemies are added each spawn and removed when destroyed or escaped

let enemies = [];
projectiles array of Projectile objects

Tracks all currently active projectile objects; projectiles are added when fired and removed on collision or going off-screen

let projectiles = [];
score number

Accumulates points earned by destroying enemies; displayed during play and on game-over screen

let score = 0;
gameState string

Tracks whether the game is actively running ('playing') or has ended ('gameOver') to control which code executes each frame

let gameState = 'playing';
shootSound p5.Oscillator

Stores the oscillator that plays a brief beep when the player fires a projectile

let shootSound;
gameOverSound p5.Oscillator

Stores the oscillator that plays a sound when the game ends

let gameOverSound;

🔧 Potential Improvements (6)

Here are some ways this code could be enhanced:

BUG draw() - enemy offscreen check

When an enemy goes offscreen at the bottom, enemies.splice(i, 1) removes it from the array, but then gameState is set to 'gameOver' unconditionally. This means an enemy that spawned but never got hit will always end the game, defeating the purpose of dodging.

💡 Remove the enemies.splice(i, 1) line before the 'Game over if enemy passes player' condition, or move the game-over logic to only trigger after splicing: if (enemies[i].offscreen()) { enemies.splice(i, 1); } without the gameState = 'gameOver' inside. Let the player dodge by moving; only end if an enemy actually hits them.

PERFORMANCE draw() - nested collision loops

The projectile loop tests every projectile against every enemy. With 50 projectiles and 50 enemies, that's 2,500 distance calculations per frame. On mobile or low-end devices, this becomes expensive.

💡 Implement spatial partitioning (dividing the canvas into grid cells) or use a quadtree so you only test nearby objects. Alternatively, limit projectiles and enemies to a reasonable maximum.

STYLE Variables and functions

Some variable names are unclear or inconsistent—for example, uses 'i' and 'j' for loop indices without explaining what they iterate, and magic numbers (60 for spawn rate, 10 for points) are hardcoded throughout.

💡 Extract magic numbers to named constants at the top: const SPAWN_INTERVAL = 60; const POINTS_PER_KILL = 10;. Use more descriptive loop variable names in complex nested loops or add comments explaining their purpose.

FEATURE Collision detection

Collision detection uses a simple circle-distance check, which works but can miss edge cases (a fast projectile can pass through a slow enemy between frames if relative speed is very high).

💡 Implement continuous collision detection by testing the projectile's path (line segment) against the enemy circle, or reduce the projectile speed or increase the frame rate.

BUG touchStarted()

The function creates a new projectile at the ship's position, but does not limit fire rate. Players can fire multiple projectiles per frame on fast devices, making the game trivially easy.

💡 Track the last fire time (let lastFireTime = 0;) and only allow a new projectile if millis() - lastFireTime > FIRE_COOLDOWN (e.g., 200ms). This prevents spam firing.

FEATURE Game over state

When the game ends, enemies and projectiles persist on the screen before the restart tap clears them. On the game-over screen, you still see the old play area flickering.

💡 Clear enemies and projectiles immediately when gameState becomes 'gameOver', or draw the game-over screen without the game background: only render enemies/projectiles while gameState === 'playing'.

🔄 Code Flow

Code flow showing player, enemy, projectile, preload, setup, draw, touchstarted, touchmoved, windowresized

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

graph TD start[Start] --> setup[setup] setup --> canvas-creation[canvas-creation] canvas-creation --> player-instantiation[player-instantiation] player-instantiation --> draw[draw loop] draw --> enemy-spawn-loop[enemy-spawn-loop] enemy-spawn-loop --> enemy-movement-loop[enemy-movement-loop] enemy-movement-loop --> player-enemy-collision[player-enemy-collision] enemy-movement-loop --> enemy-passed-check[enemy-passed-check] draw --> projectile-loop[projectile-loop] projectile-loop --> projectile-enemy-collision[projectile-enemy-collision] draw --> game-over-screen[game-over-screen] click setup href "#fn-setup" click draw href "#fn-draw" click canvas-creation href "#sub-canvas-creation" click player-instantiation href "#sub-player-instantiation" click enemy-spawn-loop href "#sub-enemy-spawn-loop" click enemy-movement-loop href "#sub-enemy-movement-loop" click player-enemy-collision href "#sub-player-enemy-collision" click enemy-passed-check href "#sub-enemy-passed-check" click projectile-loop href "#sub-projectile-loop" click projectile-enemy-collision href "#sub-projectile-enemy-collision" click game-over-screen href "#sub-game-over-screen" %% Subcomponents inside setup player-instantiation --> player-constructor[player-constructor] click player-constructor href "#sub-player-constructor" %% Subcomponents inside enemy-spawn-loop enemy-spawn-loop --> enemy-random-x[enemy-random-x] enemy-spawn-loop --> enemy-color[enemy-color] click enemy-random-x href "#sub-enemy-random-x" click enemy-color href "#sub-enemy-color" %% Subcomponents inside enemy-movement-loop enemy-movement-loop --> enemy-offscreen-check[enemy-offscreen-check] click enemy-offscreen-check href "#sub-enemy-offscreen-check" %% Subcomponents inside player-enemy-collision player-enemy-collision --> player-collision[player-collision] click player-collision href "#sub-player-collision" %% Subcomponents inside projectile-loop projectile-loop --> projectile-constructor[projectile-constructor] projectile-loop --> projectile-upward-motion[projectile-upward-motion] click projectile-constructor href "#sub-projectile-constructor" click projectile-upward-motion href "#sub-projectile-upward-motion" %% Subcomponents inside projectile-enemy-collision projectile-enemy-collision --> projectile-collision[projectile-collision] click projectile-collision href "#sub-projectile-collision" %% Subcomponents inside game-over-screen game-over-screen --> game-restart-logic[game-restart-logic] click game-restart-logic href "#sub-game-restart-logic"

❓ Frequently Asked Questions

What visual elements can I expect in this p5.js sketch?

This sketch features a blue ship controlled by the player and reddish circular enemies that descend from the top of the canvas, displaying their names as they fall.

How do users interact with the sketch?

Users can control the blue ship's horizontal position by moving their mouse or touching the screen, avoiding collisions with the falling enemies.

What coding concepts are demonstrated in this creative coding sketch?

The sketch showcases object-oriented programming through the use of classes for Player, Enemy, and Projectile, as well as basic collision detection and movement mechanics.

Preview

I think Anonymous is the goat and I hate with all my mind body and soul Stella dev (the temu one) - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of I think Anonymous is the goat and I hate with all my mind body and soul Stella dev (the temu one) - Code flow showing player, enemy, projectile, preload, setup, draw, touchstarted, touchmoved, windowresized
Code Flow Diagram