Processing Starter Article 7

Variables & Animation

Learn Java data types, declare and update variables across frames, and build a bouncing ball with smooth oscillation using sin() and map().

⏱ 15 min processing variables animation java bouncing-ball sin

Variables in Java

Processing is Java, which means variables are statically typed — you must declare what kind of data a variable holds when you create it. This is different from JavaScript or Python, where you can assign any value to any variable.

The core types you’ll use most often:

Type What it stores Example
int Whole numbers int score = 0;
float Decimal numbers float x = 3.14;
boolean True or false boolean moving = true;
String Text String name = "Processing";
color A color value color bg = color(30, 30, 50);
int lives = 3;
float speed = 1.5;
boolean gameOver = false;
String message = "Hello Processing";

int vs float

The most common confusion for beginners is mixing int and float. Division between two integers in Java produces an integer — the fractional part is dropped:

int a = 7;
int b = 2;
println(a / b);  // prints 3, not 3.5!

To get decimal results, make at least one operand a float:

float result = 7.0 / 2;    // 3.5
float result2 = 7 / 2.0;   // 3.5
float result3 = float(a) / b;  // cast int to float first

This catches many beginners. If your calculations seem off, check whether you’re dividing integers.

Global Variables for Animation

Variables declared inside setup() or draw() only exist within that function call. To share state across frames — to animate something — you need variables that exist at the sketch level (outside both functions):

float x = 100;     // global: persists across all frames
float speed = 2;   // global: persists across all frames

void setup() {
  size(400, 200);
}

void draw() {
  background(30);

  // Update the global variable
  x = x + speed;

  // Draw using the current value
  ellipse(x, height / 2, 40, 40);
}

Run this. The circle moves from left to right across the canvas. Each frame, draw() adds speed to x, then draws the circle at the new position.

The Bouncing Ball

Here’s a complete, self-contained bouncing ball sketch:

float x, y;       // position
float vx, vy;     // velocity (speed + direction)
float r = 25;     // radius

void setup() {
  size(600, 400);
  x = width / 2;
  y = height / 2;
  vx = 3;
  vy = 2.5;
}

void draw() {
  background(20, 20, 40);

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

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

  // Bounce off top and bottom walls
  if (y < r || y > height - r) {
    vy = -vy;
  }

  // Draw the ball
  noStroke();
  fill(255, 200, 50);
  ellipse(x, y, r * 2, r * 2);
}

Walk through the logic:

  1. x += vx is shorthand for x = x + vx — moves the ball by its velocity each frame
  2. The if statements check whether the ball has reached an edge. When it does, the velocity is negated (flipped to the opposite sign), reversing direction
  3. Checking against r and width - r (rather than 0 and width) means the ball bounces when its edge, not its center, hits the wall

Adding a Speed Trail

For a more interesting visual, replace the solid background with a semi-transparent one:

void draw() {
  fill(20, 20, 40, 40);  // dark, semi-transparent — creates a trail
  rect(0, 0, width, height);

  // ... rest of draw code unchanged
}

The ball now leaves a fading trail behind it.

frameCount and frameRate

frameCount is a built-in integer that increments by 1 every frame. It’s useful for time-based events and phase calculations:

println(frameCount);  // 1 on first frame, 2 on second, etc.

frameRate(n) in setup() sets the target frames per second. The default is 60. To slow an animation down deliberately:

void setup() {
  size(400, 400);
  frameRate(12);  // 12 fps — choppy, like early animation
}

frameRate (without arguments) returns the current actual frame rate — useful for checking performance.

millis() for Time-Based Animation

millis() returns the number of milliseconds since the sketch started. This lets you base animation on real elapsed time rather than frame count, which is more consistent when frame rate fluctuates:

void draw() {
  background(30);

  float seconds = millis() / 1000.0;  // convert to seconds
  float x = map(seconds % 3.0, 0, 3.0, 50, width - 50);  // ping-pong over 3 seconds

  fill(100, 200, 255);
  noStroke();
  ellipse(x, height / 2, 50, 50);
}

This circle completes one pass every 3 seconds regardless of the frame rate.

sin() for Smooth Oscillation

The sine function produces a smooth wave between -1 and 1 as its input increases. This is perfect for oscillating animations — bobbing, pulsing, swaying:

void draw() {
  background(30);

  // Oscillate between 0 and height, once per ~6.28 seconds at 60fps
  float t = frameCount * 0.05;  // t increases slowly
  float y = height / 2 + sin(t) * 150;  // sin(t) goes -1 to 1, scaled to ±150

  fill(255, 100, 150);
  noStroke();
  ellipse(width / 2, y, 60, 60);
}

Combine multiple sine waves with different speeds and amplitudes for more complex motion:

float t = frameCount * 0.05;
float x = width / 2 + sin(t) * 150;         // horizontal oscillation
float y = height / 2 + sin(t * 1.3) * 100;  // slightly different frequency
float size = 40 + sin(t * 2.1) * 15;        // pulsing size

Because the frequencies aren’t round multiples of each other, the pattern never exactly repeats — it produces an organic, alive-feeling motion.

map() for Scaling Values

map(value, start1, stop1, start2, stop2) re-maps a value from one numeric range to another. It’s one of the most used functions in all of Processing:

// Map mouse x (0 to width) to a red channel value (0 to 255)
float r = map(mouseX, 0, width, 0, 255);
fill(r, 100, 200);
ellipse(200, 200, 100, 100);
// Map mouse y to speed (mouse at top = fast, at bottom = slow)
float speed = map(mouseY, 0, height, 5.0, 0.1);

map() also extrapolates — if value is outside start1-stop1, the output goes outside start2-stop2. To clamp the output to the target range:

float clamped = constrain(map(mouseX, 0, width, 0, 255), 0, 255);

constrain(value, low, high) clamps a value between low and high.

A Complete Animation Example

This sketch combines variables, sine oscillation, and map() for a multi-element animated composition:

float t = 0;

void setup() {
  size(600, 400);
  noStroke();
}

void draw() {
  background(15, 15, 30);
  t += 0.02;

  int count = 8;
  for (int i = 0; i < count; i++) {
    float phase = TWO_PI * i / count;  // evenly space phases around the circle
    float x = width / 2 + cos(t + phase) * 150;
    float y = height / 2 + sin(t * 1.1 + phase) * 100;

    float brightness = map(sin(t * 2 + phase), -1, 1, 100, 255);
    float diameter = map(sin(t * 0.7 + phase), -1, 1, 20, 55);

    fill(brightness * 0.4, brightness * 0.7, brightness);
    ellipse(x, y, diameter, diameter);
  }
}

Eight circles orbit a common center, each offset in phase. Their sizes and brightnesses breathe in and out at slightly different frequencies, creating an organic, undulating composition.

In the next tutorial, you’ll wire up mouse interaction properly — not just reading position, but responding to clicks, drags, and events with dedicated functions.