Variables & Animation
Learn Java data types, declare and update variables across frames, and build a bouncing ball with smooth oscillation using sin() and map().
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:
x += vxis shorthand forx = x + vx— moves the ball by its velocity each frame- The
ifstatements check whether the ball has reached an edge. When it does, the velocity is negated (flipped to the opposite sign), reversing direction - Checking against
randwidth - r(rather than0andwidth) 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.