Trigonometry for Creative Coding
Apply sin, cos, and radians to create circular motion, oscillation, Lissajous figures, and a solar system.
Why trigonometry?
Sin and cos are the mathematical tools behind circles, waves, oscillation, rotation, and any motion that repeats. They sound intimidating but the core idea is simple: both functions take an angle and return a number between -1 and 1. That small output range is surprisingly powerful.
Angles: degrees vs radians
Processing’s sin(), cos(), and rotate() all work in radians, not degrees. The conversion:
radians = degrees × (PI / 180)
Processing provides the helper radians(deg) so you rarely do the multiplication by hand. A few useful constants are built in:
PI // 3.14159… — half a circle
TWO_PI // 6.28318… — full circle
HALF_PI // 1.5708… — quarter circle
QUARTER_PI // 0.7854… — eighth of a circle
// Convert
float r = radians(90); // 1.5708 (same as HALF_PI)
float d = degrees(PI); // 180.0
Circular motion
The classic formula: to place something on a circle of radius r centred at (cx, cy):
x = cx + r * cos(angle)
y = cy + r * sin(angle)
As angle increases from 0 to TWO_PI, the point traces a full circle counter-clockwise.
float angle = 0;
void setup() {
size(500, 500);
}
void draw() {
background(15);
float cx = width / 2;
float cy = height / 2;
float r = 160;
float x = cx + cos(angle) * r;
float y = cy + sin(angle) * r;
// Draw orbit path
noFill();
stroke(60);
ellipse(cx, cy, r * 2, r * 2);
// Draw moving dot
noStroke();
fill(100, 180, 255);
ellipse(x, y, 30, 30);
angle += 0.03;
}
Oscillation
sin() alone, mapped over time, produces a smooth back-and-forth wave — oscillation:
// Simple horizontal oscillation
float x = width / 2 + sin(frameCount * 0.04) * 200;
ellipse(x, height / 2, 40, 40);
// Vertical oscillation with amplitude and speed control
float amplitude = 120;
float speed = 0.05;
float y = height / 2 + amplitude * sin(frameCount * speed);
ellipse(width / 2, y, 40, 40);
Layer multiple oscillations with different speeds for complex motion:
float x = width / 2 + sin(frameCount * 0.03) * 150
+ sin(frameCount * 0.07) * 60;
float y = height / 2 + cos(frameCount * 0.04) * 100
+ cos(frameCount * 0.09) * 40;
ellipse(x, y, 20, 20);
Lissajous figures
A Lissajous figure is the path traced when x and y oscillate at different frequencies. They produce elegant looping curves:
float t = 0;
void setup() {
size(600, 600);
background(10);
stroke(100, 200, 255, 120);
strokeWeight(1.5);
noFill();
}
void draw() {
// a = x frequency, b = y frequency, delta = phase offset
int a = 3, b = 2;
float delta = HALF_PI;
float r = 220;
// Draw the complete figure each frame (it's static unless delta changes)
background(10);
beginShape();
for (float angle = 0; angle <= TWO_PI; angle += 0.01) {
float x = width / 2 + r * sin(a * angle + delta);
float y = height / 2 + r * sin(b * angle);
vertex(x, y);
}
endShape(CLOSE);
// Slowly rotate the phase for animation
// delta += 0.005;
}
Try different integer ratios for a and b — 3:2, 4:3, 5:4, 1:2. Each ratio produces a distinctly shaped curve.
Rotating shapes with rotate()
rotate() turns the coordinate system by a given angle (in radians). Combine with translate() and pushMatrix()/popMatrix() to rotate shapes around their own centre:
float angle = 0;
void setup() {
size(400, 400);
rectMode(CENTER);
}
void draw() {
background(20);
pushMatrix();
translate(width / 2, height / 2); // move origin to canvas centre
rotate(angle); // rotate coordinate system
fill(255, 160, 50);
noStroke();
rect(0, 0, 100, 100); // square now rotates around its centre
popMatrix();
angle += 0.02;
}
pushMatrix() saves the current transformation state; popMatrix() restores it. Everything drawn between the pair is affected by the transformations applied inside.
Full example: solar system with orbiting planets
This sketch puts circular motion and nested transformations together. Each planet orbits the sun; a moon orbits each planet.
// Planet data: [orbit radius, orbit speed, planet radius, colour]
float[][] planets = {
{ 80, 0.047, 8, #AAAAAA }, // Mercury
{ 130, 0.034, 12, #E8C080 }, // Venus
{ 190, 0.024, 13, #4488EE }, // Earth
{ 260, 0.019, 10, #CC6644 }, // Mars
};
// Moon data (for Earth only, index 2): [orbit r, speed, radius]
float[] moon = { 28, 0.13, 5 };
float time = 0;
void setup() {
size(700, 700);
}
void draw() {
background(8, 8, 20);
// Draw stars (stationary noise field)
randomSeed(7);
fill(255, 200);
noStroke();
for (int i = 0; i < 120; i++) {
float sx = random(width);
float sy = random(height);
ellipse(sx, sy, random(1, 3), random(1, 3));
}
pushMatrix();
translate(width / 2, height / 2);
// Sun
noStroke();
fill(255, 220, 60);
ellipse(0, 0, 50, 50);
// Orbit rings
noFill();
stroke(255, 255, 255, 25);
for (float[] p : planets) {
ellipse(0, 0, p[0] * 2, p[0] * 2);
}
// Planets
for (int i = 0; i < planets.length; i++) {
float orbitR = planets[i][0];
float speed = planets[i][1];
float pRadius = planets[i][2];
color pColor = color(unhex(hex((int) planets[i][3], 6)));
float angle = time * speed;
float px = cos(angle) * orbitR;
float py = sin(angle) * orbitR;
noStroke();
fill(pColor);
ellipse(px, py, pRadius * 2, pRadius * 2);
// Moon orbiting Earth (index 2)
if (i == 2) {
pushMatrix();
translate(px, py);
float moonAngle = time * moon[1];
float mx = cos(moonAngle) * moon[0];
float my = sin(moonAngle) * moon[0];
fill(200, 200, 210);
ellipse(mx, my, moon[2] * 2, moon[2] * 2);
popMatrix();
}
}
popMatrix();
time += 0.5;
}
The key structure: every orbit uses cos(angle) * r and sin(angle) * r to find the position. The moon uses an extra pushMatrix/popMatrix pair centred on Earth’s current position, so it orbits Earth in Earth’s own local coordinate system.
Key takeaways
- Processing uses radians;
radians(deg)converts, or use thePI/TWO_PI/HALF_PIconstants. x = cx + r * cos(angle)andy = cy + r * sin(angle)place anything on a circle.sin(frameCount * speed) * amplitudeproduces smooth oscillation.- Lissajous figures arise from different x and y oscillation frequencies.
rotate()always rotates around the current origin; move the origin withtranslate()first.pushMatrix()/popMatrix()save and restore transformations — use them around every independent rotating element.