Flow field

플로우 필드

Hundreds of particles drifting along an invisible vector field, leaving trails as they go.

Also known as: Vector field artNoise field
···
js
const c = document.createElement('canvas');
document.body.appendChild(c);
const ctx = c.getContext('2d');
const cs = getComputedStyle(document.documentElement);
const BG = cs.getPropertyValue('--bg').trim() || '#0d0d12';
const ACCENT = cs.getPropertyValue('--accent').trim() || '#5b5bf7';
let w, h;
function resize() {
  const dpr = Math.min(devicePixelRatio || 1, 2);
  w = innerWidth; h = innerHeight;
  c.width = w * dpr; c.height = h * dpr;
  c.style.width = '100%'; c.style.height = '100%';
  ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  ctx.fillStyle = BG; ctx.fillRect(0, 0, w, h);
}
addEventListener('resize', resize);
resize();

// 부드럽게 이어지는 값 노이즈 (해시 기반)
function hash(i, j) { const s = Math.sin(i * 127.1 + j * 311.7) * 43758.5453; return s - Math.floor(s); }
function noise2(x, y) {
  const xi = Math.floor(x), yi = Math.floor(y), xf = x - xi, yf = y - yi;
  const u = xf * xf * (3 - 2 * xf), v = yf * yf * (3 - 2 * yf);
  const a = hash(xi, yi), b = hash(xi + 1, yi), cc = hash(xi, yi + 1), d = hash(xi + 1, yi + 1);
  return a + (b - a) * u + (cc - a) * v + (a - b - cc + d) * u * v;
}

const N = 320, SCALE = 0.0045;
const pts = Array.from({ length: N }, () => ({ x: Math.random() * w, y: Math.random() * h }));
function tick() {
  ctx.fillStyle = 'rgba(13,13,18,0.045)';
  ctx.fillRect(0, 0, w, h);
  ctx.strokeStyle = ACCENT;
  ctx.globalAlpha = 0.55;
  ctx.lineWidth = 1.1;
  for (const p of pts) {
    const angle = noise2(p.x * SCALE, p.y * SCALE) * Math.PI * 4;
    const nx = p.x + Math.cos(angle) * 2, ny = p.y + Math.sin(angle) * 2;
    ctx.beginPath(); ctx.moveTo(p.x, p.y); ctx.lineTo(nx, ny); ctx.stroke();
    p.x = nx; p.y = ny;
    if (p.x < 0 || p.x > w || p.y < 0 || p.y > h) { p.x = Math.random() * w; p.y = Math.random() * h; }
  }
  ctx.globalAlpha = 1;
}
for (let i = 0; i < 80; i++) tick(); // 스크린샷 시점에도 궤적이 보이도록 미리 진행
(function loop() { tick(); requestAnimationFrame(loop); })();

Picture every point on the screen carrying a hidden arrow that says "go this way." Assign that angle purely at random per pixel and you get static; assign it with a smoothly-varying noise function instead and neighbouring angles stay close, so particles trace flowing, water-like curves.

Each frame, every particle reads the field angle at its own position, steps one unit in that direction, and leaves a thin line behind. Instead of clearing the canvas each frame, the demo paints a barely-visible translucent rectangle over it first — old trails fade instead of vanishing, which is what produces the streaked, painterly look.

Designers reach for this for poster backgrounds, album art and loading screens — anywhere that needs a texture that feels organic but still obviously rule-based. Changing the scale of the underlying noise trades a loose, sweeping flow for a tight, swirling one.

When to use

Use it when a plain gradient feels too static. Tune particle count and trail opacity to control density.