Sketch 2026-03-02 23:21

This is a 'Catch the Stars' game where players control a paddle at the bottom of the screen to catch falling stars and earn points. The game uses Matter.js physics simulation to handle gravity and collisions, increases difficulty over time by spawning stars faster, and ends when all three lives are lost.

🧪 Try This!

Experiment with the code by making these changes:

  1. Make stars fall faster — Increase gravity to see stars accelerate downward quicker, making the game harder from the start
  2. Start with more lives
  3. Award more points per catch
  4. Make the paddle wider
  5. Spawn smaller stars — Change star size range from 15-30 to 5-15 so all stars are tiny and harder to catch
  6. Lose more lives per miss
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch creates an interactive game where stars fall from the top of the canvas and players move a paddle to catch them. Each caught star increases the score, but missed stars cost a life. The visual appeal comes from the combination of p5.js graphics rendering and Matter.js physics simulation—gravity pulls stars downward naturally, they bounce slightly on impact, and the paddle responds instantly to mouse movement, creating a responsive arcade experience.

The code is organized into setup (initializes the physics engine and game state), draw (the main game loop that updates physics, spawns stars, and renders everything), and several helper functions for collision detection, drawing, and game state management. By studying this sketch, you will learn how to integrate an external physics library, detect collisions between objects, manage game state (lives, score, game over), and create a complete playable game with increasing difficulty.

⚙️ How It Works

  1. When the sketch loads, setup() initializes the Matter.js physics engine, creates a static paddle at the bottom of the canvas, adds an invisible ground slightly below the canvas to detect missed stars, and sets up collision event handlers.
  2. Every frame, draw() updates the physics engine, moves the paddle to follow the mouse (constrained to stay on-screen), and checks if enough time has passed to spawn a new star.
  3. Stars are created at random horizontal positions above the canvas and fall due to gravity. Their spawn rate gradually increases (the starSpawnInterval gets smaller) to make the game progressively harder.
  4. When a star collides with the paddle, the collision handler fires, increments the score, and removes the star. When a star hits the invisible ground, the collision handler decrements lives.
  5. If lives reach zero, gameOver is set to true, the game loop stops updating physics, and a game-over screen is displayed. Pressing 'R' calls resetGame() to clear all bodies and restart.
  6. The paddle's position updates every frame based on mouseX, constrained to the canvas width, creating smooth responsive control.

🎓 Concepts You'll Learn

Physics engine integrationCollision detection and event handlingGame state managementDynamic difficulty scalingMouse-based input and constraintsParticle spawning and cleanupFrame-based timing

📝 Code Breakdown

setup()

setup() runs once when the sketch starts. Here we initialize the Matter.js physics engine, create the physical bodies (paddle and ground), set up collision event listeners, and reset all game variables. Understanding this function teaches you how external libraries like Matter.js are initialized and how to create rigid bodies with properties like 'static' and 'label'.

function setup() {
  createCanvas(windowWidth, windowHeight);
  
  // Initialize Matter.js engine and world
  engine = Engine.create();
  world = engine.world;
  world.gravity.y = 1; // Set gravity

  // Create the paddle (a static rectangle at the bottom)
  const paddleWidth = 150;
  const paddleHeight = 20;
  paddle = Bodies.rectangle(
    width / 2, 
    height - paddleHeight / 2 - 10, 
    paddleWidth, 
    paddleHeight, 
    { isStatic: true, label: 'paddle' }
  );
  World.add(world, paddle);

  // Create an invisible ground slightly below the canvas to detect misses
  ground = Bodies.rectangle(
    width / 2, 
    height + 50, 
    width, 
    100, 
    { isStatic: true, label: 'ground' }
  );
  World.add(world, ground);

  // Set up collision events
  Events.on(engine, 'collisionStart', collisionHandler);

  // Initialize game state
  score = 0;
  lives = 3;
  lastStarSpawnTime = millis();
  gameOver = false;
}
Line-by-line explanation (7 lines)

🔧 Subcomponents:

initialization Physics Engine Creation engine = Engine.create(); world = engine.world; world.gravity.y = 1;

Creates the Matter.js physics engine and sets gravity to pull objects downward

initialization Paddle Body Creation paddle = Bodies.rectangle( width / 2, height - paddleHeight / 2 - 10, paddleWidth, paddleHeight, { isStatic: true, label: 'paddle' } );

Creates a static (immovable) rectangular body for the paddle and positions it at the bottom center

initialization Invisible Ground Creation ground = Bodies.rectangle( width / 2, height + 50, width, 100, { isStatic: true, label: 'ground' } );

Creates an invisible static body below the canvas to detect missed stars

createCanvas(windowWidth, windowHeight);
Creates a p5.js canvas that fills the entire browser window
engine = Engine.create();
Creates a new Matter.js physics engine that will simulate gravity and collisions
world = engine.world;
Gets a reference to the physics world where all bodies (objects) live
world.gravity.y = 1;
Sets the downward gravity in the physics world—positive y pulls objects down
{ isStatic: true, label: 'paddle' }
Makes the paddle static (immovable by physics) and gives it a label so collisions can identify it
Events.on(engine, 'collisionStart', collisionHandler);
Registers the collisionHandler function to run every time two bodies collide
lastStarSpawnTime = millis();
Records the current millisecond time so we can track when the next star should spawn

draw()

draw() runs 60 times per second and is the heartbeat of the game. Each frame it updates physics, moves the paddle, spawns new stars, draws everything, and checks for game-over conditions. This function teaches you the typical structure of a game loop: check state, update physics, handle input, spawn objects, render, and display UI.

🔬 The constrain() function prevents the paddle from moving off-screen. What happens if you remove the constrain and just set the position directly to mouseX? Try replacing paddleX with mouseX to see the paddle escape the edges.

  const paddleX = constrain(mouseX, paddle.width / 2, width - paddle.width / 2);
  Matter.Body.setPosition(paddle, { x: paddleX, y: paddle.position.y });
function draw() {
  background(40, 42, 54); // Dark background

  if (gameOver) {
    displayGameOver();
    return; // Stop game logic if game over
  }

  // Update Matter.js engine
  Engine.update(engine);

  // Move the paddle with the mouse
  // Constrain paddle to stay within canvas bounds
  const paddleX = constrain(mouseX, paddle.width / 2, width - paddle.width / 2);
  Matter.Body.setPosition(paddle, { x: paddleX, y: paddle.position.y });

  // Spawn stars
  if (millis() - lastStarSpawnTime > starSpawnInterval) {
    createStar();
    lastStarSpawnTime = millis();
    // Gradually increase difficulty by reducing spawn interval
    starSpawnInterval = max(200, starSpawnInterval - 10); 
  }

  // Draw paddle
  drawBody(paddle, color(255, 204, 0)); // Yellow paddle

  // Draw and manage stars
  for (let i = stars.length - 1; i >= 0; i--) {
    let star = stars[i];
    drawBody(star, color(255, 255, 0)); // Yellow stars

    // Remove stars that are way off-screen (missed or caught and fell through)
    if (star.position.y > height + 100) {
      World.remove(world, star);
      stars.splice(i, 1);
    }
  }

  // Display score and lives
  displayGameInfo();
}
Line-by-line explanation (11 lines)

🔧 Subcomponents:

conditional Game Over State Check if (gameOver) { displayGameOver(); return; // Stop game logic if game over }

If the game is over, display the game-over screen and skip all game logic

calculation Physics Engine Update Engine.update(engine);

Advances the physics simulation one frame, updating all body positions based on gravity and velocities

calculation Paddle Position Update const paddleX = constrain(mouseX, paddle.width / 2, width - paddle.width / 2); Matter.Body.setPosition(paddle, { x: paddleX, y: paddle.position.y });

Moves the paddle to follow the mouse horizontally, keeping it within screen bounds

conditional Star Spawning with Difficulty Increase if (millis() - lastStarSpawnTime > starSpawnInterval) { createStar(); lastStarSpawnTime = millis(); starSpawnInterval = max(200, starSpawnInterval - 10);

Spawns a new star when enough time has passed, then reduces the spawn interval to increase difficulty

for-loop Star Drawing and Cleanup Loop for (let i = stars.length - 1; i >= 0; i--) { let star = stars[i]; drawBody(star, color(255, 255, 0)); if (star.position.y > height + 100) { World.remove(world, star); stars.splice(i, 1); } }

Draws each star and removes any that have fallen far below the screen to prevent memory buildup

background(40, 42, 54); // Dark background
Fills the entire canvas with a dark blue-gray color, erasing the previous frame
if (gameOver) {
Checks if the game state is over
return; // Stop game logic if game over
Exits the draw function early, skipping all game updates and only showing the game-over screen
Engine.update(engine);
Tells the Matter.js physics engine to calculate one frame of movement—gravity pulls bodies down, velocities update positions
const paddleX = constrain(mouseX, paddle.width / 2, width - paddle.width / 2);
Gets the current mouse X position, but constrains it to stay within the paddle's width from both edges
Matter.Body.setPosition(paddle, { x: paddleX, y: paddle.position.y });
Moves the paddle to the constrained mouse position, keeping its Y position fixed at the bottom
if (millis() - lastStarSpawnTime > starSpawnInterval) {
Checks if enough milliseconds have passed since the last star spawn
starSpawnInterval = max(200, starSpawnInterval - 10);
Reduces spawn interval by 10 (stars come faster), but never goes below 200ms so the game doesn't become impossible
for (let i = stars.length - 1; i >= 0; i--) {
Loops through the stars array backwards (from last to first) so we can safely remove items during iteration
if (star.position.y > height + 100) {
Checks if the star has fallen far below the canvas—if so, it was either caught or missed and should be cleaned up
stars.splice(i, 1);
Removes the star from the stars array so it's no longer drawn or checked

createStar()

createStar() is called every time the spawn interval elapses. It demonstrates how to create random-sized objects at random positions and add them to both the physics world and a tracking array. This pattern—creating objects, adding them to a physics engine, and storing references—is fundamental to game development.

🔬 These three lines set the star's size and spawn position. What happens if you change the y value from -starSize to 0 or height / 2? Stars would spawn at different heights instead of always from the top.

  const starSize = random(15, 30);
  const x = random(starSize, width - starSize);
  const y = -starSize;
function createStar() {
  const starSize = random(15, 30);
  const x = random(starSize, width - starSize);
  const y = -starSize; // Start above the canvas
  const star = Bodies.circle(x, y, starSize / 2, {
    restitution: 0.8, // Make stars bounce slightly
    friction: 0.1,
    label: 'star' // Label for collision detection
  });
  World.add(world, star);
  stars.push(star);
}
Line-by-line explanation (9 lines)

🔧 Subcomponents:

calculation Random Star Size const starSize = random(15, 30);

Generates a random star size between 15 and 30 pixels for visual variety

calculation Random Star Position const x = random(starSize, width - starSize);

Places the star at a random horizontal position, offset by its radius to keep it on-screen

initialization Star Physics Body Creation const star = Bodies.circle(x, y, starSize / 2, { restitution: 0.8, friction: 0.1, label: 'star' });

Creates a circular Matter.js body with properties for bouncing and friction

const starSize = random(15, 30);
Generates a random number between 15 and 30 for the star's diameter—adds variety to the falling objects
const x = random(starSize, width - starSize);
Generates a random X position, but offset by the star's radius on both sides so the star doesn't spawn half off-screen
const y = -starSize; // Start above the canvas
Places the star just above the top of the canvas so it falls into view smoothly
const star = Bodies.circle(x, y, starSize / 2, {
Creates a circular physics body at position (x, y) with radius = starSize/2
restitution: 0.8, // Make stars bounce slightly
Sets how bouncy the star is—0.8 means it retains 80% of its speed after hitting something (0 = dead stop, 1 = perfect bounce)
friction: 0.1,
Sets how much friction (air resistance) affects the star—low values mean it's slippery
label: 'star' // Label for collision detection
Tags this body with a label so the collision handler can identify it
World.add(world, star);
Adds the star to the physics world so it will be simulated (gravity will pull it, collisions will affect it)
stars.push(star);
Adds the star to the stars array so draw() can access it and render it

drawBody(body, fillColor)

drawBody() is the bridge between Matter.js physics bodies and p5.js graphics. It reads the body's position and angle from the physics engine and draws the corresponding shape at that location. This function teaches you how to synchronize a rendering library (p5.js) with a physics library (Matter.js) using transformations like translate and rotate.

function drawBody(body, fillColor) {
  fill(fillColor);
  noStroke();
  const pos = body.position;
  const angle = body.angle;

  push();
  translate(pos.x, pos.y);
  rotate(angle);

  // Draw based on body type (only circles and rectangles for this game)
  if (body.circleRadius) {
    // Circle (star)
    ellipse(0, 0, body.circleRadius * 2);
  } else {
    // Rectangle (paddle, ground)
    rectMode(CENTER);
    rect(0, 0, body.width, body.height);
  }

  pop();
}
Line-by-line explanation (12 lines)

🔧 Subcomponents:

conditional Shape Type Conditional if (body.circleRadius) { ellipse(0, 0, body.circleRadius * 2); } else { rectMode(CENTER); rect(0, 0, body.width, body.height); }

Checks if the body is circular or rectangular and draws the appropriate shape

fill(fillColor);
Sets the color for the shape being drawn (passed in as a parameter)
noStroke();
Removes the outline, so shapes are filled solid with no border
const pos = body.position;
Gets the current position (x, y) of the physics body
const angle = body.angle;
Gets the current rotation angle of the physics body (in radians)
push();
Saves the current drawing state (transformation, fill color, etc.) so we can restore it later
translate(pos.x, pos.y);
Moves the origin (0,0) to the body's position so shapes draw at the correct location
rotate(angle);
Rotates the drawing context by the body's angle so the shape rotates as the physics body rotates
if (body.circleRadius) {
Checks if the body has a circleRadius property—if so, it's a circle
ellipse(0, 0, body.circleRadius * 2);
Draws a circle at the origin with diameter = circleRadius * 2
rectMode(CENTER);
Sets rect mode to CENTER so the rect is drawn from its center point (0, 0) instead of the top-left
rect(0, 0, body.width, body.height);
Draws a rectangle at the origin with the body's width and height
pop();
Restores the drawing state from before the push() call, undoing the translate and rotate

collisionHandler(event)

collisionHandler() is called by Matter.js every time two bodies collide. It checks the labels of the colliding bodies and applies game logic (increase score, decrease lives, remove stars). This function is crucial to game development—it's where you detect interactions between game objects and respond with gameplay consequences. The reason we check both orders (bodyA/bodyB and bodyB/bodyA) teaches you that collisions are reported symmetrically, and you must handle both cases.

function collisionHandler(event) {
  const pairs = event.pairs;

  for (let i = 0; i < pairs.length; i++) {
    const pair = pairs[i];
    const bodyA = pair.bodyA;
    const bodyB = pair.bodyB;

    // Check for star-paddle collision
    if (bodyA.label === 'star' && bodyB.label === 'paddle') {
      score++;
      World.remove(world, bodyA);
      // Remove star from our array
      stars = stars.filter(star => star !== bodyA);
    } else if (bodyB.label === 'star' && bodyA.label === 'paddle') {
      score++;
      World.remove(world, bodyB);
      // Remove star from our array
      stars = stars.filter(star => star !== bodyB);
    }

    // Check for star-ground collision (miss)
    if (bodyA.label === 'star' && bodyB.label === 'ground') {
      lives--;
      World.remove(world, bodyA);
      // Remove star from our array
      stars = stars.filter(star => star !== bodyA);
      if (lives <= 0) {
        gameOver = true;
      }
    } else if (bodyB.label === 'star' && bodyA.label === 'ground') {
      lives--;
      World.remove(world, bodyB);
      // Remove star from our array
      stars = stars.filter(star => star !== bodyB);
      if (lives <= 0) {
        gameOver = true;
      }
    }
  }
}
Line-by-line explanation (12 lines)

🔧 Subcomponents:

conditional Star-Paddle Collision Handler if (bodyA.label === 'star' && bodyB.label === 'paddle') { score++; World.remove(world, bodyA); stars = stars.filter(star => star !== bodyA); } else if (bodyB.label === 'star' && bodyA.label === 'paddle') {

Increases score and removes the star when it collides with the paddle

conditional Star-Ground Collision Handler if (bodyA.label === 'star' && bodyB.label === 'ground') { lives--; World.remove(world, bodyA); stars = stars.filter(star => star !== bodyA); if (lives <= 0) { gameOver = true; } } else if (bodyB.label === 'star' && bodyA.label === 'ground') {

Decreases lives and removes the star when it misses and hits the ground, ending the game if lives reach zero

const pairs = event.pairs;
Gets the array of collision pairs from the Matter.js collision event—each pair contains two bodies that collided
for (let i = 0; i < pairs.length; i++) {
Loops through each collision pair
const bodyA = pair.bodyA;
Gets the first body in the collision pair
const bodyB = pair.bodyB;
Gets the second body in the collision pair
if (bodyA.label === 'star' && bodyB.label === 'paddle') {
Checks if bodyA is a star and bodyB is a paddle—collision detected
score++;
Increments the score by 1 for catching a star
World.remove(world, bodyA);
Removes the star from the physics world so it stops being simulated
stars = stars.filter(star => star !== bodyA);
Removes the star from the stars array by keeping only stars that are not this one
} else if (bodyB.label === 'star' && bodyA.label === 'paddle') {
Checks the opposite order (bodyB is star, bodyA is paddle) since collision pairs can come in either order
lives--;
Decrements lives by 1 for missing a star
if (lives <= 0) {
Checks if lives have reached zero or below
gameOver = true;
Sets the game over flag so the draw loop will stop updating and display the game-over screen

displayGameInfo()

displayGameInfo() renders the user interface on top of the game. It demonstrates basic p5.js text functions and how to position UI elements at the edges of the screen. This function runs every frame and updates the displayed score and lives in real-time.

function displayGameInfo() {
  fill(255);
  textSize(24);
  textAlign(LEFT);
  text(`Score: ${score}`, 20, 40);
  textAlign(RIGHT);
  text(`Lives: ${lives}`, width - 20, 40);
}
Line-by-line explanation (6 lines)
fill(255);
Sets the text color to white (255, 255, 255)
textSize(24);
Sets the font size to 24 pixels
textAlign(LEFT);
Aligns subsequent text to the left side of its position
text(`Score: ${score}`, 20, 40);
Draws the score text at position (20, 40)—the template string inserts the current score variable
textAlign(RIGHT);
Changes text alignment to the right for the next text call
text(`Lives: ${lives}`, width - 20, 40);
Draws the lives text near the right edge of the canvas (width - 20)

displayGameOver()

displayGameOver() is called from draw() when gameOver is true. It overlays a game-over screen with the final score and restart instructions. This function is an example of UI layering—rendering text on top of the game world to communicate state to the player.

function displayGameOver() {
  fill(255);
  textSize(64);
  textAlign(CENTER, CENTER);
  text('GAME OVER', width / 2, height / 2 - 50);
  textSize(32);
  text(`Final Score: ${score}`, width / 2, height / 2 + 10);
  text('Press R to Restart', width / 2, height / 2 + 60);
}
Line-by-line explanation (7 lines)
fill(255);
Sets text color to white
textSize(64);
Sets large font size for the 'GAME OVER' title
textAlign(CENTER, CENTER);
Centers text both horizontally and vertically around its position coordinates
text('GAME OVER', width / 2, height / 2 - 50);
Draws 'GAME OVER' centered horizontally and positioned above center vertically
textSize(32);
Reduces font size for the score and restart instruction
text(`Final Score: ${score}`, width / 2, height / 2 + 10);
Displays the final score centered on screen
text('Press R to Restart', width / 2, height / 2 + 60);
Displays the restart instruction below the score

keyPressed()

keyPressed() is called every time a key is pressed. This is how we detect player input for restarting the game. Note: there is a subtle operator precedence bug here—the condition is interpreted as '(gameOver && key === r) OR (key === R)', which means pressing R restarts the game even if not gameOver. This is the 'improvements' section.

function keyPressed() {
  if (gameOver && key === 'r' || key === 'R') {
    resetGame();
  }
}
Line-by-line explanation (2 lines)

🔧 Subcomponents:

conditional Restart Key Check if (gameOver && key === 'r' || key === 'R') {

Checks if the game is over and the R or r key was pressed, then restarts

if (gameOver && key === 'r' || key === 'R') {
Checks if the game is over AND the player pressed 'r' or 'R' (note: operator precedence means this is: (gameOver && key === 'r') || (key === 'R'))
resetGame();
Calls the resetGame() function to clear the world and restart the game

resetGame()

resetGame() is called when the player presses 'R' after a game-over. It demonstrates how to fully reset game state—clearing physics bodies, resetting all variables, and re-adding the persistent objects. This pattern is useful for any game that needs to restart.

function resetGame() {
  // Clear all bodies from the world except ground and paddle
  World.clear(world, false);
  stars = [];
  score = 0;
  lives = 3;
  starSpawnInterval = 1000;
  lastStarSpawnTime = millis();
  gameOver = false;
  
  // Re-add paddle and ground as they were cleared
  World.add(world, [paddle, ground]);
}
Line-by-line explanation (8 lines)

🔧 Subcomponents:

calculation Clear Physics World World.clear(world, false);

Removes all bodies from the physics world

calculation Reset Game State Variables stars = []; score = 0; lives = 3; starSpawnInterval = 1000; lastStarSpawnTime = millis(); gameOver = false;

Resets all game state variables to their initial values

World.clear(world, false);
Removes all physics bodies from the world (the 'false' parameter means don't clear the world's events/listeners)
stars = [];
Empties the stars array so there are no stars at the start
score = 0;
Resets score to 0
lives = 3;
Resets lives to 3
starSpawnInterval = 1000;
Resets spawn interval to its initial value (1000ms) so difficulty starts over
lastStarSpawnTime = millis();
Records the current time as the new baseline for spawn timing
gameOver = false;
Clears the gameOver flag so the game loop resumes updating
World.add(world, [paddle, ground]);
Re-adds the paddle and ground to the physics world since they were cleared in World.clear()

windowResized()

windowResized() is called automatically whenever the browser window is resized. It demonstrates responsive design in p5.js—the game adapts to fill the new window size, and the physics bodies are recreated to match. This ensures the paddle and ground always span the correct dimensions.

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
  // Recreate paddle and ground to match new canvas size
  World.remove(world, paddle);
  World.remove(world, ground);

  const paddleWidth = 150;
  const paddleHeight = 20;
  paddle = Bodies.rectangle(
    width / 2, 
    height - paddleHeight / 2 - 10, 
    paddleWidth, 
    paddleHeight, 
    { isStatic: true, label: 'paddle' }
  );
  World.add(world, paddle);

  ground = Bodies.rectangle(
    width / 2, 
    height + 50, 
    width, 
    100, 
    { isStatic: true, label: 'ground' }
  );
  World.add(world, ground);
}
Line-by-line explanation (7 lines)
resizeCanvas(windowWidth, windowHeight);
Resizes the p5.js canvas to match the new window dimensions
World.remove(world, paddle);
Removes the old paddle from the physics world
World.remove(world, ground);
Removes the old ground from the physics world
paddle = Bodies.rectangle(
Creates a new paddle at the center of the resized canvas
ground = Bodies.rectangle(
Creates a new ground that spans the width of the resized canvas
World.add(world, paddle);
Adds the new paddle to the physics world
World.add(world, ground);
Adds the new ground to the physics world

📦 Key Variables

engine object

The Matter.js physics engine that simulates gravity, velocity, and collisions for all bodies

let engine;
world object

The physics world owned by the engine—contains all bodies and gravity settings

let world;
paddle object

The static rectangular physics body controlled by the player's mouse that catches stars

let paddle;
ground object

An invisible static rectangular body below the canvas that detects missed stars

let ground;
stars array

An array storing references to all currently active star bodies for rendering and cleanup

let stars = [];
score number

The player's current score, incremented by 1 for each caught star

let score = 0;
lives number

The number of remaining lives—decremented when a star is missed, game ends when it reaches 0

let lives = 3;
starSpawnInterval number

The number of milliseconds between star spawns—decreases over time to increase difficulty

let starSpawnInterval = 1000;
lastStarSpawnTime number

The millisecond timestamp of the last star spawn, used to calculate when the next spawn should occur

let lastStarSpawnTime = 0;
gameOver boolean

A flag indicating whether the game has ended (lives <= 0)—when true, draw() stops updating and displays game-over screen

let gameOver = false;

🔧 Potential Improvements (6)

Here are some ways this code could be enhanced:

BUG keyPressed() function

Operator precedence bug: 'gameOver && key === 'r' || key === 'R'' is evaluated as '(gameOver && key === 'r') || (key === 'R')', which means pressing 'R' restarts the game even when NOT gameOver. The condition should use parentheses.

💡 Change to: if ((gameOver && key === 'r') || (gameOver && key === 'R')) or more concisely: if (gameOver && (key.toLowerCase() === 'r'))

BUG collisionHandler() function

The collision detection checks for both 'star-paddle' and 'star-ground' within the same loop iteration. If a star collides with multiple bodies in one frame, both conditions could fire. However, stars are removed from the stars array after the first hit, so this is mostly safe but not ideal.

💡 Add a 'continue' statement or restructure the logic to ensure each collision pair is only processed once per type.

PERFORMANCE collisionHandler() function and draw() loop

Using 'stars.filter()' to remove stars creates a new array every collision and every draw frame (for cleanup). This is inefficient for large star counts.

💡 Use a Set or maintain a 'toRemove' array and splice after iterating, or use 'stars.findIndex()' and splice directly.

FEATURE draw() function

The spawn interval decreases indefinitely, eventually reaching the minimum of 200ms. After several minutes of play, the minimum might still feel too fast or not fast enough depending on star size.

💡 Add a maximum difficulty level that caps spawn rate, or scale star size inversely with difficulty so smaller, harder-to-catch stars spawn instead.

STYLE setup() function

The paddle and ground are created with hard-coded dimensions (paddleWidth, paddleHeight) inside setup(). If a developer wants to change these, they must modify two places (setup and windowResized).

💡 Define paddleWidth and paddleHeight as global constants at the top of the sketch so they can be changed in one place and used everywhere.

FEATURE draw() function

Stars sometimes spawn outside the visible canvas if their random x position + radius extends beyond the width. The constrain in createStar() helps but isn't perfect.

💡 Use 'const x = random(starSize/2 + 5, width - starSize/2 - 5);' to ensure stars spawn completely on-screen with a small margin.

🔄 Code Flow

Code flow showing setup, draw, createstar, drawbody, collisionhandler, displaygameinfo, displaygameover, keypressed, resetgame, windowresized

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

graph TD start[Start] --> setup[setup] setup --> engine_init[Engine Initialization] setup --> paddle_creation[Paddle Creation] setup --> ground_creation[Ground Creation] setup --> draw[draw loop] click setup href "#fn-setup" click engine_init href "#sub-engine-init" click paddle_creation href "#sub-paddle-creation" click ground_creation href "#sub-ground-creation" draw --> gameover_check[Game Over Check] draw --> physics_update[Physics Update] draw --> paddle_movement[Paddle Movement] draw --> star_spawn[Star Spawn] draw --> star_rendering_loop[Star Rendering Loop] draw --> displaygameinfo[Display Game Info] draw --> displaygameover[Display Game Over] click draw href "#fn-draw" click gameover_check href "#sub-gameover-check" click physics_update href "#sub-physics-update" click paddle_movement href "#sub-paddle-movement" click star_spawn href "#sub-star-spawn" click star_rendering_loop href "#sub-star-rendering-loop" click displaygameinfo href "#fn-displaygameinfo" click displaygameover href "#fn-displaygameover" star_spawn --> star_size_generation[Star Size Generation] star_spawn --> star_position_generation[Star Position Generation] star_spawn --> star_body_creation[Star Body Creation] click star_size_generation href "#sub-star-size-generation" click star_position_generation href "#sub-star-position-generation" click star_body_creation href "#sub-star-body-creation" star_rendering_loop --> body_type_check[Body Type Check] star_rendering_loop --> star_paddle_collision[Star-Paddle Collision] star_rendering_loop --> star_ground_collision[Star-Ground Collision] click body_type_check href "#sub-body-type-check" click star_paddle_collision href "#sub-star-paddle-collision" click star_ground_collision href "#sub-star-ground-collision" gameover_check --> displaygameover gameover_check -->|if gameOver| draw gameover_check -->|else| physics_update physics_update --> draw keypressed[Key Pressed] --> restart_condition[Restart Condition] click keypressed href "#fn-keypressed" click restart_condition href "#sub-restart-condition" resetgame[Reset Game] --> world_clear[Clear Physics World] resetgame --> state_reset[Reset Game State Variables] click resetgame href "#fn-resetgame" click world_clear href "#sub-world-clear" click state_reset href "#sub-state-reset" windowresized[Window Resized] --> world_clear click windowresized href "#fn-windowresized"

❓ Frequently Asked Questions

What does Sketch 2026-03-02 23:21 create visually?

This sketch features a dark background with a paddle at the bottom and falling stars, creating a dynamic and engaging visual experience.

How can users interact with the Sketch 2026-03-02 23:21?

Users can control the paddle's position horizontally using their mouse to catch falling stars and score points.

What creative coding technique does Sketch 2026-03-02 23:21 demonstrate?

This sketch utilizes physics-based simulations with Matter.js to create interactive gameplay and collision detection.

Preview

Sketch 2026-03-02 23:21 - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of Sketch 2026-03-02 23:21 - Code flow showing setup, draw, createstar, drawbody, collisionhandler, displaygameinfo, displaygameover, keypressed, resetgame, windowresized
Code Flow Diagram