Flow Field
Hundreds of particles ride a seeded, slowly changing vector field and leave fine fading trails, as a seamless loop
$ pnpm dlx shadcn@latest add @remocn/flow-fieldUsage
FlowField draws particles that follow a vector field across the frame. The
field angle at each point is a sum of four seeded sine waves, and it shifts
slowly over the loop. Each particle is born at a seeded position, sometimes
just outside the frame, and steps along the field for its life. Its
head moves first and the trail grows behind it; then the head stops and the
tail catches up, so trails grow in and drain out instead of popping. Births
are spread over the life, so the frame always holds a steady population.
Each trail is split into six age segments that get fainter and thinner
toward the tail. A seeded share of particles, accentRatio, is drawn in
accent.
import { FlowField } from "@/components/remocn/flow-field";
export const Texture = () => <FlowField />;The component fills its parent and paints its own background. Particle
speed and stroke width scale with the short side of the composition, and
strokeWidth is in px at 720p. Positions come from Remotion's seeded
random(), so a seed always produces the same field and every frame
renders the same way.
Timing
The field and every particle's life cycle repeat every loopFrames / speed
frames, 300 frames (10 seconds) at the defaults. The last frame of the loop
flows into the first without a seam, so a Sequence of any multiple of the
period, or a Remotion <Loop> of that length, plays as a continuous
background.
A particle lives loopFrames / round(loopFrames / lifeFrames) frames, 150
at the defaults, so a whole number of lives fits in one loop. The trail is
capped at 60% of the life: the head travels for life − trail frames, then
holds while the tail drains.
There is no build-up: frame 0 already shows a full population.
Performance
Each frame recomputes every visible trail from the particle's birth, so the
cost grows with particles × lifeFrames. The defaults render quickly; 800
particles with 300-frame lives is about five times heavier. Lower
particles before lowering trailLength if renders get slow.
Variations
A high trailLength with a low particles count gives long, sparse strands.
accentRatio={0} keeps the field monochrome, and accentRatio={1} draws
everything in the accent. Change seed for a different field layout.
<FlowField
particles={160}
trailLength={110}
strokeWidth={2}
accentRatio={0.25}
background="#0b1020"
color="#9fb4ff"
accent="#ffffff"
seed={5}
/>| Prop | Type | Default | Description |
|---|---|---|---|
particles | number | 300 | Particles in flight |
trailLength | number | 72 | Trail length in frames of travel, capped at 60% of the particle life |
loopFrames | number | 300 | Frames for one seamless loop of the field and the particle life cycles |
lifeFrames | number | 150 | Particle lifetime in frames, adjusted so a whole number of lives fits in loopFrames |
accentRatio | number | 0.1 | Share of particles drawn in the accent color, 0 to 1 |
strokeWidth | number | 1.4 | Trail width in px at 720p, at the head; older segments thin to half |
seed | number | 1 | Seeds the field, the spawn points and which particles take the accent |
background | string | "#111111" | Background fill |
color | string | "#FFFFFF" | Trail color |
accent | string | "#E5322D" | Color of the accent share of trails |
speed | number | 1 | Playback multiplier; the loop period is loopFrames / speed |