Processing Beginner Article 9

Trigonometry for Creative Coding

Apply sin, cos, and radians to create circular motion, oscillation, Lissajous figures, and a solar system.

⏱ 20 min trigonometry sin cos radians circular motion oscillation rotation

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 the PI/TWO_PI/HALF_PI constants.
  • x = cx + r * cos(angle) and y = cy + r * sin(angle) place anything on a circle.
  • sin(frameCount * speed) * amplitude produces smooth oscillation.
  • Lissajous figures arise from different x and y oscillation frequencies.
  • rotate() always rotates around the current origin; move the origin with translate() first.
  • pushMatrix() / popMatrix() save and restore transformations — use them around every independent rotating element.