Trigonometry for Creative Coding
sin(), cos(), and angles — the mathematics behind circular, wave, and orbital motion.
Why trigonometry matters here
You don’t need to understand the unit circle theoretically to use trigonometry in creative coding. You need two facts:
cos(angle)gives the x component of a point on a circlesin(angle)gives the y component of a point on a circle
Everything else — waves, orbits, oscillations, spirals, lissajous figures — follows from these two facts.
Radians vs degrees
p5.js uses radians by default. One full circle = TWO_PI ≈ 6.28.
| Degrees | Radians | p5.js constant |
|---|---|---|
| 0° | 0 | 0 |
| 90° | π/2 | HALF_PI |
| 180° | π | PI |
| 270° | 3π/2 | PI + HALF_PI |
| 360° | 2π | TWO_PI |
Convert between them:
let r = radians(90); // degrees → radians → 1.5708
let d = degrees(PI); // radians → degrees → 180
If you prefer to work in degrees, switch the angle mode:
function setup() {
angleMode(DEGREES); // now all trig functions expect degrees
}
Points on a circle
The most-used pattern in creative coding:
let x = cx + cos(angle) * radius;
let y = cy + sin(angle) * radius;
Where cx, cy is the centre. Use this to place any number of things evenly around a circle:
function setup() {
createCanvas(500, 500);
noLoop();
}
function draw() {
background(20);
translate(width / 2, height / 2);
let count = 12;
let radius = 180;
for (let i = 0; i < count; i++) {
let angle = TWO_PI / count * i;
let x = cos(angle) * radius;
let y = sin(angle) * radius;
fill(200, 100, 255);
noStroke();
circle(x, y, 20);
// Label with index
fill(255);
textAlign(CENTER, CENTER);
textSize(10);
text(i, x, y);
}
}
Oscillation with sin()
sin() returns values between -1 and +1, cycling smoothly. Map it to any range:
function draw() {
background(20);
let t = frameCount * 0.04;
// Horizontal oscillation
let x = map(sin(t), -1, 1, 100, 500);
fill(100, 200, 255);
noStroke();
circle(x, height / 2, 40);
}
Amplitude and frequency:
let amplitude = 100; // how far it moves (half the range)
let frequency = 0.05; // how fast it oscillates
let offset = height / 2; // centre point
let y = offset + sin(frameCount * frequency) * amplitude;
Multiple objects with phase offsets create wave patterns:
function draw() {
background(20);
noStroke();
fill(100, 200, 255, 150);
let n = 20;
for (let i = 0; i < n; i++) {
let x = map(i, 0, n - 1, 50, width - 50);
let phase = TWO_PI / n * i;
let y = height / 2 + sin(frameCount * 0.05 + phase) * 80;
circle(x, y, 24);
}
}
Lissajous curves
Combine sin and cos with different frequencies for complex orbital patterns:
function setup() {
createCanvas(500, 500);
background(20);
stroke(100, 200, 255, 60);
strokeWeight(1.5);
noFill();
}
function draw() {
translate(width / 2, height / 2);
let a = 3, b = 2; // frequency ratio
let delta = frameCount * 0.005; // phase shift
beginShape();
for (let t = 0; t < TWO_PI; t += 0.02) {
let x = 200 * sin(a * t + delta);
let y = 200 * cos(b * t);
vertex(x, y);
}
endShape(CLOSE);
}
Polar to cartesian — spirograph
function setup() {
createCanvas(600, 600);
background(20);
noFill();
stroke(255, 100, 200, 80);
strokeWeight(1);
translate(width / 2, height / 2);
let R = 150, r = 80, d = 120;
beginShape();
for (let t = 0; t < TWO_PI * 12; t += 0.02) {
let x = (R - r) * cos(t) + d * cos((R - r) / r * t);
let y = (R - r) * sin(t) - d * sin((R - r) / r * t);
vertex(x, y);
}
endShape();
}
atan2() — angle from position
atan2(y, x) returns the angle (in radians) from the origin to point (x, y). Essential for making things face toward a target:
function draw() {
background(20);
let angle = atan2(mouseY - height / 2, mouseX - width / 2);
push();
translate(width / 2, height / 2);
rotate(angle);
fill(255, 100, 50);
noStroke();
triangle(30, 0, -20, -12, -20, 12); // arrow pointing right before rotate
pop();
}
Key takeaways
cos(angle)→ x component;sin(angle)→ y component of a unit circle- p5.js uses radians by default;
TWO_PI= full circle; useangleMode(DEGREES)to switch x = cx + cos(a) * r,y = cy + sin(a) * rplaces a point on a circlesin(frameCount * freq)oscillates between -1 and +1 — multiply by amplitude to scale- Phase offsets (
sin(t + phaseOffset)) shift when the cycle starts — great for wave patterns atan2(dy, dx)converts a direction vector to an angle