Sketch 2026-02-14 10;08

This sketch is a catch-the-falling-objects game where the player controls a blue circle at the bottom of the screen to catch red circles falling from above. Catching points increases your score, but missing them decreases it—the game mechanics are designed to teach persistence and never giving up.

🧪 Try This!

Experiment with the code by making these changes:

  1. Make the player move faster — The player will respond more snappily to arrow key input, making it easier to catch points
  2. Make falling points bigger — Larger objects are easier to catch, making the game less frustrating
  3. Make points fall slower — Slower falling objects give you more time to react and position the player
  4. Award more points per catch — Catching a point now increases your score by 5 instead of 1, making progression feel faster
  5. Turn the canvas background dark — A darker background makes the game feel more dramatic and the red points stand out more
  6. Make the player red instead of blue — Swapping the player color makes it visually different from the falling points
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch creates an interactive catch game where red circles fall from the top of the screen and you must move a blue player circle side-to-side to catch them. It is a hands-on introduction to game development in p5.js because it combines arrays of falling objects, collision detection with the dist() function, keyboard and mouse input handling, and a game-over state that requires resetting. The game punishes missed catches with score penalties, embodying the theme that persistence matters.

The code is organized into a setup() function that initializes the player and falling points, a draw() loop that updates positions and detects collisions every frame, helper functions to create and manage falling objects, and input handlers for keyboard and window resizing. By studying this sketch, you will learn how to track many moving objects in an array, detect when they collide with the player, remove and replace them dynamically, and implement a restart mechanic—skills that power nearly every interactive game.

⚙️ How It Works

  1. When the sketch loads, setup() creates a responsive canvas and initializes the player object (a blue circle at the bottom-center) and an array of 10 falling points with random positions above the canvas.
  2. Every frame, draw() clears the background and checks if the game is still active. If so, it updates the player's position based on mouse X and arrow key input, then draws the blue player circle.
  3. For each falling point, the code increases its y position to make it fall downward, draws it as a red circle, and checks if it has collided with the player using the dist() function.
  4. If a collision is detected, the score increases by 1, the caught point is removed from the array, and a new point is created to replace it.
  5. If a point falls off the bottom of the screen, it is removed and replaced with a new point, but the score decreases by 1 as a penalty, teaching the player that missed catches have consequences.
  6. The score is displayed at the top-left each frame. When the player presses 'R', resetGame() clears the points array, resets the score, and restarts the game.

🎓 Concepts You'll Learn

Game loop and state managementArrays and dynamic object creationCollision detection with dist()Keyboard and mouse input handlingObject properties and methodsResponsive canvas sizing

📝 Code Breakdown

setup()

setup() runs once when the sketch loads. It is where you initialize your canvas, variables, and any objects you need before the game starts. The player object uses curly braces to bundle related data together—this pattern is called an object literal and is fundamental to organizing game code.

function setup() {
  createCanvas(windowWidth, windowHeight);
  // Tip: Use windowWidth/windowHeight for responsive canvas
  
  // Initialize player at the bottom center
  player = {
    x: width / 2,
    y: height - 50,
    size: 40,
    speed: 8
  };
  
  // Create an initial set of points
  for (let i = 0; i < 10; i++) {
    createPoint();
  }
}
Line-by-line explanation (8 lines)

🔧 Subcomponents:

object-creation Player Object Initialization player = { x: width / 2, y: height - 50, size: 40, speed: 8 };

Creates a player object with position, size, and movement speed properties

for-loop Create Initial Points for (let i = 0; i < 10; i++) { createPoint(); }

Populates the game with 10 falling objects at the start

createCanvas(windowWidth, windowHeight);
Creates a full-screen canvas that responds to window size—windowWidth and windowHeight are p5.js variables that update when the window resizes
player = {
Starts defining the player as a JavaScript object that will hold all its properties in one place
x: width / 2,
Sets the player's horizontal position to the horizontal center of the canvas
y: height - 50,
Places the player near the bottom of the canvas, 50 pixels up from the bottom edge
size: 40,
The player circle will have a diameter of 40 pixels
speed: 8
The player moves 8 pixels per frame when arrow keys are held down
for (let i = 0; i < 10; i++) {
Loops 10 times to create an initial batch of falling points
createPoint();
Calls the createPoint() helper function, which adds one random point to the points array

draw()

draw() runs 60 times per second, making it the heartbeat of your game. Every frame, it updates all moving objects, checks for collisions, and redraws the canvas. The backwards loop (i >= 0, i--) is a critical pattern when removing array items—forward loops would skip items after removal.

🔬 This block detects collisions. What happens if you change the '<' to '>' so the collision activates when the player is FAR from the point instead of close? Or what if you increase the threshold value to make catching easier?

      // Check for collision with player
      let d = dist(player.x, player.y, p.x, p.y);
      if (d < player.size / 2 + p.size / 2) {
        score++; // Increase score
        points.splice(i, 1); // Remove caught point
        createPoint(); // Create a new point
      }

🔬 Currently, missing a point costs you 1 point. What if you make it cost more? Or what if you change 'score--' to 'gameOver = true' to end the game on the first miss?

      // Remove points that fall off screen
      if (p.y > height + p.size / 2) {
        points.splice(i, 1); // Remove missed point
        createPoint(); // Create a new point
        score--; // Penalty for missing a point
function draw() {
  background(220); // Light gray background
  
  if (!gameOver) {
    // Update player position based on mouse or keyboard
    updatePlayer();

    // Draw player
    fill(50, 150, 250); // Blue player
    noStroke();
    ellipse(player.x, player.y, player.size);

    // Update and draw points
    for (let i = points.length - 1; i >= 0; i--) {
      let p = points[i];
      p.y += p.speed; // Make point fall
      
      // Draw point
      fill(250, 100, 100); // Red points
      ellipse(p.x, p.y, p.size);

      // Check for collision with player
      let d = dist(player.x, player.y, p.x, p.y);
      if (d < player.size / 2 + p.size / 2) {
        score++; // Increase score
        points.splice(i, 1); // Remove caught point
        createPoint(); // Create a new point
      }

      // Remove points that fall off screen
      if (p.y > height + p.size / 2) {
        points.splice(i, 1); // Remove missed point
        createPoint(); // Create a new point
        score--; // Penalty for missing a point
        if (score < 0) {
          score = 0; // Don't let score go negative for now
        }
      }
    }
    
    // Display score
    fill(0);
    textSize(32);
    textAlign(LEFT, TOP);
    text(`Score: ${score}`, 10, 10);
  } else {
    // Game Over screen
    fill(0);
    textSize(64);
    textAlign(CENTER, CENTER);
    text("GAME OVER!", width / 2, height / 2 - 40);
    textSize(32);
    text(`Final Score: ${score}`, width / 2, height / 2 + 20);
    text("Press 'R' to Restart", width / 2, height / 2 + 70);
  }
}
Line-by-line explanation (20 lines)

🔧 Subcomponents:

conditional Game Active State Check if (!gameOver) {

Branches gameplay logic when active, or shows the game-over screen when the game has ended

for-loop Update and Draw Falling Points for (let i = points.length - 1; i >= 0; i--) {

Iterates backwards through the array so items can be safely removed during the loop

conditional Collision Detection if (d < player.size / 2 + p.size / 2) {

Detects if a falling point touches the player using distance-based collision

conditional Missed Point Detection if (p.y > height + p.size / 2) {

Removes points that fall below the visible canvas and penalizes the player

background(220); // Light gray background
Clears the entire canvas with light gray color each frame, erasing all previous drawings so animation is smooth
if (!gameOver) {
Checks if the game is still running—the exclamation mark means 'NOT', so this block runs while gameOver is false
updatePlayer();
Calls the helper function that updates the player's position based on keyboard and mouse input
fill(50, 150, 250); // Blue player
Sets the fill color to blue (RGB: 50 red, 150 green, 250 blue) for all shapes drawn after this line
ellipse(player.x, player.y, player.size);
Draws the player as a circle at its current x and y position with diameter player.size
for (let i = points.length - 1; i >= 0; i--) {
Loops backwards through the points array (from last to first)—this is crucial because we will remove items during the loop
let p = points[i];
Creates a shorter variable name 'p' to reference the current point, making the code inside the loop more readable
p.y += p.speed; // Make point fall
Increases the point's y position by its speed, moving it downward each frame
fill(250, 100, 100); // Red points
Changes the fill color to red for the points being drawn
ellipse(p.x, p.y, p.size);
Draws the point as a circle at its current position
let d = dist(player.x, player.y, p.x, p.y);
Calculates the distance in pixels between the player's center and the point's center—dist() is a p5.js function
if (d < player.size / 2 + p.size / 2) {
Checks if the distance is less than the sum of their radii (half-sizes)—if true, they are touching
score++; // Increase score
Adds 1 to the score when a point is caught
points.splice(i, 1); // Remove caught point
Removes the caught point from the array at index i (splice removes 1 item starting at position i)
createPoint(); // Create a new point
Adds a fresh falling point to the array to keep the game flowing
if (p.y > height + p.size / 2) {
Checks if the point has fallen below the canvas bottom—points that are not caught eventually leave the screen
points.splice(i, 1); // Remove missed point
Removes the missed point from the array
score--; // Penalty for missing a point
Subtracts 1 from the score as punishment, encouraging the player to catch falling objects
if (score < 0) {
Prevents the score from going negative—it stops at 0
text(`Score: ${score}`, 10, 10);
Displays the current score at the top-left corner using template literals (the backtick syntax) to insert the score value

updatePlayer()

This function handles input—both mouse and keyboard. It runs every frame as part of draw(), so the player position updates smoothly. Notice that mouse control (player.x = mouseX) happens every frame and can override keyboard movement if both are used; this is a game design choice you could change.

🔬 These two if-statements let you move left and right with arrow keys. What happens if you change '-=' to '+=' on the left arrow? Or what if you multiply player.speed by 2 to make keyboard movement faster than mouse control?

  // Keyboard control (left/right arrow keys)
  if (keyIsDown(LEFT_ARROW)) {
    player.x -= player.speed;
  }
  if (keyIsDown(RIGHT_ARROW)) {
    player.x += player.speed;
  }
function updatePlayer() {
  // Mouse control
  player.x = mouseX;
  
  // Keyboard control (left/right arrow keys)
  if (keyIsDown(LEFT_ARROW)) {
    player.x -= player.speed;
  }
  if (keyIsDown(RIGHT_ARROW)) {
    player.x += player.speed;
  }
  
  // Constrain player to canvas width
  player.x = constrain(player.x, player.size / 2, width - player.size / 2);
}
Line-by-line explanation (6 lines)

🔧 Subcomponents:

conditional Left Arrow Input if (keyIsDown(LEFT_ARROW)) { player.x -= player.speed; }

Moves the player left when the left arrow key is held

conditional Right Arrow Input if (keyIsDown(RIGHT_ARROW)) { player.x += player.speed; }

Moves the player right when the right arrow key is held

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

Prevents the player from moving off the left or right edge of the canvas

player.x = mouseX;
Sets the player's x position to follow the mouse's x position—mouseX is a built-in p5.js variable that updates continuously
if (keyIsDown(LEFT_ARROW)) {
Checks if the left arrow key is currently being held down—keyIsDown() lets you detect continuous key presses
player.x -= player.speed;
Subtracts the speed value from the player's x, moving it left (lower x values are further left)
if (keyIsDown(RIGHT_ARROW)) {
Checks if the right arrow key is being held down
player.x += player.speed;
Adds the speed value to x, moving the player right
player.x = constrain(player.x, player.size / 2, width - player.size / 2);
The constrain() function clamps the player's x between a minimum (player.size/2 to account for width) and maximum (width - player.size/2) so it stays fully on screen

createPoint()

Factory functions like this one create objects on demand. By bundling all point properties into one object and using push() to add it to an array, you can manage many falling objects with just one loop in draw(). This pattern is fundamental to game development.

function createPoint() {
  let p = {
    x: random(width),
    y: -20, // Start above the canvas
    size: random(10, 30),
    speed: random(2, 6)
  };
  points.push(p);
}
Line-by-line explanation (6 lines)

🔧 Subcomponents:

object-creation Point Object Creation let p = { x: random(width), y: -20, // Start above the canvas size: random(10, 30), speed: random(2, 6) };

Creates a new point with random horizontal position, size, and falling speed

calculation Add to Array points.push(p);

Appends the new point to the points array so it will be drawn and updated in the next draw() loop

let p = {
Declares a new local variable 'p' that will hold one point object
x: random(width),
Picks a random x position anywhere across the canvas width—random(width) returns a number from 0 to width-1
y: -20, // Start above the canvas
Starts the point 20 pixels above the top of the canvas (negative y) so it falls into view smoothly
size: random(10, 30),
Gives each point a random diameter between 10 and 30 pixels—variety makes the game more interesting
speed: random(2, 6)
Each point falls at a random speed between 2 and 6 pixels per frame—faster points are harder to catch
points.push(p);
The push() method adds the new point object to the end of the points array, making it eligible for drawing and collision checking

keyPressed()

keyPressed() is different from keyIsDown()—it fires once per key press, not continuously. Use it for events like game restart, pausing, or shooting, while keyIsDown() is better for continuous movement.

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

🔧 Subcomponents:

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

Detects when 'R' or 'r' is pressed while the game is over, triggering a restart

function keyPressed() {
This is a special p5.js function that runs automatically whenever ANY key is pressed—use it for one-time key presses (unlike keyIsDown which checks continuously)
if (gameOver && key === 'r' || key === 'R') {
Checks if the game is over AND the key pressed is 'r' (lowercase) OR 'R' (uppercase)—the '||' means OR
resetGame();
Calls the resetGame() function to restart the game

resetGame()

resetGame() restores all variables to their starting state. It's called when the player presses 'R' on the game-over screen. Notice it re-runs the same initialization that happens in setup()—this is a pattern you can use any time you want to 'restart' a game or reset a level.

function resetGame() {
  score = 0;
  points = [];
  for (let i = 0; i < 10; i++) {
    createPoint();
  }
  gameOver = false;
}
Line-by-line explanation (5 lines)

🔧 Subcomponents:

calculation Reset Score score = 0;

Sets the score back to 0

calculation Clear Points Array points = [];

Empties the array so old falling points disappear

for-loop Repopulate Points for (let i = 0; i < 10; i++) { createPoint(); }

Creates 10 fresh falling points for the new game

calculation Reactivate Game gameOver = false;

Sets gameOver to false so draw() returns to normal gameplay instead of showing the game-over screen

score = 0;
Resets the score counter to 0
points = [];
Replaces the points array with an empty array, instantly removing all falling objects from the screen
for (let i = 0; i < 10; i++) {
Loops 10 times to populate the array with fresh points
createPoint();
Creates one new random point and pushes it into the array each iteration
gameOver = false;
Sets gameOver back to false, which tells draw() to show gameplay instead of the game-over screen

windowResized()

windowResized() is critical for responsive games—without it, your game breaks when the window resizes. Notice it recalculates width and height based on the new window size, then repositions objects accordingly. This is why the sketch uses windowWidth and windowHeight in setup() instead of hardcoded pixel values.

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
  // Called when preview panel is resized
  
  // Re-position player and update points for new canvas size
  player.x = width / 2;
  player.y = height - 50;
  
  // Keep points within new bounds or regenerate
  for (let p of points) {
    p.x = constrain(p.x, p.size / 2, width - p.size / 2);
  }
}
Line-by-line explanation (6 lines)

🔧 Subcomponents:

calculation Resize Canvas resizeCanvas(windowWidth, windowHeight);

Updates the canvas size to match the new window dimensions

calculation Reposition Player player.x = width / 2; player.y = height - 50;

Moves the player back to the center-bottom when the window size changes

for-loop Constrain Points to New Bounds for (let p of points) { p.x = constrain(p.x, p.size / 2, width - p.size / 2); }

Ensures all falling points stay within the new canvas width

function windowResized() {
This is a special p5.js function that runs automatically whenever the browser window is resized
resizeCanvas(windowWidth, windowHeight);
Updates the canvas to fill the new window size—without this call, the canvas would stay the old size
player.x = width / 2;
Recenters the player horizontally based on the new canvas width
player.y = height - 50;
Repositions the player vertically based on the new canvas height
for (let p of points) {
Loops through each point in the array using the modern for...of syntax, which is cleaner than index-based loops
p.x = constrain(p.x, p.size / 2, width - p.size / 2);
Makes sure each point's x position stays within the new canvas bounds so it does not get stuck off-screen

📦 Key Variables

player object

Stores the player's position (x, y), size, and movement speed. Updated by updatePlayer() and used for drawing and collision detection.

let player = { x: 200, y: 350, size: 40, speed: 8 };
points array

Holds all currently falling point objects. Items are added by createPoint() and removed when caught or missed.

let points = [];
score number

Tracks the player's current score. Increased when points are caught, decreased when points are missed.

let score = 0;
gameOver boolean

Boolean flag that switches between gameplay and the game-over screen. True means the game has ended.

let gameOver = false;

🔧 Potential Improvements (5)

Here are some ways this code could be enhanced:

BUG keyPressed() function

The condition 'gameOver && key === 'r' || key === 'R'' uses operator precedence incorrectly. Because && has higher precedence than ||, this evaluates as '(gameOver && key === 'r') || (key === 'R')', meaning pressing 'R' when the game is active will restart it even though the game is not over.

💡 Use parentheses to fix precedence: if ((gameOver && key === 'r') || (gameOver && key === 'R')) or simplify to: if (gameOver && (key === 'r' || key === 'R'))

FEATURE draw() function

There is no game-over condition implemented. The sketch initializes gameOver to false but never sets it to true, so the game-over screen is unreachable.

💡 Add a trigger to end the game, such as: if (score < 0) { gameOver = true; } or implement a lives system that counts down on misses.

STYLE updatePlayer() function

Mouse control (player.x = mouseX) is applied every frame, then keyboard input is applied after. This means mouse position takes priority and can feel unresponsive to keyboard input.

💡 Either allow the player to choose between mouse OR keyboard at game start, or remove one of the input methods for clarity. Consider: if (mouseIsPressed) { player.x = mouseX; } to only use mouse when clicked.

PERFORMANCE draw() function

The fill() and text functions are called every frame for score display even though the text content rarely changes, creating unnecessary draw calls.

💡 Extract repeated fill() and textSize() calls to the top of draw() or cache them as variables to avoid redundant function calls.

FEATURE Global scope

There is no difficulty progression—the game stays at the same speed indefinitely, which can become boring after catching many points.

💡 Add a difficulty counter (e.g., add 0.1 to point speeds every 10 points caught) or spawn more points as the score increases to create escalating challenge.

🔄 Code Flow

Code flow showing setup, draw, updateplayer, createpoint, keypressed, resetgame, windowresized

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

graph TD start[Start] --> setup[setup] setup --> draw[draw loop] draw --> updateplayer[updateplayer] draw --> game-active-check[game-active-check] draw --> points-loop[points-loop] draw --> miss-check[miss-check] updateplayer --> keyboard-left[keyboard-left] updateplayer --> keyboard-right[keyboard-right] updateplayer --> constrain-bounds[constrain-bounds] game-active-check -->|Active| points-loop game-active-check -->|Game Over| resetgame[resetgame] points-loop --> collision-check[collision-check] points-loop --> point-object[point-object] points-loop --> array-push[array-push] collision-check -->|Collision Detected| reset-score[reset-score] collision-check -->|No Collision| points-loop miss-check -->|Point Missed| clear-points[clear-points] resetgame --> reset-score resetgame --> clear-points resetgame --> repopulate-points[repopulate-points] resetgame --> set-game-active[set-game-active] windowresized --> canvas-resize[canvas-resize] windowresized --> reposition-player[reposition-player] windowresized --> constrain-points[constrain-points] click setup href "#fn-setup" click draw href "#fn-draw" click updateplayer href "#fn-updateplayer" click resetgame href "#fn-resetgame" click windowresized href "#fn-windowresized" click game-active-check href "#sub-game-active-check" click points-loop href "#sub-points-loop" click collision-check href "#sub-collision-check" click miss-check href "#sub-miss-check" click keyboard-left href "#sub-keyboard-left" click keyboard-right href "#sub-keyboard-right" click constrain-bounds href "#sub-constrain-bounds" click point-object href "#sub-point-object" click array-push href "#sub-array-push" click reset-score href "#sub-reset-score" click clear-points href "#sub-clear-points" click repopulate-points href "#sub-repopulate-points" click set-game-active href "#sub-set-game-active" click canvas-resize href "#sub-canvas-resize" click reposition-player href "#sub-reposition-player" click constrain-points href "#sub-constrain-points"

❓ Frequently Asked Questions

What visual elements does the p5.js sketch display?

The sketch features a blue player character that moves horizontally at the bottom of the screen and red points that fall from the top, which the player must catch.

How can users interact with this creative coding game?

Users can control the player's position using the mouse or keyboard to catch falling points and increase their score.

What key concept does this p5.js sketch illustrate in creative coding?

This sketch demonstrates collision detection and score tracking, showcasing how interactive elements can engage users in a game-like environment.

Preview

Sketch 2026-02-14 10;08 - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of Sketch 2026-02-14 10;08 - Code flow showing setup, draw, updateplayer, createpoint, keypressed, resetgame, windowresized
Code Flow Diagram