Pendulum Wave
A row of pendulums with tuned periods drifts from a straight line into travelling waves and braids, then realigns, as a seamless loop
$ pnpm dlx shadcn@latest add @remocn/pendulum-waveUsage
PendulumWave is the classic pendulum wave demo seen from the side. A row of
bobs swings up and down along vertical tracks. Pendulum i makes
cycles + i full swings per loop, so each one is slightly faster than its
left neighbor. At frame 0 every bob sits at the top of its track. The row
then slides into a travelling wave, breaks into braids and two- and
three-row patterns, and lines up again at the top on the last frame of the
loop.
A red line threads through the bobs as a smooth curve, so the wave reads as one shape. Each bob leaves a faint smear of its last four frames, and bobs low on the track are drawn up to 22% larger than bobs at the top, a small depth cue. The faint guides are a center line, a dotted track per pendulum and a tick at each end of every track.
import { PendulumWave } from "@/components/remocn/pendulum-wave";
export const RhythmBed = () => <PendulumWave />;The component fills its parent and paints its own background. The row
spans the middle 80% of the width and the bobs travel 30% of the height above
and below the center line. Strokes and bob sizes scale with the composition,
so 16:9, 1:1 and 9:16 look alike. Every frame is computed directly from the
Remotion frame, so seeking and rendering are deterministic.
Timing
At 30 fps, speed={1} and the defaults (cycles={10}, loopFrames={450}):
| Frames | Motion |
|---|---|
| 0 | All 15 bobs line up at the top of their tracks |
| 0–90 | The line peels into a travelling wave, fastest bobs on the right |
| 90–220 | The wave tightens into braids and short-lived row patterns |
| 112 | Bobs repeat in groups of four: top, center, bottom, center |
| 225 | Bobs alternate top and bottom in a two-row zigzag |
| 225–450 | The sequence plays back in mirror order |
| 450 | All bobs line up at the top again, identical to frame 0 |
The loop period is loopFrames / speed frames: 450 frames, 15 seconds, at
the defaults. At loopFrames / k the bobs repeat in groups of k along the
row, so the patterns land on predictable frames. The loop closes exactly only when cycles is a
whole number. For a seamless background, make the Sequence a multiple of
the period, or wrap the component in a Remotion <Loop> of that length.
import { Loop } from "remotion";
import { PendulumWave } from "@/components/remocn/pendulum-wave";
export const Background = () => (
<Loop durationInFrames={300}>
<PendulumWave count={21} cycles={8} loopFrames={300} showGuides={false} />
</Loop>
);Variations
Raise count for a denser, faster-braiding row. Lower cycles for slower
swings; the difference between neighbors stays one swing per loop, so the
patterns keep the same timing. showGuides={false} leaves only the bobs and
the wave line. Set accent to background to hide the line.
Props8
| Prop | Type | Default |
|---|---|---|
| count | number15 Number of pendulums, rounded, minimum 2 | |
| cycles | number10 Full swings of the slowest pendulum per loop; pendulum i makes cycles + i. Use a whole number for a seamless loop | |
| loopFrames | number450 Frames until every pendulum realigns at the top | |
| showGuides | booleantrue Draws the center line, dotted tracks and end ticks | |
| background | string"#111111" Background fill | |
| color | string"#FFFFFF" Bobs, smears and guides | |
| accent | string"#E5322D" The wave line threaded through the bobs | |
| speed | number1 Playback multiplier; the loop period is loopFrames / speed | |
- countnumber
Number of pendulums, rounded, minimum 2
Default
15 - cyclesnumber
Full swings of the slowest pendulum per loop; pendulum i makes cycles + i. Use a whole number for a seamless loop
Default
10 - loopFramesnumber
Frames until every pendulum realigns at the top
Default
450 - showGuidesboolean
Draws the center line, dotted tracks and end ticks
Default
true - backgroundstring
Background fill
Default
"#111111" - colorstring
Bobs, smears and guides
Default
"#FFFFFF" - accentstring
The wave line threaded through the bobs
Default
"#E5322D" - speednumber
Playback multiplier; the loop period is loopFrames / speed
Default
1