Skip to content
Backgrounds & AtmosphereHectorexperiment

Layered Depth Field

Four translucent planes at different parallax depths producing depth with no canvas, no shader, and no main-thread cost.

Built here · no external address

Hector: Solid, honourable, high-craft work.

Exhibit case

The field, live. Move the pointer through it.

Arming the exhibit…

Why it matters

Atmosphere is usually bought at a wildly disproportionate price. A shader background for a hero section means a WebGL context, a render loop running forever, a compile step on first paint, and a battery cost on every device that loads the page — for an effect that is, in most cases, soft coloured light moving slowly.

This produces convincing depth from four radial gradients on four transformed divs. The depth cue is separation and differential motion, not detail: planes at different parallax depths move at different rates, and the eye reads that as space. The lesson is not the technique, it is the question — how much of the perceived quality of an atmospheric effect actually requires the GPU pipeline you were about to reach for?

Key techniques

  • Four planes with assigned depth coefficients (0.02 to 0.18), each translated as a fraction of pointer offset from centre — differential rate is the entire depth illusion.
  • Depth paired with size and alpha: near planes are smaller, tighter, and more transparent; far planes are large and diffuse. Parallax alone without this pairing reads as sliding, not depth.
  • Damped follow via a 900ms transition on transform, so the field has apparent inertia instead of tracking the cursor rigidly.
  • Distinct hues per plane within a narrow range (240–300), which produces colour depth without the field reading as multicoloured.
  • A grain overlay at 3px, which hides gradient banding in dark values far more cheaply than adding gradient stops.
  • An inspection mode that outlines the planes, so the mechanism can be examined rather than admired.

What to steal

The transferable part.

Depth from separation, not detail. Three or four planes moving at different rates will beat one elaborate layer nearly every time, for a fraction of the cost.

Pair depth with scale and alpha. The depth coefficient alone is not enough. Near = small, tight, faint. Far = large, diffuse, stronger. Get this wrong and it reads as a slideshow.

Damped follow. A 900ms transition on the transform is the cheapest possible inertia — no spring, no simulation, no loop. It converts a twitchy field into one with mass.

Grain over gradient stops. A 3px radial-gradient dot pattern at low opacity destroys banding in dark gradients for essentially nothing.

What not to copy

Context-specific or costly.

Do not scale this past a handful of planes. Each plane is a full-viewport composited layer; at eight or ten the memory cost of the layers exceeds what a modest shader would have cost, and the technique stops being the frugal choice.

Do not drive it from scroll and pointer simultaneously without a single source of truth. Two independent transform writers on the same element produce a fight, and the resolution is whichever effect ran last.

Do not use pointer parallax as the only way to perceive something important. It does not exist on touch and it does not exist for keyboard users; the composition must be complete when the pointer never moves.

Inclusion rationale

Included as a cost argument as much as a visual one. Nyx will eventually hold shader work, and this entry establishes the baseline that shader work has to beat: if the WebGL version does not visibly exceed four gradients and a transform, it has not earned its render loop. The inspection mode makes the whole thing verifiable rather than asserted, which is the standard this canon holds atmosphere to.

Technical register

Performance

Zero JavaScript in the steady state — pointer movement writes a transform and the compositor does the rest. No requestAnimationFrame loop, no canvas, no context, no shader compile. The four planes are promoted layers animating transform only, so no repaint or layout occurs during motion. On idle the cost is precisely zero, which is the property a shader background cannot offer.

Mobile

There is no pointer, so there is no parallax; the field renders as a still composition and loses nothing essential, which is the test any pointer-driven effect should pass. pointermove is not bound to touch dragging deliberately — hijacking a touch drag inside a scrolling page to move a background is a bad trade. The aspect ratio is fixed at 1200/620, so the composition crops predictably rather than reflowing its gradient positions.

Accessibility

Parallax is checked against prefers-reduced-motion at mount and never arms when it is set — the field is a still image, not a suppressed animation. Every plane is aria-hidden, since the field is genuinely decorative and carries no information. The title sits at 90% foreground opacity over the darkest region of the composition, clearing 4.5:1 against the plane values behind it. No interaction is required to reach any content, and the effect is inert to keyboard by design rather than by omission.