Ephemeral Lines
March 2026
Lines that drift, orbit, and resonate across the canvas — each one a trail of points that grows, fades, and disappears. The technique combines smooth trajectory following with time-based fading to create traces that feel alive, then vanish. What makes it surprising is how the simplest motion rules (drift, orbit, resonance) produce such different visual textures, and how fading the canvas rather than clearing it creates persistent depth from ephemeral lines.
Live Demo
Lines spawn, trace their path, and fade. Each mode produces a different texture. Click anywhere on the canvas to add more lines.
The Line Trail
Each line is a bounded history of points. Rather than storing a single position, every line keeps an array of recent points — the trail that gets drawn. The maxLength cap prevents unbounded memory growth while keeping the visible trail long enough to see.
constructor(x, y, mode) {
this.points = [{ x, y, age: 0 }];
this.maxLength = 200 + Math.random() * 300;
this.lifetime = 400 + Math.random() * 400;
}
Every frame adds a new point and ages all existing points. When the trail exceeds maxLength, the oldest points fall off — a sliding window of position history. The random variation in max length makes some lines feel short and urgent, others long and lingering.
Fading the Canvas
The visual persistence comes from not clearing the canvas completely. Instead of ctx.clearRect(), each frame draws a semi-transparent black rectangle over everything:
ctx.fillStyle = 'rgba(10, 9, 8, 0.03)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
This means new lines draw on top of the faded ghosts of old lines. The alpha of 0.03 creates a slow fade — lines persist visually for many seconds after their points are gone. This is the key to the depth effect: you're seeing the accumulated history of hundreds of lines, each at a different stage of fade.
Drift Mode
Drift mode is the simplest: a point that wanders. The line has an angle and speed, with occasional random turns:
x += Math.cos(this.angle) * this.speed;
y += Math.sin(this.angle) * this.speed;
if (Math.random() < 0.02) {
this.turnRate = (Math.random() - 0.5) * 0.03;
}
The 2% chance of changing direction creates meandering paths rather than straight lines. Combined with the trail fade, this produces something like wind-blown threads — purposeless but coherent.
Orbit Mode
Orbit mode constrains lines to circular paths around a slowly drifting center point:
this.orbitPhase += this.orbitSpeed;
x = this.orbitCenter.x + Math.cos(this.orbitPhase) * this.orbitRadius;
y = this.orbitCenter.y + Math.sin(this.orbitPhase) * this.orbitRadius;
The orbit center wanders slightly each frame (+ (Math.random() - 0.5) * 0.5) and is gently pulled back toward the canvas center. This prevents orbits from drifting off-screen while keeping them from freezing in place. Each line's orbit speed and direction is random — some orbit clockwise, some counter-clockwise, some fast, some slow. The interference patterns of overlapping orbits with different periods create the visual density.
Resonance Mode
Resonance combines multiple harmonic frequencies into a single trajectory — Lissajous-like figures with three overlapping sine waves:
const freq1 = 0.001 * this.resonance;
const freq2 = 0.0017 * this.resonance;
const freq3 = 0.0007 * this.resonance;
x = centerX + Math.sin(t * freq1) * (canvas.width * 0.3)
+ Math.cos(t * freq2) * (canvas.width * 0.15)
+ Math.sin(this.wobblePhase + t * freq3) * 20;
The frequency ratios matter: 0.0017 / 0.001 ≈ φ (the golden ratio), and 0.0007 / 0.001 ≈ 0.7. These aren't exact ratios, which prevents the paths from closing into simple loops — the figure never quite repeats. The third frequency adds a small wobble that breaks the symmetry further, making curves feel organic rather than mathematical.
Gradient Trails
Each line draws itself as a curved path through its points, with a gradient from old to new. The gradient alpha is calculated from point age:
const fadeOldest = Math.max(0, 1 - oldest.age / 100);
const fadeNewest = Math.max(0, 1 - last.age / 50);
gradient.addColorStop(0, `hsla(${this.hue}, 60%, 70%, ${fadeOldest * 0.02})`);
gradient.addColorStop(1, `hsla(${this.hue}, 40%, 60%, ${0.4 * fadeNewest})`);
The oldest points fade first (age / 100) while newest points stay bright. The hue stays in the cyan-teal range (180-240°), creating visual continuity across different lines. The combination of point-age fading and canvas-frame fading means a line disappears from both ends: the trail head fades from age, the trail body fades from canvas accumulation.
What This Shows
Simplicity of rule, complexity of outcome. Three modes of motion — drift, orbit, resonance — produce three visually distinct textures, yet each mode is just a handful of lines of math. The depth comes not from the rules but from accumulation: hundreds of lines, each with fading trails, layered on a canvas that itself fades slowly. The technique demonstrates how visual richness emerges from time + repetition + decay, not from complex algorithms. The surprising part is that resonance mode, which is mathematically the most structured, produces the most organic-looking output — chaos from order.