the ball game

This is a fast-paced catching game where players control a sliding box at the bottom of the screen and try to catch falling colorful balls. Each caught ball adds 10 points to the score, and the game continuously spawns new balls at increasing speeds.

🧪 Try This!

Experiment with the code by making these changes:

  1. Make balls spawn twice as fast — Changing 60 to 30 makes balls appear every 30 frames instead of 60, doubling the spawn rate and making the game much harder
  2. Triple the points per catch — Earning more points makes successful catches feel more rewarding and helps you build a higher score faster
  3. Make the player box huge and easier to catch with — Changing w: 80 to w: 200 makes the catching box 2.5 times wider, making the game much easier for beginners
  4. Make balls fall slower — Changing the maximum speed from 5 to 2.5 makes all balls fall at half speed, giving you more time to react
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch creates an interactive catching game powered by three core p5.js techniques: the draw loop for smooth animation, collision detection to check when balls touch the player's box, and mouse tracking to follow the player's movements. Colorful balls fall from the top of the screen at random positions and speeds, and the player must position their sliding box to catch them before they disappear at the bottom. It's an engaging way to see how game logic works in p5.js.

The code is organized into three functions: setup() initializes the player box and canvas, draw() handles all the animation and game logic each frame, and windowResized() keeps the game responsive when the window changes size. By studying this sketch, you'll learn how to spawn objects into arrays, update them each frame, detect collisions, and respond to mouse input—the same patterns used in thousands of games.

⚙️ How It Works

  1. When the sketch loads, setup() creates a canvas that fills the entire browser window and initializes the player object as a small blue rectangle positioned at the bottom center of the screen.
  2. Every frame, draw() clears the background with a dark color and spawns a new falling ball every 60 frames (about once per second) by pushing a new object with random position, size, speed, and color into the items array.
  3. For each ball in the items array, the code increases its y position to make it fall, draws it as a colored ellipse, and checks if it has collided with the player's box using a distance comparison.
  4. When a collision is detected, the ball is removed from the array using splice() and the score increases by 10 points, giving immediate feedback to the player.
  5. The player's box is drawn at the current mouse position (constrained to stay within the canvas width) so it always follows the cursor horizontally.
  6. Finally, the current score is displayed in the top-left corner, and any balls that fall below the screen are automatically removed to keep the items array from growing infinitely.

🎓 Concepts You'll Learn

Game loop and frame-based animationObject arrays and spawningCollision detectionMouse tracking and inputConditional logic and game state

📝 Code Breakdown

setup()

setup() runs once when the sketch starts. We use it to create the canvas and initialize the player object with all the properties we'll need during the game.

function setup() {
  createCanvas(windowWidth, windowHeight);
  player = { x: width / 2, y: height - 50, w: 80, h: 20 };
}
Line-by-line explanation (2 lines)
createCanvas(windowWidth, windowHeight);
Creates a canvas that fills the entire browser window, making the game responsive to different screen sizes
player = { x: width / 2, y: height - 50, w: 80, h: 20 };
Creates a player object with properties: x and y for position, w for width, h for height - the player starts centered horizontally and 50 pixels from the bottom

draw()

draw() is the heart of the game - it runs 60 times per second and handles spawning, animation, collision, and rendering. Everything you see on screen comes from code in this function.

🔬 This collision detection checks three conditions: the ball's bottom has reached the player's top, AND the ball is horizontally within the box. What happens if you remove one of these conditions (like delete the item.x > player.x line) and see if balls get caught without proper overlap?

    // Check collision with player
    if (item.y + item.size/2 > player.y && 
        item.x > player.x && 
        item.x < player.x + player.w) {
      score += 10;
      items.splice(i, 1);
    }

🔬 The loop counts backward (i--) instead of forward. What would happen if you changed it to i++ (forward counting) and then try to remove items during the loop? Try it and notice if the game behaves strangely.

  for (let i = items.length - 1; i >= 0; i--) {
    let item = items[i];
    item.y += item.speed;
function draw() {
  background(30, 30, 40);
  
  // Spawn items
  if (frameCount % 60 === 0) {
    items.push({
      x: random(20, width - 20),
      y: 0,
      size: random(15, 30),
      speed: random(2, 5),
      color: color(random(255), random(255), random(255))
    });
  }
  
  // Update & draw items
  for (let i = items.length - 1; i >= 0; i--) {
    let item = items[i];
    item.y += item.speed;
    
    fill(item.color);
    noStroke();
    ellipse(item.x, item.y, item.size);
    
    // Check collision with player
    if (item.y + item.size/2 > player.y && 
        item.x > player.x && 
        item.x < player.x + player.w) {
      score += 10;
      items.splice(i, 1);
    }
    
    // Remove if off screen
    if (item.y > height) {
      items.splice(i, 1);
    }
  }
  
  // Draw player
  player.x = mouseX - player.w / 2;
  player.x = constrain(player.x, 0, width - player.w);
  fill(100, 200, 255);
  rect(player.x, player.y, player.w, player.h, 5);
  
  // Score
  fill(255);
  textSize(24);
  textAlign(LEFT, TOP);
  text('Score: ' + score, 20, 20);
}
Line-by-line explanation (20 lines)

🔧 Subcomponents:

conditional Spawn items every 60 frames if (frameCount % 60 === 0) {

Every 60 frames (about once per second), a new ball is created and added to the items array with random properties

for-loop Update and draw all falling items for (let i = items.length - 1; i >= 0; i--) {

Loops through every ball in the items array backwards so we can safely remove items without breaking the loop

conditional Collision detection with player if (item.y + item.size/2 > player.y && item.x > player.x && item.x < player.x + player.w) {

Tests if the ball's bottom has reached the player's top AND if the ball's horizontal position overlaps with the player's box

calculation Mouse tracking and boundary constraint player.x = constrain(player.x, 0, width - player.w);

Keeps the player box centered on the mouse but prevents it from sliding off the left or right edges of the screen

background(30, 30, 40);
Fills the entire canvas with a dark blue-gray color each frame, erasing the previous frame's drawing
if (frameCount % 60 === 0) {
frameCount is a p5.js variable that counts frames since the sketch started - this runs once every 60 frames (about once per second)
items.push({
Creates a new ball object and adds it to the items array using push()
x: random(20, width - 20),
Randomly places the ball anywhere horizontally on the canvas with a 20-pixel margin from each edge
y: 0,
All new balls start at the top of the canvas (y = 0) and fall downward
size: random(15, 30),
Each ball has a random size between 15 and 30 pixels in diameter, making gameplay less predictable
speed: random(2, 5),
Each ball falls at a random speed between 2 and 5 pixels per frame
color: color(random(255), random(255), random(255))
Each ball gets a random RGB color with red, green, and blue values all between 0 and 255
for (let i = items.length - 1; i >= 0; i--) {
Loops through the items array backwards (from the end to the beginning) - this is important because we'll remove items during the loop
item.y += item.speed;
Moves each ball downward by adding its speed value to its y position every frame
ellipse(item.x, item.y, item.size);
Draws each ball as a colored circle at its current position using the fill color set above
if (item.y + item.size/2 > player.y &&
Checks if the bottom of the ball (y position plus half its size) has reached the top of the player's box
item.x > player.x &&
Checks if the ball is to the right of the left edge of the player's box
item.x < player.x + player.w) {
Checks if the ball is to the left of the right edge of the player's box - all three conditions must be true for a collision
items.splice(i, 1);
Removes the caught ball from the items array so it stops falling and disappears from the screen
if (item.y > height) {
Checks if the ball has fallen below the bottom of the screen
player.x = mouseX - player.w / 2;
Centers the player box on the mouse position by subtracting half the box's width, so the mouse cursor appears in the middle of the box
player.x = constrain(player.x, 0, width - player.w);
Prevents the player box from moving off-screen by clamping its x position between 0 and width - player.w
rect(player.x, player.y, player.w, player.h, 5);
Draws the player as a rounded rectangle (the 5 makes slightly rounded corners) at its current position
text('Score: ' + score, 20, 20);
Displays the current score as white text in the top-left corner of the screen

windowResized()

windowResized() is a p5.js built-in function that p5 calls automatically whenever the browser window changes size. Without it, the canvas would stay its original size and look broken when resized. This is essential for modern web games.

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
}
Line-by-line explanation (1 lines)
resizeCanvas(windowWidth, windowHeight);
Automatically resizes the p5.js canvas whenever the browser window is made larger or smaller, keeping the game responsive

📦 Key Variables

player object

Stores the player's box with properties: x (horizontal position), y (vertical position), w (width), and h (height)

let player = { x: width / 2, y: height - 50, w: 80, h: 20 };
items array

Holds all the falling balls currently on screen - each item is an object with x, y, size, speed, and color properties

let items = [];
score number

Tracks the player's total points earned by catching balls

let score = 0;

🔧 Potential Improvements (4)

Here are some ways this code could be enhanced:

BUG draw() collision detection

The collision check only tests if the ball's center (x position) is within the player box horizontally, not the ball's edges. A partially overlapping ball might not register as a collision.

💡 Change the collision condition to account for the ball's radius: `if (item.y + item.size/2 > player.y && item.x - item.size/2 < player.x + player.w && item.x + item.size/2 > player.x)`

PERFORMANCE draw() - color generation in spawn logic

Creating a new color object every 60 frames with `color(random(255), random(255), random(255))` is inefficient - the color() function is called in the spawn condition.

💡 The current code is actually acceptable, but for better practice, you could pre-generate color values using just RGB values: `color: [random(255), random(255), random(255)]` and apply them with `fill(item.color[0], item.color[1], item.color[2])`

FEATURE Global scope

There is no game over condition, maximum score display, or difficulty progression - the game plays identically forever

💡 Add a high score tracker (`let highScore = 0`), a game over state when lives are lost, or increase spawn rate based on score: `if (frameCount % constrain(60 - score/500, 20, 60) === 0)`

STYLE draw() - magic numbers

Hardcoded values like 20, 30, 15, 30, 2, 5 for ball spawning are scattered throughout the code and hard to adjust

💡 Define constants at the top: `const MIN_SIZE = 15, MAX_SIZE = 30, MIN_SPEED = 2, MAX_SPEED = 5, MARGIN = 20` and use them in spawn logic for easier tuning

🔄 Code Flow

Code flow showing setup, draw, windowresized

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

graph TD start[Start] --> setup[setup] setup --> draw[draw loop] draw --> spawnlogic[spawn-logic] draw --> updateloop[update-loop] draw --> collisioncheck[collision-check] draw --> playertracking[player-tracking] spawnlogic --> draw updateloop --> collisioncheck updateloop --> draw collisioncheck --> updateloop playertracking --> draw click setup href "#fn-setup" click draw href "#fn-draw" click spawnlogic href "#sub-spawn-logic" click updateloop href "#sub-update-loop" click collisioncheck href "#sub-collision-check" click playertracking href "#sub-player-tracking"

❓ Frequently Asked Questions

What visual elements are featured in the ball game sketch?

The sketch visually presents a sliding player box at the bottom of the screen and colorful falling balls that players aim to catch.

How can players interact with the ball game?

Players control the movement of the player box using their mouse to catch the falling balls and increase their score.

What coding concepts does this p5.js sketch illustrate?

This sketch demonstrates object manipulation, collision detection, and dynamic item generation within a creative coding context.

Preview

the ball game - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of the ball game - Code flow showing setup, draw, windowresized
Code Flow Diagram