Note · Canvas

A 2D canvas flow field at 60 fps with zero dependencies

Published 17 July 2026 · The code described runs on the home page and in the Flux lab

The short answer: you do not need WebGL or a library for a spectacular generative background. A sine-based vector field, an array of particles, a translucent veil for the trails: about 120 lines of vanilla JavaScript are enough, provided you follow three performance rules.

1. The field: fake noise that costs nothing

Perlin noise is the classic tool for flow fields, but it demands an implementation or a dependency. Three phase-shifted sine waves give a continuous, organic, animatable field for the price of three Math.sin calls:

function angle(x, y, t) {
  return Math.sin(x * S * 1.3 + t * 0.34) * 1.5
       + Math.cos(y * S * 1.9 - t * 0.27) * 1.5
       + Math.sin((x + y) * S * 0.7 + t * 0.16) * 1.1;
}

The sum exceeds a full turn: all the better, the field wraps around and creates vortices. The t parameter makes everything drift slowly, so the picture is never the same twice.

2. The trails: never erase

The visual secret of a flow field fits in one line: instead of clearing the canvas each frame, you cover it with a nearly transparent veil of the background colour. Each particle draws a segment from its previous position to the new one; older segments gradually fade under the veils.

ctx.fillStyle = 'rgba(10, 12, 18, 0.075)';
ctx.fillRect(0, 0, w, h);

3. The three optimisations that matter

  • Cap devicePixelRatio at 1.5. On a 4K screen, going from 2 to 1.5 nearly halves the pixels to fill, with no visible difference on an animated background.
  • Only draw when watched. An IntersectionObserver stops the loop when the hero leaves the viewport, visibilitychange stops it when the tab goes to the background. A backdrop spinning in a hidden tab is an insult to the battery.
  • Zero allocations in the loop. Particles are mutated in place, never recreated; no temporary objects, no garbage collection mid-animation.

Graceful degradation, non-negotiable

The canvas is decorative (aria-hidden), the headline is real HTML text. Without JavaScript or with prefers-reduced-motion, a CSS gradient takes its place. The spectacle is a bonus, never a requirement: that is what lets you ship a generative background and keep green Core Web Vitals.

See the full-screen demo