Speed Lines
Parallel streaks trail an element at its real speed and die as it lands with a squash and a small bounce, or radial lines snap onto one point for a manga impact frame
$ pnpm dlx shadcn@latest add @remocn/speed-linesUsage
SpeedLines is a motion accent with two modes. In trail mode it wraps an
element and flies its center from from to to. The element leaves from rest,
peaks mid-flight and crosses the target still moving at 45% of its top speed.
A damped spring catches it there: it overshoots by 3.5% of the flight, swings
back once with a small echo and settles exactly on to. bounce sets how much
momentum carries into the landing, and bounce={0} stops it dead on the
target.
The element also changes shape with its motion. While it moves, it stretches
along its path in proportion to its speed, up to 1.3×, and narrows across it,
so its area stays the same. Past the target it squashes, down to 0.8× along the
path and wider across, and rounds out again as the spring pulls it back.
squash scales the deformation, and squash={0} keeps the element rigid.
Short parallel streaks shoot past behind it. The element's real velocity drives them, as the difference between this frame's position and the previous frame's, bounce included. Streaks exist only while the element is fast, each one's length is proportional to the speed, and they run parallel to the path. Each spawns at a seeded lane across the element's width and slides back like air rushing past. As the element brakes, every streak shortens. The last ones die on the frame after contact, so the overshoot and the swing back draw none.
import { Backdrop } from "@/components/remocn/backdrop";
import { SpeedLines } from "@/components/remocn/speed-lines";
export const Entrance = () => (
<Backdrop
fill={{ type: "color", value: "#002fa7" }}
padding={0}
radius={0}
shadow=""
>
<SpeedLines
from={{ x: -0.25, y: 0.5 }}
to={{ x: 0.5, y: 0.5 }}
spread={80}
>
<div
style={{
padding: "20px 36px",
borderRadius: 20,
background: "#ffffff",
color: "#002fa7",
fontFamily: "Inter, sans-serif",
fontSize: 40,
fontWeight: 600,
}}
>
Shipped
</div>
</SpeedLines>
</Backdrop>
);The component fills its parent and paints no background, so it composites over
any scene. Put it in a full-frame layer. Set spread to the element's size
across the direction of travel, so the streaks leave from behind its silhouette.
Without children, trail mode flies a disc of diameter spread, as in the
preview. Sizes are reference px at 720p and scale with the composition height.
Everything derives from the frame and the seed, so seeking and rendering are
deterministic.
The squash and stretch scale the element around its own center, turned to the
direction of travel, so it works for any from and to and the element never
drifts off its landing point. It deforms children too, text included. For a
text-heavy card, lower squash to about 0.25.
On a landing
focus is an impact frame. Radial lines rush in from the edges of the frame,
snap onto the point x, y three frames later, hold for about five frames,
then retract and vanish. They never come closer to the point than spread.
getSpeedLinesTimeline returns impact: the contact frame of a trail, where
the element crosses to and the spring takes over, and the snap frame of a
focus. Start the focus that many frames early, and its lines converge while the
card flies in and snap on the frame it lands, as the card starts to squash.
The focus layer comes first, so its rays pass behind the card.
import { Sequence } from "remotion";
import { Backdrop } from "@/components/remocn/backdrop";
import {
getSpeedLinesTimeline,
SpeedLines,
} from "@/components/remocn/speed-lines";
const ENTER = 20;
const LAND = ENTER + getSpeedLinesTimeline().impact;
const FOCUS = getSpeedLinesTimeline({ mode: "focus" }).impact;
export const RevenueBeat = () => (
<Backdrop
fill={{ type: "color", value: "#0a0a0a" }}
padding={0}
radius={0}
shadow=""
>
<Sequence from={LAND - FOCUS}>
<SpeedLines mode="focus" x={0.5} y={0.5} spread={160} />
</Sequence>
<Sequence from={ENTER}>
<SpeedLines
from={{ x: -0.3, y: 0.5 }}
to={{ x: 0.5, y: 0.5 }}
spread={120}
>
<div
style={{
padding: "28px 44px",
borderRadius: 24,
background: "#fafafa",
color: "#0a0a0a",
fontFamily: "Inter, sans-serif",
fontSize: 64,
fontWeight: 600,
fontVariantNumeric: "tabular-nums",
}}
>
$128k
</div>
</SpeedLines>
</Sequence>
</Backdrop>
);For a smaller landing, put trim-burst on the same point at LAND instead of
the focus.
Timing
At 30 fps and speed={1}, trail mode with the default duration={14},
bounce={0.5}, squash={0.5} and a 960 px flight:
| Frames | Motion |
|---|---|
| 0 | The element waits at from; no streaks yet |
| 0–4 | Launch; streaks appear once the element passes 5 px per frame, and it stretches as it speeds up |
| 4–11 | Cruise up to 93 px per frame, stretched up to 1.25× along the path; streaks reach 1.6 frames of travel |
| 11–14 | Braking; the stretch eases and every streak shortens with the speed |
| 14 | Contact with to (impact) at 45% of the top speed; streaks are gone from frame 15 |
| 16 | Overshoot peak, 34 px (3.5%) past the target, squashed to 0.83× along the path and 1.21× across |
| 16–21 | Swings back through the target and rounds out |
| 22 | Echo, 3 px short of the target |
| 26–31 | Visually at rest from 26, exactly on to at 31 (end) |
Focus mode:
| Frames | Motion |
|---|---|
| 0–3 | Rays rush in from the frame edges and accelerate |
| 3 | Impact: every ray snaps to its closest point, 1–1.6× spread away |
| 3–8 | Hold; the inner ends ease back out by up to 12% |
| 8–13 | Rays retract to the edges in a seeded order and vanish |
speedLinesLengthis 31, the frame where the default trail settles exactly.getSpeedLinesTimeline({ mode, duration, bounce })returnsimpactandend. For a trail,impactisdurationandendadds the landing: 6 frames atbounce={0}, 17 at the default and 29 atbounce={1}. For a focus, they are 3 and 13.bouncesets the overshoot: 1.1% of the flight at 0, 3.5% at 0.5 and 7.2% at 1, which also swings back 14 px short of the target. The overshoot grows with the arrival speed, so a shorterdurationlands harder: 8.3% at 6 frames, 1.2% at 40.getSpeedLinesDuration({ mode, duration, bounce, delay, speed })returns the preview length, which adds a 24-frame hold: 55 frames for trail and 37 for focus.delaycounts composition frames before the motion and is not scaled byspeed. At another speed or frame rate, divide the motion frames byspeedand multiply byfps / 30. A higherspeedalso draws longer streaks, because the element really moves faster.
| Prop | Type | Default | Description |
|---|---|---|---|
mode | "trail" | "focus" | "trail" | Streaks behind a moving element, or an impact frame of converging lines |
from | { x: number; y: number } | { x: -0.25, y: 0.5 } | Trail start of the element's center as fractions of the composition; off-screen values are fine |
to | { x: number; y: number } | { x, y } | Trail landing point; falls back to the x, y point |
x | number | 0.5 | Accent point as a fraction of the width: the focus center, and the trail landing point when to is not set |
y | number | 0.5 | Accent point as a fraction of the height |
count | number | 12 | Streaks spawned per flight in trail, about a third alive at once; rays in focus. Clamped to 1–64 |
weight | number | 2 | Stroke width in reference px, clamped to 0.5–12; caps are round |
spread | number | 96 | Trail: width of the streak band across the path, and the fallback disc diameter. Focus: the closest the rays come to the point. Reference px, clamped to 8–480 |
duration | number | 14 | Trail frames from launch to contact at 30 fps, clamped to 4–120; the landing adds 6–29 frames depending on bounce |
bounce | number | 0.5 | Momentum carried into the landing, 0–1. At 0 the element stops dead on the target; at 0.5 it overshoots 3.5% of the flight and swings back once; at 1 it overshoots 7.2% with a visible echo |
squash | number | 0.5 | Squash and stretch, 0–1. The element stretches along its path with its speed, up to 1.3×, and squashes past the target, down to 0.8×, keeping its area. 0 keeps it rigid |
color | string | "#ffffff" | Streaks, rays and the fallback disc |
seed | number | 1 | Seeds streak timing, lanes and lengths, and ray angles and radii |
delay | number | 0 | Composition frames before the motion starts; not scaled by speed |
speed | number | 1 | Playback multiplier; 0 freezes the first frame |
children | ReactNode | — | The element trail moves and deforms, or places on the focus point; without it, trail flies a disc, and null draws the streaks alone |
className | string | — | Optional class name on the full-frame root |