cool games

This sketch creates an interactive game where a blue circle follows your mouse and collects pink squares that appear randomly on the canvas. Each collection increases your score and spawns a new item to chase, creating a simple but engaging clicker game.

🧪 Try This!

Experiment with the code by making these changes:

  1. Make the player invisible and play blindfolded — Comment out the ellipse() line so you cannot see the blue circle—you must navigate by mouse feel alone and watch the item fly away
  2. Multiply your score by 10 for each item — Change score++ to add 10 instead of 1—collect fewer items but rack up points faster
  3. Spawn items only in the upper half of the screen — Modify placeNewItem() so items appear above the middle line, making them easier to reach
  4. Make the item glow by adding a stroke — Replace noStroke() with a colored stroke before drawing the item—watch a border appear around the pink square
  5. Reverse the colors so the item is blue and player is pink — Swap the fill() colors between player and item—the roles flip visually but gameplay is identical
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch is a playable game that teaches three essential p5.js skills: tracking the mouse position in real time, detecting when two objects collide using the distance formula, and organizing game state with score. The blue circle you control instantly mirrors your cursor movement, while pink squares appear randomly across the canvas. Touch a square and your score climbs by one—then a fresh square spawns elsewhere, challenging you to keep collecting.

The code is organized into three functions: setup() initializes the canvas and places the first item, draw() runs 60 times per second updating the player position and checking for collisions, and placeNewItem() handles the random spawning logic. A fourth function, windowResized(), ensures the game adapts when the window changes size. By studying this sketch, you will learn how to build the skeleton of any interactive game in p5.js.

⚙️ How It Works

  1. When the sketch loads, setup() creates a full-window canvas, centers the player circle, places the first pink square at a random position, and configures text styling for the score display.
  2. Every frame, draw() clears the background and updates the player's position to match your mouse cursor using mouseX and mouseY.
  3. The player (blue circle) and item (pink square) are drawn at their current positions with fill colors and noStroke().
  4. A distance calculation checks whether the player's center is close enough to the item's center to trigger a collision—if they touch, the score increments and placeNewItem() randomly repositions the square.
  5. The current score is displayed as text in the top-left corner so you can track your progress.
  6. If you resize the window, windowResized() automatically adapts the canvas size and ensures both the player and item stay visible on the new canvas.

🎓 Concepts You'll Learn

Mouse trackingCollision detectionDistance formulaGame loopsScore trackingRandom positioningCanvas resizing

📝 Code Breakdown

setup()

setup() runs once when the sketch starts. It initializes all variables, configures the canvas size, and prepares text settings. Think of it as the game's startup phase.

function setup() {
  createCanvas(windowWidth, windowHeight);
  // Set initial player position to the center of the canvas
  playerX = width / 2;
  playerY = height / 2;

  // Place the first item at a random position
  placeNewItem();
  
  // Set text properties for score display
  textSize(32);
  textAlign(LEFT, TOP);
}
Line-by-line explanation (6 lines)

🔧 Subcomponents:

calculation Canvas Setup createCanvas(windowWidth, windowHeight);

Creates a canvas that fills the entire browser window

calculation Player Initialization playerX = width / 2; playerY = height / 2;

Places the player circle in the center of the canvas at game start

calculation Text Configuration textSize(32); textAlign(LEFT, TOP);

Sets score text to be large and positioned from the top-left

createCanvas(windowWidth, windowHeight);
Creates a canvas that dynamically sizes to your entire browser window, so the game fills the screen
playerX = width / 2;
Sets the player's starting X position to the horizontal center of the canvas
playerY = height / 2;
Sets the player's starting Y position to the vertical center of the canvas
placeNewItem();
Calls the helper function to spawn the first item at a random location
textSize(32);
Makes the score text 32 pixels tall so it is easy to read
textAlign(LEFT, TOP);
Aligns text from its top-left corner instead of center, positioning it neatly at coordinate (10, 10)

draw()

draw() is the heartbeat of the game—it runs 60 times per second and contains every frame's logic: updating positions, drawing shapes, checking collisions, and updating the display. This is where the magic happens every single frame.

🔬 This line calculates whether the player and item are touching. What happens if you change the < to > so collisions trigger when they are FAR apart instead of close together? What about changing the number to 0 to make collision require exact overlap?

  // Check for collision between player and item
  let d = dist(playerX, playerY, itemX, itemY);
  // If the distance between their centers is less than half their combined sizes, they are colliding
  if (d < playerSize / 2 + itemSize / 2) {

🔬 Right now the player's position equals the mouse position instantly. What happens if you change = to += so the player moves TOWARD the mouse but lags behind? Try: playerX += (mouseX - playerX) * 0.1; for a smooth chase effect.

  // Update player position to follow the mouse
  playerX = mouseX;
  playerY = mouseY;
function draw() {
  background(220); // Light gray background

  // Update player position to follow the mouse
  playerX = mouseX;
  playerY = mouseY;

  // Draw the player (a circle)
  fill(0, 150, 255); // Blue color for player
  noStroke();
  ellipse(playerX, playerY, playerSize, playerSize);

  // Draw the item (a square)
  fill(255, 0, 100); // Pink color for item
  noStroke();
  rectMode(CENTER); // Draw rectangle from its center
  rect(itemX, itemY, itemSize, itemSize);

  // Check for collision between player and item
  let d = dist(playerX, playerY, itemX, itemY);
  // If the distance between their centers is less than half their combined sizes, they are colliding
  if (d < playerSize / 2 + itemSize / 2) {
    score++; // Increase score
    placeNewItem(); // Move item to a new random position
  }

  // Display the score
  fill(0); // Black text
  text("Score: " + score, 10, 10); // Show score at top-left
}
Line-by-line explanation (15 lines)

🔧 Subcomponents:

calculation Mouse Tracking playerX = mouseX; playerY = mouseY;

Makes the player circle instantly follow your cursor position

calculation Player Drawing fill(0, 150, 255); // Blue color for player noStroke(); ellipse(playerX, playerY, playerSize, playerSize);

Draws the blue circle at the player's current position

calculation Item Drawing fill(255, 0, 100); // Pink color for item noStroke(); rectMode(CENTER); // Draw rectangle from its center rect(itemX, itemY, itemSize, itemSize);

Draws the pink square at the item's current position

conditional Collision Detection let d = dist(playerX, playerY, itemX, itemY); if (d < playerSize / 2 + itemSize / 2) { score++; placeNewItem(); }

Measures distance between centers and triggers score increment and item respawn when they overlap

calculation Score Rendering fill(0); // Black text text("Score: " + score, 10, 10);

Displays the current score as black text in the top-left corner

background(220);
Clears the canvas with light gray every frame, erasing all previous drawings so nothing trails behind
playerX = mouseX;
Updates the player's X position to your cursor's horizontal location
playerY = mouseY;
Updates the player's Y position to your cursor's vertical location
fill(0, 150, 255);
Sets the fill color to bright blue (RGB: 0 red, 150 green, 255 blue)
noStroke();
Removes the outline around the circle so it looks smooth
ellipse(playerX, playerY, playerSize, playerSize);
Draws the blue player circle centered at (playerX, playerY) with diameter playerSize
fill(255, 0, 100);
Sets the fill color to pink for the item square (RGB: 255 red, 0 green, 100 blue)
rectMode(CENTER);
Tells p5.js to draw rectangles from their center point instead of their top-left corner
rect(itemX, itemY, itemSize, itemSize);
Draws a pink square centered at (itemX, itemY) with width and height of itemSize
let d = dist(playerX, playerY, itemX, itemY);
Calculates the distance in pixels between the player's center and the item's center
if (d < playerSize / 2 + itemSize / 2) {
Checks if the distance is less than half the player's size plus half the item's size—meaning they are overlapping
score++;
Increases the score by 1 when a collision is detected
placeNewItem();
Moves the item to a new random position so you have a fresh target to collect
fill(0);
Sets the fill color to black for the text
text("Score: " + score, 10, 10);
Displays 'Score: ' followed by the current score number as black text at position (10, 10)

placeNewItem()

placeNewItem() is a helper function that encapsulates the logic for spawning a new item at a safe random location. By writing it once as a separate function, you can reuse it from setup() and draw() without repeating code. The key insight is the boundary math: itemSize/2 and width-itemSize/2 prevent the square from being clipped by the canvas edges.

🔬 Right now the item spawns anywhere on the canvas with equal probability. What happens if you change the second random() to only spawn in the left half of the screen? Try: itemX = random(0, width / 2);

  itemX = random(itemSize / 2, width - itemSize / 2); // Random X within canvas bounds
  itemY = random(itemSize / 2, height - itemSize / 2); // Random Y within canvas bounds
// Function to place the item at a new random position
function placeNewItem() {
  itemX = random(itemSize / 2, width - itemSize / 2); // Random X within canvas bounds
  itemY = random(itemSize / 2, height - itemSize / 2); // Random Y within canvas bounds
}
Line-by-line explanation (2 lines)

🔧 Subcomponents:

calculation Random X Position itemX = random(itemSize / 2, width - itemSize / 2);

Picks a random X coordinate that keeps the item fully visible on canvas

calculation Random Y Position itemY = random(itemSize / 2, height - itemSize / 2);

Picks a random Y coordinate that keeps the item fully visible on canvas

itemX = random(itemSize / 2, width - itemSize / 2);
Chooses a random X position between itemSize/2 and width-itemSize/2—this ensures the square never spawns partially off the left or right edge
itemY = random(itemSize / 2, height - itemSize / 2);
Chooses a random Y position between itemSize/2 and height-itemSize/2—this ensures the square never spawns partially off the top or bottom edge

windowResized()

windowResized() is a special p5.js function that automatically runs whenever the browser window changes size. It ensures the game adapts gracefully—the canvas grows or shrinks, and any game objects that would be off-screen are repositioned. This makes your sketch responsive without extra work.

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
  // If player position is outside new canvas bounds, recenter them
  if (playerX > width) playerX = width / 2;
  if (playerY > height) playerY = height / 2;
  // Also ensure the item is within bounds, or re-place it
  if (itemX > width || itemY > height || itemX < 0 || itemY < 0) {
    placeNewItem();
  }
}
Line-by-line explanation (5 lines)

🔧 Subcomponents:

calculation Canvas Resizing resizeCanvas(windowWidth, windowHeight);

Automatically expands or shrinks the canvas to match the browser window size

conditional Player Boundary Check if (playerX > width) playerX = width / 2; if (playerY > height) playerY = height / 2;

Ensures the player stays on-canvas if the window shrinks

conditional Item Boundary Check if (itemX > width || itemY > height || itemX < 0 || itemY < 0) { placeNewItem(); }

Respawns the item if it falls outside the new canvas boundaries

resizeCanvas(windowWidth, windowHeight);
p5.js calls this function whenever the browser window is resized, and resizeCanvas() adjusts the canvas to match the new window dimensions
if (playerX > width) playerX = width / 2;
If the player's X position is now beyond the right edge of the new canvas, recenter it horizontally
if (playerY > height) playerY = height / 2;
If the player's Y position is now beyond the bottom edge of the new canvas, recenter it vertically
if (itemX > width || itemY > height || itemX < 0 || itemY < 0) {
Checks if the item is now outside the new canvas bounds in any direction (right edge, bottom, left, or top)
placeNewItem();
If the item fell off-screen, calls placeNewItem() to respawn it at a valid random location

📦 Key Variables

playerX number

Stores the horizontal position of the player circle on the canvas

let playerX;
playerY number

Stores the vertical position of the player circle on the canvas

let playerY;
playerSize number

The diameter of the player circle in pixels

let playerSize = 50;
itemX number

Stores the horizontal position of the item square on the canvas

let itemX;
itemY number

Stores the vertical position of the item square on the canvas

let itemY;
itemSize number

The width and height of the item square in pixels

let itemSize = 30;
score number

Tracks how many items the player has collected throughout the game

let score = 0;

🔧 Potential Improvements (4)

Here are some ways this code could be enhanced:

BUG windowResized() player boundary check

The function only checks if playerX > width and playerY > height, but ignores if they are negative (left or top edges). If you drag your cursor far to the left, playerX could become negative when the window shrinks, placing the player off-screen.

💡 Add boundary checks for the negative side: if (playerX < 0) playerX = width / 2; and if (playerY < 0) playerY = height / 2;

STYLE draw() function organization

The collision detection and item spawning logic is mixed with drawing code, making draw() do three separate jobs: rendering, collision, and game state. This becomes harder to maintain as the game grows.

💡 Extract collision logic into a separate function like checkCollision() that returns true/false, then call it from draw(). This keeps rendering, physics, and game logic separate.

FEATURE placeNewItem() function

Items can spawn directly under the mouse cursor, making the first collection instant and sometimes frustrating or too easy

💡 Add a check: if the new item's position is too close to the current playerX/playerY (within 100 pixels), re-roll the random position until it is far enough away

PERFORMANCE draw() text rendering

textSize() and textAlign() are called in setup(), but text properties could be accidentally changed. Calling them once per frame in draw() guarantees consistent text display with minimal performance cost.

💡 Move textSize(32) and textAlign(LEFT, TOP) into the draw() function just before the text() call to ensure the score always displays exactly as intended

🔄 Code Flow

Code flow showing setup, draw, placenewitem, windowresized

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

graph TD start[Start] --> setup[setup] setup --> canvas-creation[Canvas Setup] setup --> player-centering[Player Initialization] setup --> text-config[Text Configuration] setup --> draw[draw loop] click setup href "#fn-setup" click canvas-creation href "#sub-canvas-creation" click player-centering href "#sub-player-centering" click text-config href "#sub-text-config" draw --> mouse-tracking[Mouse Tracking] draw --> player-draw[Player Drawing] draw --> item-draw[Item Drawing] draw --> collision-check[Collision Detection] draw --> score-display[Score Rendering] click draw href "#fn-draw" click mouse-tracking href "#sub-mouse-tracking" click player-draw href "#sub-player-draw" click item-draw href "#sub-item-draw" click collision-check href "#sub-collision-check" click score-display href "#sub-score-display" collision-check -->|if overlap| placenewitem[placeNewItem] collision-check -->|if no overlap| draw placenewitem --> random-x[Random X Position] placenewitem --> random-y[Random Y Position] click placenewitem href "#fn-placenewitem" click random-x href "#sub-random-x" click random-y href "#sub-random-y" draw --> windowresized[windowResized] windowresized --> canvas-resize[Canvas Resizing] windowresized --> player-bounds[Player Boundary Check] windowresized --> item-bounds[Item Boundary Check] click windowresized href "#fn-windowresized" click canvas-resize href "#sub-canvas-resize" click player-bounds href "#sub-player-bounds" click item-bounds href "#sub-item-bounds"

❓ Frequently Asked Questions

What visual elements can be found in the cool games sketch?

The sketch features a blue circular player that follows the mouse cursor and a pink square item that appears randomly on the canvas.

How can users interact with the cool games sketch?

Users interact by moving their mouse to control the player and collect the pink item, which increases their score upon collision.

What creative coding concepts does the cool games sketch demonstrate?

The sketch demonstrates collision detection, mouse tracking, and dynamic item placement within a responsive canvas.

Preview

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