AI Bouncing Ball - Xelsed p5flash-alpha02

This sketch animates a glowing ball that bounces around the browser window, leaving a soft fading trail behind it. Every time it hits an edge it flips direction, picks a brand new vibrant color, and casts a subtle drop shadow, all rendered using p5.js's HSB color mode.

🧪 Try This!

Experiment with the code by making these changes:

  1. Make trails last much longer — Lowering fadeAmount makes the background wash more transparent, so old ball images fade away more slowly and linger on screen.
  2. Supersize the ball — Increasing ballRadius makes the ball (and its pulse range) noticeably bigger everywhere it's drawn.
  3. Freeze the color palette — Fixing the hue to a single value instead of random(360) makes every bounce reuse the same color instead of jumping to a new random hue.
Prefer the full editor? Open it there →

📖 About This Sketch

This sketch creates a bouncing ball that fills the entire browser window, trailing soft color-fade ghosts behind it as it moves. What makes it visually striking is the combination of a semi-transparent background (which creates the trail effect), HSB color mode for vibrant random hues, a sine-wave pulse that makes the ball gently grow and shrink, and a drop shadow drawn just before the ball itself. Together these techniques turn a simple velocity-and-bounce animation into something that feels alive and glowing.

The code is organized into three functions: setup() runs once to size the canvas and give the ball a random starting velocity and color, draw() runs 60 times per second to move the ball, draw its shadow and body, and check for wall collisions, and windowResized() keeps everything working if the browser window changes size. By studying it you'll learn how velocity variables create motion, how translucent backgrounds fake motion trails, how sin(frameCount) creates smooth oscillation over time, and how colorMode(HSB, ...) makes generating random vibrant colors trivial.

⚙️ How It Works

  1. When the page loads, setup() creates a canvas that fills the window, places the ball at the center, gives it a random speed and direction on both axes, switches to HSB color mode, and picks a random vibrant starting color.
  2. Every frame, draw() paints a nearly-transparent white rectangle over the whole canvas instead of a solid background - because it doesn't fully erase the previous frame, older ball positions slowly fade into white, creating a glowing trail.
  3. The ball's x and y position are updated by adding its velocity (ballVX, ballVY) each frame, moving it in a straight line until something changes its direction.
  4. A sine wave based on frameCount is added to the ball's base radius so its size gently pulses larger and smaller over time, then a dark offset shadow is drawn first followed by the brightly colored ball on top.
  5. After drawing, draw() checks if the ball's edge has reached the left/right or top/bottom of the canvas; if so it flips the relevant velocity's sign (bouncing it back) and assigns ballColor a fresh random HSB color, which is why the ball changes color every time it bounces.
  6. If the browser window is resized, windowResized() resizes the canvas to match and nudges the ball back inside the new boundaries using constrain() so it never gets stuck off-screen.

🎓 Concepts You'll Learn

Animation loop (draw)Velocity-based motionCollision/bounce detectionHSB color modeTransparency for trail effectssin() oscillationResponsive canvas resizing

📝 Code Breakdown

setup()

setup() runs exactly once when the sketch starts, making it the right place to size the canvas and initialize starting values like position, velocity, and color mode before any animation begins.

🔬 This sets a random horizontal speed and then randomly reverses it. What happens if you remove the second line entirely so the ball always starts moving right?

  ballVX = random(3, 8); // Random speed between 3 and 8
  if (random() < 0.5) ballVX *= -1; // Randomly choose initial horizontal direction
function setup() {
  createCanvas(windowWidth, windowHeight);
  
  // Initialize ball in the center
  ballX = width / 2;
  ballY = height / 2;
  
  // Initialize ball velocity with random values
  // Ensure velocity is not too small or zero to make it move
  ballVX = random(3, 8); // Random speed between 3 and 8
  if (random() < 0.5) ballVX *= -1; // Randomly choose initial horizontal direction
  
  ballVY = random(3, 8); // Random speed between 3 and 8
  if (random() < 0.5) ballVY *= -1; // Randomly choose initial vertical direction
  
  // Set color mode to HSB: Hue (0-360), Saturation (0-100), Brightness (0-100), Alpha (0-100)
  colorMode(HSB, 360, 100, 100, 100);
  // Reference: https://p5js.org/reference/#/p5/colorMode
  
  // Initialize ball color with a random, vibrant HSB color
  // High saturation (80) and brightness (90) for vibrancy
  ballColor = color(random(360), 80, 90);
}
Line-by-line explanation (9 lines)

🔧 Subcomponents:

conditional Random Horizontal Direction if (random() < 0.5) ballVX *= -1;

Gives the ball a 50/50 chance of starting by moving left instead of right

conditional Random Vertical Direction if (random() < 0.5) ballVY *= -1;

Gives the ball a 50/50 chance of starting by moving up instead of down

createCanvas(windowWidth, windowHeight);
Makes the canvas exactly as big as the browser window, so the sketch fills the whole screen.
ballX = width / 2;
Places the ball's starting horizontal position at the exact center of the canvas.
ballY = height / 2;
Places the ball's starting vertical position at the exact center of the canvas.
ballVX = random(3, 8); // Random speed between 3 and 8
Picks a random horizontal speed between 3 and 8 pixels per frame - never zero, so the ball always moves.
if (random() < 0.5) ballVX *= -1; // Randomly choose initial horizontal direction
random() with no arguments returns 0-1; about half the time this flips the speed negative so the ball can start moving left.
ballVY = random(3, 8); // Random speed between 3 and 8
Same idea as ballVX but for vertical speed.
if (random() < 0.5) ballVY *= -1; // Randomly choose initial vertical direction
Randomly decides whether the ball starts moving up or down.
colorMode(HSB, 360, 100, 100, 100);
Switches p5's color system from default RGB to HSB (Hue, Saturation, Brightness, Alpha), with hue ranging 0-360 and the rest 0-100 - this makes picking vivid random colors much easier than with RGB.
ballColor = color(random(360), 80, 90);
Creates the ball's first color using a random hue (0-360) with fixed high saturation (80) and brightness (90), guaranteeing a vibrant color rather than a dull or grayish one.

draw()

draw() is the animation heartbeat of every p5.js sketch - it runs automatically about 60 times per second, and everything inside it (movement, drawing, and collision checks) repeats every frame to create the illusion of continuous motion.

🔬 This is what triggers a bounce and color change. What happens if you also multiply ballVX by 1.05 here, so the ball speeds up a little every time it bounces?

  if (ballX + currentBallSize >= width || ballX - currentBallSize <= 0) {
    ballVX *= -1; // Reverse horizontal velocity
    
    // Change ball color randomly on bounce (new vibrant HSB color)
    ballColor = color(random(360), 80, 90);

🔬 The 0.1 controls how fast the ball pulses. What happens if you change it to 0.5? To 0.02?

  currentBallSize = ballRadius + sin(frameCount * 0.1) * 5;
function draw() {
  // Draw a semi-transparent white background to create the fading trail
  // 0,0,100 is pure white in HSB
  background(0, 0, 100, fadeAmount); 
  // Reference: https://p5js.org/reference/#/p5/background
  
  // Update ball position based on velocity
  ballX += ballVX;
  ballY += ballVY;
  // Reference: https://p5js.org/reference/#/p5/+=
  
  // Vary the ball size slightly using a sine wave based on frameCount
  // Oscillation amplitude of 5 pixels around ballRadius
  currentBallSize = ballRadius + sin(frameCount * 0.1) * 5;
  // Reference: https://p5js.org/reference/#/p5/sin
  // Reference: https://p5js.org/reference/#/p5/frameCount
  
  // --- Draw the subtle shadow first ---
  fill(0, 0, 0, 30); // Dark gray/black with 30% transparency in HSB
  noStroke(); // No outline for the shadow
  // Shadow is offset down and right, and slightly larger than the ball
  circle(ballX + 10, ballY + 10, currentBallSize * 2.2);
  
  // --- Draw the main ball ---
  fill(ballColor); // Set fill color to the ball's current vibrant color
  // Reference: https://p5js.org/reference/#/p5/fill
  noStroke(); // Remove outline for the ball
  // Reference: https://p5js.org/reference/#/p5/noStroke
  circle(ballX, ballY, currentBallSize * 2); // Draw a circle (x, y, diameter) with varying size
  // Reference: https://p5js.org/reference/#/p5/circle
  
  // Check for horizontal bounces
  // If the ball hits the right edge OR the left edge
  if (ballX + currentBallSize >= width || ballX - currentBallSize <= 0) {
    ballVX *= -1; // Reverse horizontal velocity
    
    // Change ball color randomly on bounce (new vibrant HSB color)
    ballColor = color(random(360), 80, 90);
    // Reference: https://p5js.org/reference/#/p5/color
    // Reference: https://p5js.org/reference/#/p5/random
  }
  
  // Check for vertical bounces
  // If the ball hits the bottom edge OR the top edge
  if (ballY + currentBallSize >= height || ballY - currentBallSize <= 0) {
    ballVY *= -1; // Reverse vertical velocity
    
    // Change ball color randomly on bounce (new vibrant HSB color)
    ballColor = color(random(360), 80, 90);
  }
}
Line-by-line explanation (13 lines)

🔧 Subcomponents:

conditional Horizontal Bounce Check if (ballX + currentBallSize >= width || ballX - currentBallSize <= 0) {

Detects if the ball's edge has reached the left or right side of the canvas and reverses its horizontal velocity plus assigns a new random color

conditional Vertical Bounce Check if (ballY + currentBallSize >= height || ballY - currentBallSize <= 0) {

Detects if the ball's edge has reached the top or bottom of the canvas and reverses its vertical velocity plus assigns a new random color

background(0, 0, 100, fadeAmount);
Instead of fully clearing the frame, this paints a nearly-transparent white rectangle over everything. Because it's not fully opaque, the previous frame's ball images show through faintly, creating the glowing trail effect.
ballX += ballVX;
Moves the ball horizontally by adding its current velocity to its position - this is the core of the motion.
ballY += ballVY;
Moves the ball vertically by adding its current velocity to its position.
currentBallSize = ballRadius + sin(frameCount * 0.1) * 5;
sin() oscillates smoothly between -1 and 1 over time; multiplying by 5 and adding to the base radius makes the ball's size gently pulse between 25 and 35 pixels as frameCount increases.
fill(0, 0, 0, 30); // Dark gray/black with 30% transparency in HSB
Sets the next shape's fill color to a translucent black, used for the drop shadow beneath the ball.
circle(ballX + 10, ballY + 10, currentBallSize * 2.2);
Draws the shadow slightly offset down-and-right from the ball's actual position and a bit larger, giving the illusion of light coming from one direction.
fill(ballColor); // Set fill color to the ball's current vibrant color
Switches the fill color to the ball's current stored HSB color before drawing the actual ball on top of its shadow.
circle(ballX, ballY, currentBallSize * 2); // Draw a circle (x, y, diameter) with varying size
Draws the ball itself; circle() takes a diameter as its third argument, so currentBallSize (a radius) is doubled.
if (ballX + currentBallSize >= width || ballX - currentBallSize <= 0) {
Checks whether the ball's right edge has reached the canvas width, OR its left edge has gone past zero - either condition means it has hit a horizontal wall.
ballVX *= -1; // Reverse horizontal velocity
Flips the sign of the horizontal velocity, making the ball bounce back in the opposite direction.
ballColor = color(random(360), 80, 90);
Generates a fresh random vibrant hue for the ball, giving the color-changing-on-bounce effect.
if (ballY + currentBallSize >= height || ballY - currentBallSize <= 0) {
Same bounce logic as above, but checking the top and bottom edges instead of left and right.
ballVY *= -1; // Reverse vertical velocity
Flips the vertical velocity's sign so the ball bounces off the floor or ceiling.

windowResized()

windowResized() is a special p5.js function that fires automatically whenever the browser window is resized, making it the ideal place to keep a full-window sketch responsive.

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
  // Reference: https://p5js.org/reference/#/p5/windowResized
  // Adjust ball position if canvas resizes to keep it within bounds
  ballX = constrain(ballX, currentBallSize, width - currentBallSize);
  ballY = constrain(ballY, currentBallSize, height - currentBallSize);
  // Reference: https://p5js.org/reference/#/p5/constrain
}
Line-by-line explanation (3 lines)
resizeCanvas(windowWidth, windowHeight);
Automatically called by p5.js whenever the browser window changes size - this resizes the canvas to match the new window dimensions.
ballX = constrain(ballX, currentBallSize, width - currentBallSize);
constrain() clamps ballX so it stays between currentBallSize and (width - currentBallSize), preventing the ball from ending up off-screen if the window shrinks.
ballY = constrain(ballY, currentBallSize, height - currentBallSize);
Does the same clamping for the vertical position, keeping the ball fully visible after a resize.

📦 Key Variables

ballX number

Stores the ball's current horizontal position on the canvas

let ballX, ballY; // Ball's position
ballY number

Stores the ball's current vertical position on the canvas

let ballX, ballY; // Ball's position
ballVX number

Horizontal velocity - how many pixels the ball moves left/right each frame, flips sign on bounce

let ballVX, ballVY; // Ball's velocity
ballVY number

Vertical velocity - how many pixels the ball moves up/down each frame, flips sign on bounce

let ballVX, ballVY; // Ball's velocity
ballRadius number

The base radius the ball's size oscillates around every frame

let ballRadius = 30; // Base radius of the ball
currentBallSize number

The ball's actual radius this frame, computed from ballRadius plus a sine-wave pulse, and used for both drawing and collision checks

let currentBallSize; // Current radius, varies slightly
ballColor object

Stores the p5.Color object currently used to fill the ball; replaced with a new random HSB color on every bounce

let ballColor; // Current color of the ball
fadeAmount number

The alpha (transparency) value used for the background wash each frame; lower values create longer-lasting trails

let fadeAmount = 8; 

🔧 Potential Improvements (4)

Here are some ways this code could be enhanced:

BUG draw() collision detection

The bounce checks use >= and <= against width/height without repositioning the ball back inside bounds, so at high speeds the ball can partially or fully overshoot the canvas edge before bouncing, occasionally appearing to clip outside the visible area for a frame.

💡 After reversing velocity, also clamp the position with something like ballX = constrain(ballX, currentBallSize, width - currentBallSize); to guarantee the ball never visually escapes the canvas.

PERFORMANCE draw()

color(random(360), 80, 90) is created twice in separate if-blocks with identical logic, and a full-screen background() wash with transparency is a relatively expensive full-canvas redraw every frame.

💡 Factor the color creation into a small helper function like randomVibrantColor() to avoid duplicated code, and consider using a semi-transparent rect() only if the fade look is essential, since background() with alpha is already about as efficient as it gets in p5.

STYLE global variables

Variable declarations like 'let ballX, ballY;' combine two unrelated concerns on one line and some have trailing whitespace (e.g. 'let fadeAmount = 8; '), which can make the code slightly harder to scan.

💡 Declare each variable on its own line with a clear comment, and trim trailing whitespace for cleaner, more consistent formatting.

FEATURE draw()

The ball currently only changes to a completely random new color on every bounce, which can feel jarring and disconnected from its previous color.

💡 Use lerpColor() to smoothly transition between the old and new color over a few frames, or shift the hue by a fixed amount (e.g. +30) each bounce for a more rainbow-cycling effect.

🔄 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 --> horizontal-direction-flip[Horizontal Direction Flip] draw --> vertical-direction-flip[Vertical Direction Flip] draw --> horizontal-bounce-check[Horizontal Bounce Check] draw --> vertical-bounce-check[Vertical Bounce Check] horizontal-direction-flip -->|50/50 chance| horizontal-bounce-check vertical-direction-flip -->|50/50 chance| vertical-bounce-check horizontal-bounce-check -->|if edge reached| horizontal-bounce-check horizontal-bounce-check -->|assign new color| draw vertical-bounce-check -->|if edge reached| vertical-bounce-check vertical-bounce-check -->|assign new color| draw click setup href "#fn-setup" click draw href "#fn-draw" click horizontal-direction-flip href "#sub-horizontal-direction-flip" click vertical-direction-flip href "#sub-vertical-direction-flip" click horizontal-bounce-check href "#sub-horizontal-bounce-check" click vertical-bounce-check href "#sub-vertical-bounce-check"

❓ Frequently Asked Questions

What visual effects does the AI Bouncing Ball sketch create?

The sketch features a colorful bouncing ball that changes colors dynamically upon each bounce, leaving a smooth fading trail that enhances the visual appeal.

Is the AI Bouncing Ball sketch interactive for users?

While the sketch does not have direct user interactions, it responds to the canvas size, adjusting the ball's position and movement within the responsive environment.

What creative coding concepts are showcased in the AI Bouncing Ball sketch?

This sketch demonstrates concepts such as dynamic color manipulation, smooth animation through velocity and position updates, and creating a fading trail effect using transparency.

Preview

AI Bouncing Ball - Xelsed p5flash-alpha02 - p5.js creative coding sketch preview
Sketch Preview
Code flow diagram showing the structure of AI Bouncing Ball - Xelsed p5flash-alpha02 - Code flow showing setup, draw, windowresized
Code Flow Diagram