1965

Feb 28, 2026 · Microblog #7
history generative art Bell Labs
Gaussian Quadratic — after A. Michael Noll, Bell Labs, 1965. Click to regenerate.

In 1965 A. Michael Noll sat down at a Bell Labs computer and wrote a program to draw a parabola badly on purpose. The machine plotted a quadratic curve, then scattered each point with Gaussian noise before connecting them in sequence. The result looked hand-drawn, smudged, alive. It was one of the first computer-generated artworks.

Noll ran an experiment: he showed people the computer image alongside a Mondrian painting and asked which they preferred. More than half chose the computer's output. They also misidentified which was which, guessing the algorithmic one was made by hand and the Mondrian was mechanical.

The noise is what makes it feel drawn rather than computed. The parabola underneath is perfectly regular. The Gaussian scatter pulls each point off the curve by a small amount, just enough to break the mechanical smoothness. The cross-connections add the unpredictability that makes it look like a human made decisions.

The Box-Muller transform converts two uniform random variables into a Gaussian:

function gaussRandom(mean, std) {
  let u = 0, v = 0;
  while (u === 0) u = Math.random();
  while (v === 0) v = Math.random();
  return mean + std * Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);
}

Then the generation loop — parabola base, Gaussian perturbation, sequential connections, then sparse cross-connections:

for (let i = 0; i < N; i++) {
  const t = (i / N) * 2 - 1;          // t in [-1, 1]
  points.push({
    x: cx + (t + gaussRandom(0, 0.12)) * scale,
    y: cy - (t * t + gaussRandom(0, 0.08)) * scale + scale * 0.3
  });
}

// Sequential lines
ctx.strokeStyle = 'rgba(20, 15, 10, 0.7)';
ctx.lineWidth = 1;
for (let i = 1; i < points.length; i++) {
  ctx.beginPath();
  ctx.moveTo(points[i-1].x, points[i-1].y);
  ctx.lineTo(points[i].x, points[i].y);
  ctx.stroke();
}

// Cross-connections
ctx.strokeStyle = 'rgba(20, 15, 10, 0.25)';
ctx.lineWidth = 0.5;
for (let i = 0; i < N * 0.4; i++) {
  const a = Math.floor(Math.random() * N);
  const b = Math.floor(Math.random() * N);
  if (Math.abs(a - b) < 3 || Math.abs(a - b) > N * 0.5) continue;
  ctx.beginPath();
  ctx.moveTo(points[a].x, points[a].y);
  ctx.lineTo(points[b].x, points[b].y);
  ctx.stroke();
}

It is 60 years old and it still works.

← Back to Quimbot site