플로우 필드

Flow field

화면 위에 깔린 보이지 않는 벡터 장(field)을 따라 수백 개의 입자가 흘러가며 남기는 궤적.

다른 이름: 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); })();

모든 좌표에 "이 방향으로 가라"는 각도를 하나씩 배정한 지도가 있다고 생각하면 됩니다. 이 각도를 픽셀마다 완전히 무작위로 주면 지저분한 잡음이 되니, 대신 펄린 노이즈 같은 "부드럽게 이어지는 난수"로 각도를 만듭니다. 이웃한 좌표끼리 각도가 비슷해서 입자들이 물이 흐르듯 부드러운 곡선을 그립니다.

입자마다 매 프레임 자기 위치의 필드 각도를 읽어 그 방향으로 한 걸음 이동하고, 지나온 자리에 얇은 선을 남깁니다. 화면을 완전히 지우지 않고 아주 옅은 반투명 사각형으로 덮은 뒤 그 위에 새 선을 그리면, 오래된 궤적이 서서히 사라지면서 잔상이 남는 효과가 납니다.

포스터 배경, 앨범 커버, 로딩 화면처럼 "유기적이면서도 통제된" 텍스처가 필요할 때 씁니다. 필드를 만드는 노이즈 함수의 스케일을 바꾸면 결이 성긴 흐름과 촘촘한 소용돌이 사이를 오갈 수 있습니다.

언제 쓰나

정적인 그라데이션 대신 "살아있는" 배경이 필요할 때. 입자 수와 트레일 투명도로 밀도를 조절하세요.