p5.js Starter Article 7

Variables & Animation

Use variables to store state and create motion — bouncing balls, growing shapes, drifting particles.

⏱ 18 min read variables animation motion let const

Variables in JavaScript

A variable is a named container for a value. In JavaScript, declare variables with let or const:

let x = 100;        // can be reassigned later
const speed = 2;    // cannot be reassigned (constant)

In creative coding, let is more common because most values change over time.

Where to declare variables

Variables used across both setup() and draw() must be declared outside both functions, at the top level:

let x = 0;   // accessible everywhere

function setup() {
  createCanvas(600, 400);
}

function draw() {
  background(20);
  circle(x, 200, 40);
  x = x + 2;   // or: x += 2
}

If you declare let x inside draw(), it resets to its initial value every single frame — usually not what you want.

A bouncing ball

Classic first animation — a ball that bounces off the walls:

let x, y;       // position
let vx, vy;     // velocity (speed + direction)

function setup() {
  createCanvas(600, 400);
  x  = width / 2;
  y  = height / 2;
  vx = 3;
  vy = 2;
}

function draw() {
  background(20);

  // Move
  x += vx;
  y += vy;

  // Bounce off left/right walls
  if (x < 20 || x > width - 20) {
    vx *= -1;
  }

  // Bounce off top/bottom
  if (y < 20 || y > height - 20) {
    vy *= -1;
  }

  // Draw
  fill(100, 200, 255);
  noStroke();
  circle(x, y, 40);
}

The key idea: vx and vy are the velocity. Every frame, position is updated by velocity. When the ball hits a wall, velocity in that direction is flipped (multiplied by -1).

Using frameCount for time

frameCount increments by 1 every frame. Use it to drive animations that repeat or evolve:

function draw() {
  background(20);

  // Oscillate with sin() — value bounces smoothly between -1 and +1
  let t    = frameCount * 0.02;   // slow time
  let osc  = sin(t);              // -1 to 1
  let size = map(osc, -1, 1, 20, 150); // 20 to 150

  fill(200, 100, 255);
  noStroke();
  circle(width / 2, height / 2, size);
}

sin() and cos() are your best friends in animation — they produce smooth, cyclical values. The pattern sin(frameCount * speed) is everywhere in creative coding.

Multiple variables — a spiral

let angle = 0;
let radius = 0;

function setup() {
  createCanvas(600, 600);
  background(20);
  noLoop();

  stroke(100, 200, 255, 180);
  strokeWeight(1.5);
  noFill();

  beginShape();
  for (let i = 0; i < 600; i++) {
    let x = width / 2 + cos(angle) * radius;
    let y = height / 2 + sin(angle) * radius;
    vertex(x, y);
    angle  += 0.15;
    radius += 0.4;
  }
  endShape();
}

Easing — smooth follow

Instead of snapping directly to a target, interpolate toward it:

let x = 300;
let y = 200;

function draw() {
  background(20);

  // Ease toward mouse — move 10% of the remaining distance each frame
  x += (mouseX - x) * 0.1;
  y += (mouseY - y) * 0.1;

  fill(255, 150, 50);
  noStroke();
  circle(x, y, 40);
}

The factor (0.1 here) controls the lag. 0.05 = very sluggish, 0.5 = almost instant.

lerp() — linear interpolation

lerp(start, stop, amount) returns a value between start and stop:

let x = 0;

function draw() {
  x = lerp(x, mouseX, 0.1);  // same easing as above, cleaner
  // ...
}

Multiple animated objects with arrays

Managing many objects with individual variables becomes unworkable. Taste of what’s coming in the Beginner level:

let circles = [];

function setup() {
  createCanvas(600, 400);
  for (let i = 0; i < 20; i++) {
    circles.push({
      x:  random(width),
      y:  random(height),
      vx: random(-2, 2),
      vy: random(-2, 2),
      r:  random(10, 30)
    });
  }
}

function draw() {
  background(20);
  noStroke();

  for (let c of circles) {
    c.x += c.vx;
    c.y += c.vy;

    if (c.x < 0 || c.x > width)  c.vx *= -1;
    if (c.y < 0 || c.y > height) c.vy *= -1;

    fill(100 + c.r * 3, 150, 255, 180);
    circle(c.x, c.y, c.r * 2);
  }
}

Key takeaways

  • Declare variables outside setup() and draw() if they need to persist between frames
  • Update position by adding velocity each frame: x += vx
  • Reverse velocity to bounce: vx *= -1
  • sin(frameCount * speed) creates smooth oscillation
  • Easing (x += (target - x) * 0.1) gives smooth, organic motion
  • lerp(start, stop, t) interpolates linearly between two values