Trim Burst
Short strokes shoot out from a point and vanish in flight, each start chasing its end, to punctuate a click, a landing number or a beat
$ pnpm dlx shadcn@latest add @remocn/trim-burstUsage
TrimBurst is a punctuation accent. A ring of short straight strokes shoots
out from a point and vanishes in flight. Each stroke is animated like Trim
Paths in After Effects, with its own curve for each end. The end, the outer
point, shoots out on a fast ease-out and brakes hard at its final distance.
The start, the inner point, holds for about three frames, then chases the end
on a smoother ease-in-out and catches up. So the stroke grows, travels near
full length, and shrinks to nothing at its outer distance. Lengths, angles and
launch times vary slightly per stroke from seed, so the ring never reads as
a rigid asterisk.
import { Backdrop } from "@/components/remocn/backdrop";
import { TrimBurst } from "@/components/remocn/trim-burst";
export const Accent = () => (
<Backdrop
fill={{ type: "color", value: "#002fa7" }}
padding={0}
radius={0}
shadow=""
>
<TrimBurst />
</Backdrop>
);The component fills its parent, paints no background, and draws into one SVG,
so it composites over any scene. x and y place the burst center as
fractions of the composition. innerRadius, reach, strokeLength and
weight are pixels at a 720px-tall composition and scale with the height. The
same props look the same at 720p, 1080p and 4K. Everything derives from the
Remotion frame and the seed, so seeking and rendering are deterministic.
On a moment
Start the burst on the event frame: put it in a Sequence that begins there
and center it on the element it punctuates. Set innerRadius just outside the
element so the strokes are born at its edge. Here a badge springs in and
reaches full size around frame 6, and the burst fires on that frame.
trimBurstLength covers the whole burst at the default jitter and speed,
so it is a safe Sequence duration.
import { Sequence, spring, useCurrentFrame, useVideoConfig } from "remotion";
import { Backdrop } from "@/components/remocn/backdrop";
import { TrimBurst, trimBurstLength } from "@/components/remocn/trim-burst";
const LAND = 6;
const Badge = () => {
const frame = useCurrentFrame();
const { fps, height } = useVideoConfig();
const size = 72 * (height / 720);
const scale = spring({ frame, fps, config: { damping: 14, stiffness: 160 } });
return (
<div
style={{
position: "absolute",
left: "70%",
top: "40%",
width: size,
height: size,
borderRadius: "50%",
background: "#ffffff",
transform: `translate(-50%, -50%) scale(${scale})`,
}}
/>
);
};
export const BadgeLanding = () => (
<Backdrop
fill={{ type: "color", value: "#002fa7" }}
padding={0}
radius={0}
shadow=""
>
<Badge />
<Sequence from={LAND} durationInFrames={trimBurstLength}>
<TrimBurst x={0.7} y={0.4} innerRadius={48} reach={40} />
</Sequence>
</Backdrop>
);The badge is 72px wide and overshoots to about 81px, so an innerRadius of 48
keeps every stroke clear of it. left and top use the same fractions as x
and y, so the burst stays centered on the badge at any composition size.
Timing
At 30 fps, speed={1} and the defaults:
| Frames | Motion |
|---|---|
| 0 | Nothing is drawn; every stroke still sits on the inner radius |
| 0–3 | Strokes launch one after another in a seeded order |
| 1–7 | Each end covers 36% of its reach one frame after launch and 60% after two, while its start holds near the inner radius, so the stroke grows |
| 4–9 | Strokes run near full length and travel outward as the ends brake and the starts pick up speed |
| 7–18 | Each start zips into its braking tip, and the stroke vanishes at the outer distance |
| 18–42 | The docs preview holds an empty frame before it loops |
A stroke lives 15 frames. Its start holds for about three frames, then
follows its own ease-in-out: it moves fastest around frame 9 and lands on the
end at frame 15, the frame the end arrives. At the defaults the stroke peaks
at about 77% of reach near frame 5 and travels near that length for a few
frames before it shrinks.
strokeLength is a soft cap on that length. On their own, the two curves open
a stroke to about 85% of reach, so the default of 56 only trims the peak. Set
strokeLength well below that, for short dashes on a long reach, and the
start is pulled along behind the end: each stroke travels as a dash of that
length, then its start zips it shut.
trimBurstLength is 18: the 15-frame stroke plus a launch spread of
6 × jitter frames. getTrimBurstTimeline({ jitter, count }) returns those
parts. getTrimBurstDuration({ speed, jitter, count, delay }) returns the
preview length, which adds a 24-frame empty hold. delay counts composition
frames before the burst fires. speed scales the motion, so at speed={2}
the burst lasts 9 frames. For another frame rate, multiply by fps / 30.
Variations
spread below 360 turns the ring into a fan centered on rotation, measured
clockwise from 12 o'clock. For example, spread={100} with count={5} fans
five strokes upward over a number. dots adds a small dot just past each tip.
It pops 4 frames after its stroke launches, overshoots by 7%, and shrinks to
nothing as the stroke closes. jitter={0} gives a rigid ring that launches all
at once, and jitter={1} spreads the launches over 6 frames. cap="butt"
gives the strokes flat ends.
| Prop | Type | Default | Description |
|---|---|---|---|
x | number | 0.5 | Burst center as a fraction of the composition width |
y | number | 0.5 | Burst center as a fraction of the composition height |
count | number | 8 | Number of strokes, rounded and clamped to 1–32 |
innerRadius | number | 16 | Distance from the center where strokes are born, in px at 720p; set it just outside the element |
reach | number | 64 | How far each end travels past the inner radius, in px at 720p |
strokeLength | number | 56 | Soft cap on the stroke length in px at 720p; the free chase peaks near 85% of reach, and lower values turn strokes into short dashes that travel before they zip shut |
weight | number | 2 | Stroke width in px at 720p, clamped to 0.25–24 |
rotation | number | 0 | Direction of the first stroke, or the center of a fan, in degrees clockwise from 12 o'clock |
spread | number | 360 | 360 for a full ring, or the angle of a fan centered on rotation |
jitter | number | 0.5 | Seeded variation from 0 to 1; offsets each angle by up to a quarter of the spacing, shortens strokes by up to 45% and spreads launches over up to 6 frames |
dots | boolean | false | Adds a small dot just past each tip that pops and shrinks with its stroke |
cap | "round" | "butt" | "round" | Stroke line cap |
color | string | "#ffffff" | Stroke and dot color |
seed | number | 1 | Seeds the per-stroke angles, lengths and launch order |
delay | number | 0 | Composition frames to wait before the burst fires |
speed | number | 1 | Playback multiplier; 0 never fires |
className | string | — | Optional class name on the full-frame root |
Truchet FlipNew
Quarter-circle tiles snap a quarter turn as a wave crosses the grid, reconnecting the lines until they settle into rings, a calm field, or a circle
Speed LinesNew
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