Skip to main content

Video scrub

Video scrub maps scroll progress directly to a video's playback position — scroll down and the video plays forwards, scroll back up and it rewinds, frame by frame. It's normally used full-bleed inside a pinned scene, but works on any <video>.

See it live: Video scrub on fancyanimations.com.

How it works​

  • Add the Video scrub preset on a <video> element itself, or on a wrapper that contains one — the plugin finds its first <video> descendant automatically.
  • Each video is driven by exactly one element — the first one in the page that finds it. It's fine for both a wrapper and its inner <video> to carry the setting.
  • The plugin automatically pauses the video and sets preload="auto" if you haven't set a preload value yourself. You don't need autoplay or loop turned on.
  • Like any row, it has a Start/End/From/To/Invert window — here, From/To describe what part of the video plays, as a percentage of its total duration.
SettingMeaning
Start / EndThe scroll window (same as any row)
From / ToThe part of the video played, as % of its duration
InvertPlays backwards

There is no --fa-scroll--video-scrub variable for custom CSS — if you need something else (a crop, a caption) tied to the same scroll range, add a separate Custom row to the same element. Video scrub also has its own easing, tuned to how long the browser's decoder takes to seek — "Scroll smoothing" doesn't apply to it. "Sequence children" doesn't count Video scrub rows either.

Typical structure​

A pinned scene: the video sits absolute and full-bleed inside the stage, with "Get parent" so its playback position follows the whole section rather than itself, and its window set to the scene's pinned window (e.g. 25–75% in a 300vh scene). Captions usually get their own rows on the same timeline — see Advanced recipes.

The video file decides whether it's smooth​

Scrubbing seeks through the file frame by frame, and two encoding choices make that visibly jump:

ProblemFix
Too few keyframes — every seek takes tens to hundreds of millisecondsRe-encode with every single frame as a keyframe (in HandBrake: Extra Options keyint=1)
Too few frames for the scroll distance (4s at 30fps = 120 frames covering ~2700px of scroll)About 300 frames or more — e.g. 6 seconds at 60fps
New in 3.2.0

A Check video button appears next to every scrubbed video in the Bricks, Etch and block editor canvases. It measures the real file: the average time a seek actually takes (over 50ms means it needs more keyframes) and estimates the total frame count (under 300 looks jumpy). It never appears on the published, visitor-facing site.

Use the native Video element with source "Media", and add a Video scrub row in the Style tab → ScrollTrigger Animations panel. A few things specific to Bricks' video element:

  • Leave Controls off and set the element's pointer events to "none" — without controls, Bricks still adds its own click-to-play/pause behaviour, and a click would interrupt the scrub.
  • Leave Preload empty so the plugin can set preload="auto" for you.
  • The native "Object fit" control targets the inner <video> correctly; for object-position, write it in custom CSS targeting the tag directly (%root% video { object-position: 50% 30%; }) — Bricks renders a wrapper div around the video and the Fancy classes land on both.

Common problems​

  • The video advances in small, visible jumps. Almost always too few frames for the scroll distance, not a plugin issue — aim for 300+ frames, and confirm with Check video (3.2.0+) or by testing in real Chrome (some automated test browsers don't decode H.264 video at all).
  • Every seek lags behind the scroll. The keyframe interval is too long — re-encode with every frame as a keyframe.
  • A click on the video pauses or restarts it. That's Bricks' own click handler on a controls-less video — set pointer events to "none" on the element.
  • object-fit has no visible effect, or the video shows letterboxing. It was applied to the wrapper div rather than the <video> tag itself — use the native Object fit control, or target .your-class video { } directly in custom CSS.