Where ideas percolate and thoughts brew

Lissajous Curves

About This Sketch

Named after French physicist Jules Antoine Lissajous, these elegant curves emerge from the intersection of two perpendicular sine waves. The 3:4 frequency ratio creates a pattern that loops and weaves through itself.

Lissajous curves appear in physics and engineering—oscilloscopes display them when analyzing signals, and they describe the motion of pendulums and vibrating strings. The mathematical simplicity (just two sine functions) belies their visual complexity.

Algorithm

Lissajous curves are parametric curves created by combining two perpendicular harmonic oscillations with different frequencies. They create beautiful, complex looping patterns. **Key Concepts:** - **Parametric Equations**: x = A sin(at + δ), y = B sin(bt) - **Frequency Ratio**: a:b determines the curve's complexity - **Phase Difference**: δ affects the curve's shape and orientation - **Harmonic Motion**: Two independent sine waves combined **How it works:** 1. Choose frequency ratio (3:4 creates complex loops) 2. For each time step, calculate x using sine with frequency a 3. Calculate y using sine with frequency b 4. Phase shift (delta) rotates the entire pattern 5. Store points to draw trailing path 6. Fade older points for motion trail effect 7. The curve repeats when both oscillations complete cycles

Pseudocode

SETUP:
  t = 0
  path = empty array
  a = 3  // x frequency
  b = 4  // y frequency
  delta = π/4  // phase shift

DRAW (every frame):
  CLEAR background with transparency

  x = canvas_center_x + amplitude × sin(a × t + delta)
  y = canvas_center_y + amplitude × sin(b × t)

  ADD (x, y) to path
  IF path.length > 300:
    REMOVE oldest point

  FOR i = 0 to path.length:
    opacity = map(i, 0, path.length, 0, 255)
    color = gradient based on i
    DRAW line segment to path[i]

  DRAW point at current (x, y)

  t = t + 0.03

Source Code

let sketch = function(p) {
    let t = 0;
    let path = [];

    p.setup = function() {
        p.createCanvas(400, 300);
        p.colorMode(p.RGB);
    };

    p.draw = function() {
        const colors = getThemeColors();
        p.background(...colors.bg, 20);

        // Parameters for Lissajous curve
        let a = 3;  // x frequency
        let b = 4;  // y frequency
        let delta = p.HALF_PI / 2;  // phase difference

        // Calculate current position
        let x = p.width/2 + 120 * p.sin(a * t + delta);
        let y = p.height/2 + 100 * p.sin(b * t);

        // Add to path
        path.push({x: x, y: y});
        if (path.length > 300) {
            path.shift();
        }

        // Draw path
        p.noFill();
        p.beginShape();
        for (let i = 0; i < path.length; i++) {
            let pt = path[i];
            let alpha = p.map(i, 0, path.length, 0, 255);
            let colorProgress = i / path.length;
            let col = colorProgress < 0.5 ?
                colors.accent1.map((c, idx) => p.lerp(c, colors.accent2[idx], colorProgress * 2)) :
                colors.accent2.map((c, idx) => p.lerp(c, colors.accent3[idx], (colorProgress - 0.5) * 2));

            p.stroke(...col, alpha);
            p.strokeWeight(2);
            p.vertex(pt.x, pt.y);
        }
        p.endShape();

        // Draw current point
        p.fill(...colors.accent2);
        p.noStroke();
        p.circle(x, y, 8);

        t += 0.01;
    };
};