Harmonography

assets/vertigo.png

Inspiration The spiralling harmonic patterns decorating Vertigo's opening credits (1958)

It’s been 60 years since Vertigo came out, classic among other things for introducing computer graphics on film. In brief, the story goes, because no equipment at hand could accurately handle the Lissajous derived designs of Saul Bass for the title sequence, John Whitney was called in to assist with the tooling and his solution involved repurposing World War II legacy machine the M5 AA Director originally devised for coordinating anti aircraft cannons.

swing.js Pair of damped pendula in relative symmetry dancing around making waves

The core math describing this kind of plot relates to damped harmonic oscillation and is fortunately pretty basic, often expressed as,

x(t) = A sin(t f + p) e-dt

Where A is the amplitude or deviation from rest, f angular frequency, p denotes phase, d is the damping factor, and t stands for time. In other words,

function pendulum(A = 0, f = 1, p = 0, d = 0) {
  return t => A * Math.cos((t * f) + p) * Math.exp(-d * t)
}

Running the above along both axes is enough for drawing simple elliptical shapes such as the Lemniscate of Gerono, for example,

const lookup = pendulum2d(175, 100)

const canvas = document.createElement('canvas')
const target = canvas.getContext('2d')

const center = { x: canvas.width * 0.5, y: canvas.height * 0.5 }

setInterval(() => {
  const t = Date.now()
  const { x, y } = lookup(t)

  target.fillRect(center.x + x, center.y + y, 1, 1)
}, 10)

// Pick defaults accomodating the 1:2 relationship in frequency
// that's known to create figures of eight, note the absense of damping
function pendulum2d(A = 1, B = A, a = 1, b = a * 2, p = Math.PI * 0.5) {
  const x = pendulum(A, a, p)
  const y = pendulum(B, b, p)
  
  return t => ({ x: x(t), y: y(t) })
}

But results get even more interesting with coupling or superposition as in the case of the harmonograph,

// Emulating harmonograph style gestures, adapted from
// https://workout.thewhodidthis.com/harmonography

const head = pendulum2d(100, 50, 1, 3)
const foot = pendulum2d(50, 100, 3, 1)

for (let t = 0; t < 100; t += 0.01) {
  const { x1: x, y1: y } = head(t)
  const { x2: x, y2: y } = foot(t)

  target.fillRect(x1 + x2, y1 + y2, 1, 1)
}

Module source, thewhodidthis/swing →

Further reading