Framer Overrides
Auto-Scrubbing Video / Lottie Feature Walkthrough Tied to Scroll Progress
A pinned product walkthrough whose video (or Lottie) frame follows scroll position. The video is encoded with every frame as a keyframe so seeking stays smooth, and a poster image keeps LCP fast.
Works in Webflow Framer React Zero CLS
Customize before you copy
Showing the Webflow version
You are a senior creative developer. Build a product walkthrough where a video (or Lottie) plays forwards and backwards with scroll.
Behaviour:
- A tall section (about 300vh) with a sticky full-height stage. As the section scrolls, video.currentTime follows scroll progress from 0 to the end, with 0.8s of smoothing so it glides instead of stepping.
- Optional captions: 3–4 text steps that fade in at set progress points (25%, 50%, 75%).
- A Lottie variant: same progress drives goToAndStop(progress × totalFrames, true).
Video encoding (this is what makes or breaks smoothness):
- Give me the exact ffmpeg command: H.264, every frame a keyframe (-g 1), no audio, faststart, about 1280px wide, CRF around 23, plus a WebM/VP9 fallback.
- Keep the file under 4 MB; tell me how to check it.
Performance and accessibility:
- The poster image is the LCP candidate: same first frame, explicit width/height, loaded eagerly. The video preloads only metadata until the section is near.
- muted, playsinline, no autoplay, no controls while scrubbing; iOS Safari needs a play()/pause() prime on the first touch.
- Reduced motion: show the poster and a normal video with controls instead of scrubbing.
- The captions are real text, not baked into the video.
Deliver the structure, the script and the ffmpeg commands.
Target platform: Webflow.
- Build the structure as native Webflow Designer elements with Client-First class names (section_[name], [name]_component, [name]_item). No div soup from an HTML embed unless I ask for one.
- Hook every behaviour to custom attributes (data-gsap="…"), never to class names, so designers can restyle without breaking the script.
- Load GSAP through Webflow's built-in GSAP integration (Site settings) or the jsDelivr CDN tags, and put the init script in Page settings → Before </body> tag, wrapped in Webflow.push(() => { … }).
- Say which parts go in Designer styles, which in a page-head <style>, and which in the body script.
- Designer: section_walkthrough (data-gsap="scrub-section", height 300vh) > walkthrough_stage (sticky, top 0, height 100vh) > an Embed with the <video>. Host the MP4 on your CDN or as a Webflow asset. For Lottie, Webflow's native Lottie element with a "While page is scrolling" interaction is fine for simple files.
Motion timing: "Editorial". Use a base duration of 0.8s, ease "expo.out" (CSS: cubic-bezier(0.16, 1, 0.3, 1)), stagger 0.08s, scrub smoothing 0.8, and springs of stiffness 220 / damping 22, everywhere a duration, ease or spring appears.<!-- Designer: section_walkthrough [data-gsap="scrub-section"] (height 300vh)
> walkthrough_stage (position sticky, top 0, height 100vh) > Embed: -->
<video data-gsap="scrub-video" muted playsinline preload="metadata"
poster="https://cdn.example.com/walkthrough-poster.webp" width="1280" height="720">
<source src="https://cdn.example.com/walkthrough.webm" type="video/webm" />
<source src="https://cdn.example.com/walkthrough.mp4" type="video/mp4" />
</video>
<!-- Page settings > Before </body> tag -->
<script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3/dist/ScrollTrigger.min.js"></script>
<script>
window.Webflow ||= [];
window.Webflow.push(() => {
gsap.registerPlugin(ScrollTrigger);
const section = document.querySelector('[data-gsap="scrub-section"]');
const video = section?.querySelector('[data-gsap="scrub-video"]');
if (!video) return;
gsap.matchMedia().add({
motion: "(prefers-reduced-motion: no-preference)",
reduce: "(prefers-reduced-motion: reduce)",
}, (ctx) => {
if (ctx.conditions.reduce) { video.controls = true; return; }
// Start fetching the full file only when the section is near.
ScrollTrigger.create({ trigger: section, start: "top 200%", once: true, onEnter: () => { video.preload = "auto"; video.load(); } });
// iOS Safari won't seek a video that has never played.
addEventListener("touchstart", () => video.play().then(() => video.pause()).catch(() => {}), { once: true });
const init = () => {
video.pause();
gsap.fromTo(video, { currentTime: 0 }, {
currentTime: video.duration - 0.05,
ease: "none",
scrollTrigger: { trigger: section, start: "top top", end: "bottom bottom", scrub: 0.8 },
});
};
video.readyState >= 1 ? init() : video.addEventListener("loadedmetadata", init, { once: true });
});
});
</script>
<!-- Encode (every frame a keyframe = smooth seeking):
ffmpeg -i in.mov -an -vf "scale=1280:-2" -c:v libx264 -preset slow -crf 23 -g 1 -pix_fmt yuv420p -movflags +faststart walkthrough.mp4
ffmpeg -i in.mov -an -vf "scale=1280:-2" -c:v libvpx-vp9 -b:v 0 -crf 36 -g 1 walkthrough.webm -->3-step implementation guide
Step 1
DOM & attribute setup
Export the walkthrough without audio and re-encode it with every frame as a keyframe (the ffmpeg command is in the snippet). Without that, scrubbing backwards stutters on every browser. Export the first frame as the poster image.
Step 2
Prompt Claude or drop in the snippet
Build the tall section with a sticky full-height stage, then paste the prompt or drop in the snippet. Scroll progress drives currentTime with a little smoothing; captions are real text on the same timeline.
Step 3
Mobile & Core Web Vitals check
Test on an iPhone first (iOS needs the play/pause prime), check that the poster is the LCP element in PageSpeed Insights, and keep the video under about 4 MB so it never competes with the first paint.
Building a full site like this?
Skip the manual setup — I ship complete Figma-to-Webflow & Framer builds in 5–10 business days, or can wire this exact interaction into your live site in 48 hours ($250).