Intro to Object-Oriented Programming
Learn classes, fields, constructors, and methods by building a system of 20 independently bouncing balls.
Blueprint vs instance
A class is a blueprint — a template that describes what data a thing holds and what it can do. An instance (or object) is one real thing built from that blueprint. A single class can produce thousands of independent instances, each with its own values.
Think of a Ball class as the design for a ball: it has a position, a velocity, a size, and a colour. Every actual ball on screen is one instance of that design, carrying its own x, y, vx, vy, and so on.
Defining a class
In Processing, classes live in the same .pde file (or in a separate tab). The class keyword, a name (capitalised by convention), and a pair of curly braces:
class Ball {
// Fields — instance variables
float x, y;
float vx, vy;
float r;
color col;
// Constructor — runs once when you create an instance
Ball(float startX, float startY) {
x = startX;
y = startY;
vx = random(-3, 3);
vy = random(-3, 3);
r = random(10, 30);
col = color(random(100, 255), random(100, 255), random(100, 255));
}
// Methods — things the ball can do
void update() {
x += vx;
y += vy;
// Bounce off walls
if (x - r < 0 || x + r > width) vx *= -1;
if (y - r < 0 || y + r > height) vy *= -1;
// Keep inside canvas
x = constrain(x, r, width - r);
y = constrain(y, r, height - r);
}
void display() {
noStroke();
fill(col);
ellipse(x, y, r * 2, r * 2);
// White specular highlight
fill(255, 255, 255, 80);
ellipse(x - r * 0.3, y - r * 0.3, r * 0.5, r * 0.5);
}
}
Fields (instance variables)
Fields are variables that belong to an instance. Every Ball has its own x, y, vx, vy, r, and col. Changing one ball’s x has no effect on any other ball’s x.
Constructor
The constructor has the same name as the class and no return type. It runs exactly once when you write new Ball(...). Use it to initialise every field — an uninitialised float starts at 0, but it is better to be explicit.
Methods
Methods are functions defined inside the class. They can read and write the instance’s own fields directly — no parameters needed to pass x and y to update() because update() already has access to them.
Instantiating: creating one object
Ball b = new Ball(200, 200);
new Ball(200, 200) calls the constructor with startX = 200, startY = 200, allocates memory for the object, and returns a reference stored in b.
Call methods using dot notation:
b.update(); // move the ball one step
b.display(); // draw the ball
b.x = 100; // access a field directly (fine for quick experiments)
ArrayList of objects
To manage many balls, store them in an ArrayList:
import java.util.ArrayList;
ArrayList<Ball> balls = new ArrayList<Ball>();
void setup() {
size(800, 600);
for (int i = 0; i < 20; i++) {
float x = random(30, width - 30);
float y = random(30, height - 30);
balls.add(new Ball(x, y));
}
}
void draw() {
background(15, 15, 25);
for (Ball b : balls) {
b.update();
b.display();
}
}
The for-each loop (for (Ball b : balls)) visits every ball in order. Call update() then display() on each — separate the logic from the drawing.
Full example: 20 bouncing balls with click-to-add
This complete sketch brings everything together. Click to add more balls; press c to clear all of them.
import java.util.ArrayList;
ArrayList<Ball> balls = new ArrayList<Ball>();
void setup() {
size(800, 600);
// Start with 20 balls at random positions
for (int i = 0; i < 20; i++) {
spawnBall(random(width), random(height));
}
}
void draw() {
background(12, 12, 20);
// Update and draw every ball
for (Ball b : balls) {
b.update();
b.display();
}
// HUD
fill(200);
noStroke();
textSize(13);
textAlign(LEFT, TOP);
text(balls.size() + " balls | click to add | C to clear", 10, 10);
}
void mousePressed() {
spawnBall(mouseX, mouseY);
}
void keyPressed() {
if (key == 'c' || key == 'C') {
balls.clear();
}
}
void spawnBall(float x, float y) {
balls.add(new Ball(x, y));
}
// ── Ball class ───────────────────────────────────────────────────────────────
class Ball {
float x, y;
float vx, vy;
float r;
color baseColor;
color glowColor;
Ball(float startX, float startY) {
x = startX;
y = startY;
vx = random(-4, 4);
vy = random(-4, 4);
r = random(10, 32);
// Pick a random saturated hue
colorMode(HSB, 360, 100, 100);
float hue = random(360);
baseColor = color(hue, 85, 95);
glowColor = color(hue, 50, 100, 50);
colorMode(RGB, 255);
}
void update() {
x += vx;
y += vy;
if (x - r < 0) {
x = r;
vx = abs(vx); // ensure it moves away from the wall
} else if (x + r > width) {
x = width - r;
vx = -abs(vx);
}
if (y - r < 0) {
y = r;
vy = abs(vy);
} else if (y + r > height) {
y = height - r;
vy = -abs(vy);
}
}
void display() {
// Soft glow: slightly larger transparent circle underneath
noStroke();
fill(glowColor);
ellipse(x, y, (r + 12) * 2, (r + 12) * 2);
// Main body
fill(baseColor);
ellipse(x, y, r * 2, r * 2);
// Specular highlight
fill(255, 255, 255, 100);
ellipse(x - r * 0.28, y - r * 0.28, r * 0.45, r * 0.45);
}
// Return true if this ball overlaps position (px, py)
boolean contains(float px, float py) {
return dist(px, py, x, y) < r;
}
}
Notice colorMode(HSB …) inside the constructor — it temporarily switches to HSB so you can easily pick a saturated hue, then switches back to RGB. This is a common Processing pattern: change modes locally for a calculation, restore them when done.
Adding more methods
Once you have a class, adding behaviour is easy — just add a method:
// Inside the Ball class:
void grow(float amount) {
r = min(r + amount, 60); // cap at radius 60
}
boolean isOverlapping(Ball other) {
return dist(x, y, other.x, other.y) < r + other.r;
}
You could then call b.grow(0.5) each frame to make balls slowly expand, or check b.isOverlapping(other) to detect collisions.
Key takeaways
- A class is a blueprint; an instance (
new ClassName()) is one real object built from it. - Fields hold per-instance data; methods define per-instance behaviour.
- The constructor (same name as the class, no return type) initialises every field when the object is created.
- Use
ArrayList<YourClass>to manage a changing collection of objects. - The for-each loop (
for (Ball b : balls)) is the cleanest way to visit every element. - Keep
update()anddisplay()separate — logic and drawing are independent concerns.