Lorenz Attractor
A particle tracing the path of chaos
The Lorenz attractor is a system of three differential equations that produces deterministic chaos — the same equations, the same starting point, but the path never repeats. Particles spiral around two lobes, switching unpredictably between them. The butterfly shape you see is the "strange attractor": all nearby trajectories fall into its basin and stay there forever, yet never intersect.
The Lorenz Equations
Three coupled differential equations define how a point moves through 3D space. Each equation describes the rate of change of one coordinate — x, y, and z — as a function of the others. The constants σ (sigma), ρ (rho), and β (beta) are parameters that control the system's behavior.
const sigma = 10; // σ — Prandtl number
const rho = 28; // ρ — Rayleigh number
const beta = 8 / 3; // β — geometric factor
const dx = sigma * (y - x);
const dy = x * (rho - z) - y;
const dz = x * y - beta * z;
Edward Lorenz discovered these equations in 1963 while modeling atmospheric convection. σ represents fluid viscosity, ρ the temperature difference between top and bottom, and β the aspect ratio of the container. The values 10, 28, and 8/3 produce the famous chaotic attractor — small changes in one variable cascade into entirely different trajectories.
The non-obvious insight: the equations contain no randomness. They are deterministic. Every output follows directly from the inputs. The chaos emerges from the mathematics itself — sensitive dependence on initial conditions means that trajectories that start arbitrarily close together diverge exponentially over time.
Time Integration
The differential equations describe continuous change, but computers work in discrete steps. The simulation approximates the trajectory by taking small increments — adding a fraction of the derivative to each coordinate each frame. The dt value determines how fine-grained the integration is.
const dt = 0.005;
x += dx * dt;
y += dy * dt;
z += dz * dt;
Smaller dt means smoother curves but more computation. Larger dt runs faster but can introduce numerical instability — the trajectory might spiral out of control instead of staying on the attractor. The value 0.005 is small enough to trace the shape accurately while running smoothly at 60fps.
Each frame advances the simulation by 10 steps. This keeps the particle moving perceptibly — a single step per frame would barely inch forward. The trade-off: more steps per frame means faster movement but also more computation. On modern hardware, Lorenz integration is trivial; the real work is drawing 8000 line segments.
3D Projection
The attractor exists in three dimensions, but screens are two-dimensional. The visualization projects each 3D point onto the 2D canvas by rotating the viewpoint around the Y axis and discarding the depth coordinate. The rotation angle increases slowly, creating the illusion of the attractor turning in space.
function project(x, y, z, rotAngle) {
const cosA = Math.cos(rotAngle);
const sinA = Math.sin(rotAngle);
const xr = x * cosA - z * sinA;
const zr = x * sinA + z * cosA;
const scale = Math.min(canvasW, canvasH) / 55;
const px = canvasW / 2 + xr * scale;
const py = canvasH / 2 + y * scale - zr * 0.3;
return { x: px, y: py };
}
The Y-axis rotation keeps the two lobes symmetrical — the attractor's characteristic butterfly shape is best viewed from a slight incline. The - zr * 0.3 term adds a subtle depth cue: points further back appear slightly higher, creating a hint of perspective without full 3D rendering.
The scale factor of 55 comes from measuring the attractor's typical range — x spans roughly -20 to 20, y spans -30 to 30, z spans 0 to 50. The scale divides by the largest dimension to ensure the entire shape fits comfortably within the canvas.
Trail History
A single point traveling through the attractor leaves no visible trace. The simulation builds a path by storing each position in a history array, drawing line segments from each point to the next. The history caps at 8000 positions — enough to show the full attractor shape, but not so many that performance suffers.
const maxHistory = 8000;
const history = [];
history.push({ x, y, z });
if (history.length > maxHistory) {
history.shift();
}
Each frame adds 10 new points and removes old ones if the limit is exceeded. This creates a fixed-length window into the trajectory — the tail fades as the head advances. You're seeing not the infinite attractor, but a finite segment that continuously updates.
The history array is the only persistent state. The Lorenz system itself is memoryless — each new position depends only on the current position, not the past. The trail visualization superimposes memory on a memoryless process, making the invisible structure visible.
Gradient Coloring
The trail is not a uniform color. Older segments fade toward transparency; newer segments shine bright. The color gradient encodes temporal position — how recently the particle visited each point. This creates a warm terracotta-to-amber shift that mirrors the site's palette.
function getColor(index, total) {
const t = index / total;
// Warm terracotta to amber gradient
const r = Math.floor(196 + t * 40);
const g = Math.floor(168 - t * 80);
const b = Math.floor(130 - t * 80);
const a = 0.3 + t * 0.7;
return `rgba(${r}, ${g}, ${b}, ${a})`;
}
The index runs from 0 (oldest) to total-1 (newest). As t approaches 1, the color shifts from deep terracotta toward a lighter amber, and the alpha channel increases from 0.3 to 1.0. The newest point is drawn last in full opacity — a bright head that marks the particle's current position.
The line width also varies: older segments are thin, newer segments are thick. A double visual cue — color and thickness — guides attention to the leading edge. The particle doesn't just exist; it moves, and the trail shows where it came from and how long ago.
What This Reveals
The Lorenz attractor demonstrates that deterministic does not mean predictable. The equations contain no randomness, yet long-term prediction is impossible. Two trajectories that start 0.0001 units apart will diverge exponentially until they occupy opposite lobes. This sensitive dependence is the heart of chaos theory — the "butterfly effect" is literally named after this attractor's shape.
The attractor itself is a strange attractor: a fractal structure that trajectories approach but never reach. Zoom in on any part of the attractor, and you find more detail — it has non-integer dimension, somewhere between 2D and 3D. The particle circles infinitely close to itself without ever repeating, constrained to the basin but never completing a cycle.
What you're watching is the geometry of unpredictability. The equations are simple. The integration is straightforward. The projection is almost trivial. Yet together they produce a shape of infinite complexity — proof that chaos doesn't require complexity to generate it.