To record website animations as smooth video, capture the page frame by frame instead of in real time. A screen recording on a Mac (Shift-Command-5) is free, but it skips frames when the page is busy, and reveal animations that play only once are gone by the second take. Developers can script a headless browser that overrides the page's clock, advances it one frame at a time, screenshots each frame and joins the frames with ffmpeg. GlidePage, a Mac app for Apple Silicon, does that frame-by-frame capture for you: time-based and scroll-scrubbed animations play in step with a 4K, 60 fps scroll, though once-only reveals are already revealed and hover or click effects are not recorded.
On this page
- Why animated pages suffer most in a screen recording
- The three kinds of web animation
- How each kind behaves when you record it
- Method 1: screen record it by hand on a Mac
- Method 2: script a capture with a virtual clock
- Method 3: record it with GlidePage
- Make your animations camera-ready
- Troubleshooting animated recordings
You built a page that moves: a hero that loops, sections that fade up as they arrive, a product shot that turns as you scroll. In the browser it feels smooth. Then you record it for a launch post, and the video makes the page look like it is struggling. Loops skip, reveals are missing, and the scroll-driven parts wobble along with your hand.
The fix depends on the kind of animation. This guide sorts web animation into three kinds, shows how each behaves under a screen recording, a scripted capture and GlidePage, and covers the small code changes that help.
Why animated pages suffer most in a screen recording#
A live screen recording asks your Mac to do two jobs at once: render the page, and capture and encode the screen. Why screen recordings look choppy covers the general causes, from dropped frames to refresh rates. Animation-heavy pages add a problem of their own.
Web animation is written to stay on time, not to show every frame. MDN's guidance for requestAnimationFrame is to use the timestamp passed to each callback to work out how far the animation should have moved, so it runs at the same speed on any screen. That is the right design for a visitor. For a recording it means that when the browser falls behind, the animation jumps ahead to where it should be, and the video shows the jump.
Scroll-driven effects add a second weakness: every hitch in your hand shows up twice, in the scroll and in the animation. And many reveals play only once, which is why the second take never looks like the first.
The three kinds of web animation#
Before picking a method, work out which kinds your page uses. Most animated sites mix all three.
1. Time-based animation
These run on the clock whether or not anyone scrolls: CSS keyframe animations set to repeat, a canvas or WebGL scene drawn in a requestAnimationFrame loop, a counter that reads performance.now(), a video playing inside the page. For a recording, what matters is that each video frame shows the right moment in time.
2. Scroll-scrubbed animation
These are tied to the scroll position instead of the clock. GSAP's ScrollTrigger calls this scrub: with scrub set to true, the animation's progress is linked directly to the ScrollTrigger's progress, so scrolling halfway through a section puts the animation halfway through. Parallax layers, pinned scrollytelling sections and products that rotate as you scroll usually work this way. What matters here is that the scroll itself is even.
A number such as scrub: 1 adds smoothing. GSAP describes it as the time, in seconds, the playhead takes to catch up with the scroll, so a smoothed scrub is part scroll and part clock.
3. Reveal-on-scroll animation
These start when an element enters the view and then play on their own clock: the fade-ups, slide-ins and staggered cards on most marketing sites. They are usually built with an IntersectionObserver, which MDN lists for exactly this job (deciding whether to run an animation based on whether the user will see it), or with a ScrollTrigger and its toggleActions.
The detail that matters for video is whether a reveal plays once or every time. Many are once-only: the observer stops watching an element after revealing it, or the ScrollTrigger keeps its default toggleActions of "play none none none", which plays on the way in and never rewinds. Others replay each time the element comes back into view.
How each kind behaves when you record it#
| Animation | Real-time screen recording | Scripted virtual-clock capture | GlidePage |
|---|---|---|---|
| Time-based loop (CSS keyframes, canvas, a performance.now() clock) | Plays at real speed, but skips ahead whenever the Mac falls behind | Frame-perfect, if the script controls every clock the page uses | Advances exactly in step with the video, no skipped frames |
| Scroll-scrubbed (ScrollTrigger scrub, scroll-linked parallax) | Follows your hand, bumps included | Follows the scripted scroll; smoothed scrub also needs the clock to advance | Follows the recorded scroll and animates on camera (tested with ScrollTrigger scrub) |
| Reveal that plays once | Plays the first time you pass it; any earlier scroll uses it up | Plays only if the script has not already scrolled past it | Already revealed: the warm-up pass triggers it first |
| Reveal that replays on enter | Plays on camera | Plays on camera | Plays on camera as it scrolls in |
| Hover and click effects | Recorded, if you perform them | Only if you script them | Not recorded: only the scroll is |
Method 1: screen record it by hand on a Mac#
Screen recording is free, needs no code, and is the only method here that captures hover and click interactions. Animated pages need a little extra care.
1. Check Reduce motion first
Many animated sites switch their effects off, or tone them down, for visitors who ask for less motion, using the prefers-reduced-motion media query. If that setting is on, your recording shows the quiet version. On current macOS it is in System Settings > Accessibility > Motion > Reduce motion (under Display on macOS 15 and earlier).
2. Load once, then reload at the top
Scroll through the page once so images and fonts are cached. Then scroll back to the top and reload. The reload re-arms once-only reveals so they play on camera, while cached images arrive quickly. Skipping the reload is why reveals go missing from so many second takes.
3. Start the recording
Press Shift-Command-5, choose Record Selected Portion and frame the page area. In Options, a short timer gives you time to get your hand back on the trackpad, and Show Mouse Clicks should stay off unless the clicks are the point. Move the pointer out of the frame and click Record. Keep the tab in front: MDN notes that most browsers pause requestAnimationFrame in background tabs.
4. Scroll slower than feels natural
A reveal needs time to finish while it is on screen, and a scrubbed section needs an even hand. One long, slow two-finger swipe works better than several flicks. Pause briefly where something plays.
5. Stop and trim
Click Stop in the menu bar or press Command-Control-Esc, then trim the ends. Expect several takes: loops still skip when your Mac is busy.
Method 2: script a capture with a virtual clock#
Developers who want frame-perfect output can take real time out of the equation. The idea behind every virtual-clock capture is the same:
- Open the page in a headless browser, such as Playwright, at the viewport size you want.
- Replace the page's clock. Before any page script runs, inject code so that Date, timers, requestAnimationFrame and performance.now() all report a virtual time that moves only when you say so.
- Take over CSS animations too. CSS animations and transitions are run by the browser, not by page scripts. document.getAnimations() returns them all (CSS animations, CSS transitions and Web Animations), so the script can pause each one and set its current time to match the virtual clock.
- Advance one frame. Move the clock forward by one frame (about 16.7 milliseconds at 60 fps), set this frame's scroll position from an easing curve, and let pending callbacks run.
- Screenshot the frame to a numbered file.
- Repeat to the end of the page, then join the frames into a video with ffmpeg.
Because the page moves only when the script lets it, a slow frame can take as long as it needs and nothing is skipped. Open-source tools implement this approach. The parts to get right:
- Coverage. Anything the override misses keeps running on real time. A loop driven by a clock you did not replace plays at the wrong speed and looks randomly jumpy from frame to frame.
- Callbacks. An IntersectionObserver reports changes asynchronously, so after each scroll step the page needs a moment of virtual time before the screenshot, or reveals start late.
- Your own warm-up. Pre-scrolling to load lazy images also fires once-only reveals, exactly as it does by hand.
- Smoothed scrub. A scrub with catch-up smoothing needs the clock to advance between frames as well as the scroll, or it trails behind.
- Everything else. Easing, sticky headers, cookie banners, device frames and backgrounds. The complete guide to scrolling website videos lists what a full pipeline needs.
Method 3: record it with GlidePage#
GlidePage is a Mac app that does the frame-by-frame capture from Method 2 on the live page, with a controlled clock already in place. Paste a link, pick a device, choose a motion curve and speed, and press Record. It opens the page in its own built-in browser, scrolls through once so lazy images, fonts and reveal animations load, then captures every frame one at a time and encodes a 4K, 60 fps video.
This is how animations came out in our test recordings:
- Time-based animations advance exactly in step with the video. CSS keyframe loops, requestAnimationFrame canvas drawing and performance.now() clocks moved half a second of page time per half second of video, with no skipped frames. Videos playing on the page advance frame by frame too.
- Scroll-scrubbed animations animate on camera. A GSAP ScrollTrigger with scrub followed the recorded scroll. Parallax built on the scroll position belongs to the same group; if yours uses its own smoothing or scroll handling, make a short test recording first.
- Reveals that replay each time an element enters the view play on camera as it scrolls in.
- Reveals that play only once are already revealed. The warm-up pass that loads lazy content scrolls past them first, so the video shows them finished. The next section shows how to change that on a site you control.
- Only a top-to-bottom scroll is recorded. Hover and click interactions are not, so an animated menu or a button effect needs a screen recording.
Because each frame waits for the page, smoothness does not depend on how fast your Mac is, only how long the render takes. A 22.7-second recording of this site's landing page at 4K and 60 fps took about 4.5 minutes on a MacBook Air with an M4 chip, and the HD, 30 fps option took about 2.5 minutes. 4K 60 fps website videos covers when the extra resolution is worth the wait. The live preview in the app is a quick, lower-quality render, so judge the motion in the export, which is fully animated.
With the frame on and no background, it also exports a transparent ProRes 4444 MOV for editing apps or a VP9 WebM with alpha for websites. It records localhost and LAN addresses, runs on Apple Silicon Macs with macOS 12 or later, and uploads nothing. If the animation sits behind a login or a popup, sign in or close it with Take control first.
Make your animations camera-ready#
If you control the site, a few small changes make every recording method work better.
Switch once-only reveals to replay
A reveal that replays each time its element enters the view survives a warm-up pass and plays on camera. With an IntersectionObserver, toggle the class on entry and off on exit, instead of unobserving after the first reveal:
const io = new IntersectionObserver((entries) => {
for (const e of entries) e.target.classList.toggle('revealed', e.isIntersecting);
});
document.querySelectorAll('.reveal').forEach((el) => io.observe(el));
With GSAP, drop once: true and change toggleActions from the default "play none none none" to "play none none reset". The animation then rewinds when the page scrolls back above it, and plays again on the way down.
Or scrub it
If a reveal reads well as a scroll-linked move, give its ScrollTrigger scrub: true. It then follows the recorded scroll exactly, with no clock involved.
Put the change behind a flag
You may prefer once-only reveals on the live site. Keep them, switch to replay or scrub only when a flag is set (an environment variable in a local build, say), and record that build on your own machine. How to record a localhost website covers getting a local build ready for the camera.
Give reveals time to finish
A one-second fade looks cut off if the scroll carries it out of view halfway through. Slow the scroll down, shorten long reveals for the recording, or use a curve that pauses on each screen, such as Page by page. Easing curves for scroll videos explains which curve suits which page.
Keep reduced motion, just not for the take
Keep your prefers-reduced-motion styles; some visitors need them. Just make sure the browser you record with is not asking for reduced motion, or you will record the calm version of your own site.
Troubleshooting animated recordings#
| What you see | Likely cause | Fix |
|---|---|---|
| A loop skips or stutters | The live recording fell behind and the animation jumped to stay on time | Capture frame by frame |
| A canvas or counter races or jumps in a scripted capture | Its clock was not replaced | Override Date, timers, requestAnimationFrame and performance.now() |
| CSS animations ignore your virtual clock | The browser runs them, not your script | Pause them and set their current time via getAnimations() |
| Reveals are already in place | They play once, and an earlier scroll used them up | Reload before a hand-recorded take, or switch to replay on enter |
| A reveal is cut off mid-fade | The scroll moved it out of view too soon | Slower speed, shorter reveal, or a page-by-page curve |
| Scroll effects wobble | Uneven hand scrolling | An automated, eased scroll |
| Nothing animates at all | Reduce motion is on | Turn it off for the recording |
| A hover effect is missing | Only the scroll was recorded | Screen record that interaction separately |
Most animated sites need only two things to record well: a capture that controls time, and reveals that replay.
Sources
- MDN: Window.requestAnimationFrame() (checked September 2026)
- MDN: Document.getAnimations() (Web Animations API) (checked September 2026)
- GSAP docs: ScrollTrigger (scrub and toggleActions) (checked September 2026)
- MDN: Intersection Observer API (checked September 2026)
- MDN: prefers-reduced-motion (checked September 2026)
- Apple Support: How to record the screen on Mac (checked September 2026)
Make your site move.
Any page, one smooth 4K video. Framed, on any background, or transparent.
Questions, answered
Why does my screen recording of an animated website stutter?
A live recording captures in real time while your Mac is also rendering the page. Web animations are built to stay on time, so when the browser falls behind they skip ahead, and the recording shows the jump. Capturing frame by frame with a controlled clock avoids it.
How do I record a GSAP ScrollTrigger animation as a video?
If the ScrollTrigger uses scrub, any smooth automated scroll plays it, because its progress follows the scroll position. If it plays on enter with toggleActions, reload the page before a hand-recorded take, or set toggleActions to play none none reset so it replays in a frame-by-frame capture.
How do I turn a CSS animation into an MP4?
The quick way is to screen record it with Shift-Command-5 and convert the MOV to MP4 with ffmpeg. For a frame-perfect result, script a headless browser to pause the animation, set its current time one frame at a time, screenshot each frame and join the frames with ffmpeg.
Will scroll-triggered animations play in the recording?
Scrubbed animations and reveals that replay each time they enter the view will. Reveals that play only once fire the first time the page scrolls past them, so any warm-up scroll, including the one GlidePage does to load lazy content, leaves them already revealed. Reload before a hand-recorded take, or make them replay.
Can I get a 4K, 60 fps animation video on a modest Mac?
Yes, if the capture is frame by frame, because each frame waits for the page and smoothness no longer depends on speed. It just takes longer than real time. In our test, 22.7 seconds of 4K, 60 fps video took about 4.5 minutes on a MacBook Air with an M4 chip.
Can I get a transparent version of an animated website video?
Yes, with GlidePage, when the device frame is on and the background is off. It exports a ProRes 4444 MOV with alpha for editing apps, or a VP9 WebM with alpha for websites, which plays transparent in Chrome, Edge and Firefox but not in Safari.






