Console

This sketch creates an interactive red ball that bounces around a responsive canvas while being attracted to your mouse cursor. Click anywhere to 'throw' the ball to that location with random velocity and hear a drum sound effect, while a motion trail follows the ball's path.

🧪 Try This!

Experiment with the code by making these changes:

  1. Make the ball blue instead of red — The fill() color sets the ball's appearance—change the RGB values to see it instantly repaint
  2. Make the mouse attraction weaker — Lowering attractionForce means the mouse pulls on the ball less—the ball will orbit your cursor instead of rushing toward it
  3. Remove the motion trail — Change the background transparency to 255 so each frame completely erases the last one—the trail vanishes
  4. Make the ball much larger — ballRadius controls the size—doubling it from 30 to 60 makes the ball twice as wide and twice as tall
  5. Slow down the throw speed
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch animates a red ball that bounces off canvas edges while being pulled toward your mouse cursor using vector-based attraction physics. You can click anywhere to instantly throw the ball to that spot with a random velocity and trigger a drum sound effect. The semi-transparent background creates a beautiful motion trail as the ball glides, making its path visible and mesmerizing.

The code demonstrates three essential p5.js techniques working together: the draw loop for continuous animation, vector math for simulating attraction forces, and the p5.sound library for playing audio on user interaction. By reading it, you will learn how to combine physics simulation, responsive canvas sizing, and interactive audio to create a polished, engaging sketch.

⚙️ How It Works

  1. When the sketch loads, preload() fetches a drum sound file from the internet, and setup() creates a full-screen canvas and places the ball in the center with random initial velocity.
  2. Every frame, draw() calculates a vector pointing from the ball toward the mouse, normalizes it to a unit direction, and adds it to the ball's velocity—creating smooth magnetic attraction.
  3. The ball's velocity is capped at a maximum speed to prevent runaway acceleration, then added to the ball's position to move it smoothly across the canvas.
  4. Collision detection checks if the ball's radius extends past any canvas edge and reverses the appropriate velocity component to create bounces.
  5. A semi-transparent background (alpha = 50) is drawn each frame instead of a fully opaque one, causing old ball positions to fade slowly and create a motion trail.
  6. When you click anywhere, mousePressed() instantly teleports the ball to your cursor, gives it new random velocity, and plays the drum sound using the p5.sound library.

🎓 Concepts You'll Learn

Vector physics and attraction forcesAnimation loop and frame-by-frame updatesCollision detection with bouncingp5.sound library and audio playbackMotion trails using transparencyResponsive canvas sizingMouse interaction and click events

📝 Code Breakdown

preload()

preload() is called automatically by p5.js before setup(). It's the perfect place to load images, sounds, and other files from the internet that your sketch needs. Without preload(), your sketch might try to draw before the files finish downloading.

function preload() {
  // Load the sound file here. This function runs before setup() and draw().
  // Using a CORS-friendly preview URL from freesound.org
  // Source: https://freesound.org/s/235970/ (Drum Beat, 100 bpm)
  throwSound = loadSound('https://freesound.org/data/previews/235/235970_42065-lq.ogg');
}
Line-by-line explanation (2 lines)

🔧 Subcomponents:

assignment Load Sound File throwSound = loadSound('https://freesound.org/data/previews/235/235970_42065-lq.ogg');

Fetches the drum sound from the internet and stores it in a variable so it can be played later

function preload() {
preload() is a special p5.js function that runs once, before setup() and draw(), giving you time to load files from the internet
throwSound = loadSound('https://freesound.org/data/previews/235/235970_42065-lq.ogg');
loadSound() downloads the audio file from the URL and stores it in the throwSound variable—this is why preload() is needed, so the sketch waits for the file to arrive before continuing

setup()

setup() runs once when the sketch loads. It's where you initialize the canvas, load variables, and prepare your sketch to run. Notice how the ball's starting position and velocity are random—each reload gives you a different starting configuration.

function setup() {
  createCanvas(windowWidth, windowHeight);
  // Tip: Use windowWidth/windowHeight for responsive canvas

  // --- NEW SOUND CODE ---
  userStartAudio(); // Required to enable audio playback in most browsers
  // --- END NEW SOUND CODE ---

  // Initialize ball position to the center of the canvas
  ballX = width / 2;
  ballY = height / 2;

  // Initialize ball velocity (speed and direction)
  // random() function returns a random floating-point number in a specified range.
  // For velocity, a range like -5 to 5 is good for a noticeable speed.
  // === MODIFIED FOR SUPER SPEED ===
  ballVX = random(-100, 100); // Now up to 20 times faster!
  ballVY = random(-100, 100); // Now up to 20 times faster!
}
Line-by-line explanation (6 lines)

🔧 Subcomponents:

assignment Create Responsive Canvas createCanvas(windowWidth, windowHeight);

Makes the canvas fill the entire browser window and respond to resizing

assignment Enable Browser Audio userStartAudio();

Unlocks audio playback—most browsers require user interaction before sounds can play

assignment Center Ball Position ballX = width / 2; ballY = height / 2;

Places the ball in the center of the canvas when the sketch starts

assignment Random Starting Velocity ballVX = random(-100, 100); ballVY = random(-100, 100);

Gives the ball a random velocity between -100 and 100 in both directions, making it move unpredictably from the start

createCanvas(windowWidth, windowHeight);
Creates a canvas that fills the entire browser window using windowWidth and windowHeight—this makes the sketch responsive to different screen sizes
userStartAudio();
Tells the browser to enable audio playback—without this, many browsers will block sound until the user clicks or interacts with the page
ballX = width / 2;
Sets the ball's x position to the horizontal center of the canvas (width / 2 is the midpoint)
ballY = height / 2;
Sets the ball's y position to the vertical center of the canvas
ballVX = random(-100, 100);
random(-100, 100) returns a random number between -100 and 100, giving the ball unpredictable horizontal motion
ballVY = random(-100, 100);
Gives the ball unpredictable vertical velocity—this creates variety each time you reload the sketch

draw()

draw() is the heart of your animation—it runs 60 times per second. Each frame: the background is cleared (with fading), physics updates the ball's velocity and position, collisions are checked, and the ball is drawn at its new location. The semi-transparent background is the secret to the beautiful motion trail: instead of erasing everything, it lets old frames fade away slowly.

function draw() {
  // === MODIFIED FOR TRAIL EFFECT ===
  background(220, 50); // Clear the background each frame with 50% transparency

  // === NEW "FOLLOW MY MOUSE" LOGIC ===
  // Calculate the vector from the ball to the mouse
  let attractionVector = createVector(mouseX - ballX, mouseY - ballY);
  attractionVector.normalize(); // Set its length to 1 (direction only)
  attractionVector.mult(attractionForce); // Scale by attraction force

  // Add the attraction vector to the ball's velocity
  ballVX += attractionVector.x;
  ballVY += attractionVector.y;

  // Cap the ball's speed
  let currentSpeed = sqrt(ballVX * ballVX + ballVY * ballVY);
  if (currentSpeed > maxSpeed) {
    ballVX = (ballVX / currentSpeed) * maxSpeed;
    ballVY = (ballVY / currentSpeed) * maxSpeed;
  }
  // === END NEW LOGIC ===

  // 1. Update ball position based on velocity
  ballX += ballVX; // Add velocity to X position
  ballY += ballVY; // Add velocity to Y position

  // 2. Check for bounces off the canvas edges
  // Bounce off left/right edges
  // If the ball's edge goes past the canvas edge, reverse its X velocity
  if (ballX + ballRadius > width || ballX - ballRadius < 0) {
    ballVX *= -1; // Reverse X velocity
  }

  // Bounce off top/bottom edges
  // If the ball's edge goes past the canvas edge, reverse its Y velocity
  if (ballY + ballRadius > height || ballY - ballRadius < 0) {
    ballVY *= -1; // Reverse Y velocity
  }

  // 3. Draw the ball
  fill(255, 0, 0); // Set ball color to red
  noStroke();      // Don't draw an outline for the ball
  circle(ballX, ballY, ballRadius * 2); // Draw a circle (p5.js circle function expects diameter)
}
Line-by-line explanation (19 lines)

🔧 Subcomponents:

assignment Semi-Transparent Background background(220, 50);

Clears the background to light gray but keeps 50% of the previous frame visible, creating a motion trail effect

calculation Calculate Attraction Vector let attractionVector = createVector(mouseX - ballX, mouseY - ballY);

Creates a vector pointing from the ball toward the mouse—the first step in simulating magnetic attraction

method-call Normalize Vector to Unit Direction attractionVector.normalize();

Shortens the vector to length 1, making it purely directional—this ensures the attraction force doesn't depend on distance

method-call Scale by Attraction Force attractionVector.mult(attractionForce);

Multiplies the unit vector by the attraction force constant to control how strongly the mouse pulls the ball

assignment Apply Attraction to Velocity ballVX += attractionVector.x; ballVY += attractionVector.y;

Adds the attraction force to the ball's velocity, gradually steering it toward the mouse

conditional Cap Maximum Speed let currentSpeed = sqrt(ballVX * ballVX + ballVY * ballVY); if (currentSpeed > maxSpeed) { ballVX = (ballVX / currentSpeed) * maxSpeed; ballVY = (ballVY / currentSpeed) * maxSpeed; }

Prevents the ball from accelerating infinitely by limiting its total speed to the maxSpeed value

assignment Update Ball Position ballX += ballVX; ballY += ballVY;

Moves the ball by adding its velocity to its position each frame

conditional Horizontal Bounce Check if (ballX + ballRadius > width || ballX - ballRadius < 0) { ballVX *= -1; }

Checks if the ball's edge has crossed the left or right canvas boundary and reverses horizontal velocity if so

conditional Vertical Bounce Check if (ballY + ballRadius > height || ballY - ballRadius < 0) { ballVY *= -1; }

Checks if the ball's edge has crossed the top or bottom canvas boundary and reverses vertical velocity if so

assignment Draw Ball at New Position circle(ballX, ballY, ballRadius * 2);

Renders a red circle at the ball's current position with diameter = radius * 2

background(220, 50);
Draws a light gray background with 50% alpha (transparency)—this fades out old frames instead of completely erasing them, leaving a motion trail
let attractionVector = createVector(mouseX - ballX, mouseY - ballY);
Creates a vector from the ball's position (ballX, ballY) to the mouse position (mouseX, mouseY)—this vector points in the direction the ball should be attracted
attractionVector.normalize();
Normalizes the vector to length 1, so it's purely directional—without this, far-away cursors would pull harder than close ones
attractionVector.mult(attractionForce);
Scales the unit vector by the attractionForce constant, controlling how aggressively the mouse pulls on the ball
ballVX += attractionVector.x;
Adds the horizontal component of the attraction force to the ball's horizontal velocity—this gradually steers the ball toward the mouse's x position
ballVY += attractionVector.y;
Adds the vertical component of the attraction force to the ball's vertical velocity
let currentSpeed = sqrt(ballVX * ballVX + ballVY * ballVY);
Calculates the ball's total speed using the Pythagorean theorem: √(vx² + vy²)—this is the length of the velocity vector
if (currentSpeed > maxSpeed) {
Checks if the ball is moving faster than the maxSpeed limit
ballVX = (ballVX / currentSpeed) * maxSpeed;
If speed is too high, this reduces vx proportionally while keeping the direction intact—dividing by currentSpeed normalizes, multiplying by maxSpeed applies the new speed limit
ballVY = (ballVY / currentSpeed) * maxSpeed;
Does the same speed capping for the vertical component
ballX += ballVX;
Updates the ball's x position by adding its horizontal velocity—on the next draw call, the ball will be drawn slightly to the right (or left if vx is negative)
ballY += ballVY;
Updates the ball's y position by adding its vertical velocity
if (ballX + ballRadius > width || ballX - ballRadius < 0) {
Checks if the ball's right edge (ballX + ballRadius) has crossed the right boundary (width) OR if the ball's left edge (ballX - ballRadius) has crossed the left boundary (0)
ballVX *= -1;
Reverses the horizontal velocity, bouncing the ball off the left or right edge
if (ballY + ballRadius > height || ballY - ballRadius < 0) {
Checks if the ball's bottom edge (ballY + ballRadius) has crossed the bottom boundary (height) OR if the ball's top edge has crossed the top (0)
ballVY *= -1;
Reverses the vertical velocity, bouncing the ball off the top or bottom edge
fill(255, 0, 0);
Sets the fill color to red (RGB: 255, 0, 0)—all shapes drawn after this will be red until the color changes
noStroke();
Removes the outline stroke from the circle—without this, the ball would have a thin black border
circle(ballX, ballY, ballRadius * 2);
Draws a circle at the ball's current position with a diameter of ballRadius * 2 (since radius = 30, diameter = 60)

windowResized()

windowResized() is called automatically by p5.js whenever the browser window or preview panel is resized. It's essential for responsive sketches—without it, your canvas would stop responding to size changes. The ball is re-centered to prevent it from escaping the visible area when the canvas shrinks.

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
  // Called when preview panel is resized

  // Re-center the ball if the canvas is resized
  ballX = width / 2;
  ballY = height / 2;
}
Line-by-line explanation (4 lines)

🔧 Subcomponents:

assignment Resize Canvas to Window resizeCanvas(windowWidth, windowHeight);

Updates the canvas dimensions when the browser window is resized

assignment Re-center Ball ballX = width / 2; ballY = height / 2;

Moves the ball back to the center when the canvas is resized, preventing it from getting stuck off-screen

function windowResized() {
windowResized() is a special p5.js function that automatically runs whenever the browser window is resized
resizeCanvas(windowWidth, windowHeight);
Updates the canvas to match the new window dimensions—without this, the canvas would stay at its original size when you resize the browser
ballX = width / 2;
Re-centers the ball horizontally so it doesn't get stranded off-screen when the window size changes
ballY = height / 2;
Re-centers the ball vertically for the same reason

mousePressed()

mousePressed() fires once every time you click anywhere on the canvas. It's perfect for implementing click interactions—in this sketch, it creates the 'throw' mechanic by teleporting the ball and giving it a new velocity. The random velocity makes each throw feel different and unpredictable, keeping the interaction fun.

function mousePressed() {
  // Move the ball to the current mouse position
  ballX = mouseX;
  ballY = mouseY;

  // Give the ball a new random velocity
  // This makes it feel like you're "throwing" the ball
  // === MODIFIED FOR SUPER SPEED ===
  ballVX = random(-100, 100); // Now up to 20 times faster!
  ballVY = random(-100, 100); // Now up to 20 times faster!

  // --- NEW SOUND CODE ---
  throwSound.play(); // Play the sound when the mouse is pressed
  // --- END NEW SOUND CODE ---
}
Line-by-line explanation (6 lines)

🔧 Subcomponents:

assignment Teleport Ball to Mouse ballX = mouseX; ballY = mouseY;

Instantly moves the ball to wherever you clicked

assignment Give Random Velocity ballVX = random(-100, 100); ballVY = random(-100, 100);

Assigns new random velocity in both directions, making it feel like you 'threw' the ball with unpredictable force

assignment Play Drum Sound throwSound.play();

Plays the loaded drum sound effect, providing audio feedback for the throw interaction

function mousePressed() {
mousePressed() is a special p5.js function that automatically runs once every time you click the mouse
ballX = mouseX;
Sets the ball's x position to the current mouse x coordinate—this teleports the ball to where you clicked
ballY = mouseY;
Sets the ball's y position to the current mouse y coordinate
ballVX = random(-100, 100);
Assigns a new random velocity between -100 and 100 to the x direction—this makes the throw direction unpredictable and fun
ballVY = random(-100, 100);
Assigns a new random velocity in the y direction
throwSound.play();
Plays the drum sound that was loaded in preload()—this provides immediate audio feedback to your click, making the interaction feel responsive

📦 Key Variables

ballX number

Stores the ball's horizontal position on the canvas—updated every frame by adding ballVX

let ballX = 200;
ballY number

Stores the ball's vertical position on the canvas—updated every frame by adding ballVY

let ballY = 200;
ballVX number

Horizontal velocity of the ball—how many pixels it moves left/right each frame, positive moves right, negative moves left

let ballVX = 5;
ballVY number

Vertical velocity of the ball—how many pixels it moves up/down each frame, positive moves down, negative moves up

let ballVY = -3;
ballRadius number

The radius of the ball in pixels—used for drawing (diameter = radius * 2) and collision detection

let ballRadius = 30;
maxSpeed number

The maximum speed the ball can reach—prevents infinite acceleration from the mouse attraction force

let maxSpeed = 150;
attractionForce number

How strongly the mouse attracts the ball—higher values pull the ball toward the cursor more aggressively

let attractionForce = 1.5;
throwSound object

Stores the loaded drum sound from preload()—called with .play() when the mouse is clicked

let throwSound;

🔧 Potential Improvements (4)

Here are some ways this code could be enhanced:

BUG windowResized()

The function doesn't return false, which can cause the page to scroll when the window is resized on some browsers

💡 Add 'return false;' at the end of windowResized() to prevent default behavior: function windowResized() { ... return false; }

PERFORMANCE draw() - speed capping calculation

The speed calculation sqrt(ballVX * ballVX + ballVY * ballVY) is done every frame even when the ball is slow—this is usually fine but wastes computation

💡 Only recalculate speed when necessary: if (ballVX * ballVX + ballVY * ballVY > maxSpeed * maxSpeed) { ... }

STYLE sketch.js globals

Global variables like ballX, ballY, ballVX, ballVY would be better organized in an object for clarity as the sketch grows

💡 Group related variables: let ball = { x: 0, y: 0, vx: 0, vy: 0, radius: 30 }; then use ball.x, ball.y, etc.

FEATURE mousePressed()

The throw is always random—no way to control where the ball goes with precision

💡 Calculate velocity based on the difference between click position and where the ball was: let vx = (mouseX - ballX) * 0.1; for predictable 'flick' throws

🔄 Code Flow

Code flow showing preload, setup, draw, windowresized, mousepressed

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

graph TD start[Start] --> setup[setup] setup --> loadsound[load-sound] setup --> createcanvas[create-canvas] setup --> enableaudio[enable-audio] setup --> initposition[init-position] setup --> initvelocity[init-velocity] setup --> draw[draw loop] click setup href "#fn-setup" click loadsound href "#sub-load-sound" click createcanvas href "#sub-create-canvas" click enableaudio href "#sub-enable-audio" click initposition href "#sub-init-position" click initvelocity href "#sub-init-velocity" draw --> transparentbackground[transparent-background] draw --> attractionvector[attraction-vector] draw --> normalizevector[normalize-vector] draw --> scalebyforce[scale-by-force] draw --> applyattraction[apply-attraction] draw --> speedcap[speed-cap] draw --> updateposition[update-position] draw --> horizontalbounce[horizontal-bounce] draw --> verticalbounce[vertical-bounce] draw --> drawball[draw-ball] click draw href "#fn-draw" click transparentbackground href "#sub-transparent-background" click attractionvector href "#sub-attraction-vector" click normalizevector href "#sub-normalize-vector" click scalebyforce href "#sub-scale-by-force" click applyattraction href "#sub-apply-attraction" click speedcap href "#sub-speed-cap" click updateposition href "#sub-update-position" click horizontalbounce href "#sub-horizontal-bounce" click verticalbounce href "#sub-vertical-bounce" click drawball href "#sub-draw-ball" draw --> drawloop[Draw Loop] drawloop --> draw windowresized --> resizecanvas[resize-canvas] windowresized --> recenterball[recenter-ball] click windowresized href "#fn-windowresized" click resizecanvas href "#sub-resize-canvas" click recenterball href "#sub-recenter-ball" mousepressed --> teleportball[teleport-ball] mousepressed --> randomvelocity[random-velocity] mousepressed --> playsound[play-sound] click mousepressed href "#fn-mousepressed" click teleportball href "#sub-teleport-ball" click randomvelocity href "#sub-random-velocity" click playsound href "#sub-play-sound"

❓ Frequently Asked Questions

What visual effects can I expect from this p5.js sketch?

This sketch creates a dynamic visual where a ball moves across the canvas, leaving a faded trail as it follows the mouse cursor.

How can users interact with the ball in this sketch?

Users can interact with the sketch by moving their mouse, causing the ball to be attracted to the mouse position, resulting in a responsive and engaging experience.

What creative coding concepts are demonstrated in this p5.js sketch?

This sketch illustrates concepts such as velocity manipulation, mouse interaction, and visual trails using transparency effects in a dynamic environment.

Preview

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