Capture a smooth clip of a CSS transition in a live web UI when the recorder cannot keep up: pause the Web Animations, step currentTime between agent-browser screenshots, block the pointer events that screenshots re-fire, assemble with ffmpeg, and composite onto a still as a high-quality GIF. Use for hover, slide and mask interactions in product graphics.
agent-browser record start recreates the browser context (the page reloads and client state is lost) and, at 1440 by 900 with a device pixel ratio of 4, keeps one or two frames per second. A 300ms thumb glide shows as two frames. Stop-motion gives every frame you want, at full density.
graphics/build/capture-sidebar.sh is the worked example (the docs sidebar's rail thumb sliding row by row, then the pill following the pointer back up). The shape:
pointerover and mouseover on whatever the last real click landed on, which moves hover states while you are stepping. A capture-phase listener that calls stopImmediatePropagation() on isTrusted pointer and mouse events fixes it; drive hovers with synthetic PointerEvent('pointerover') dispatches instead.eval (el.click() or a dispatched event), flush styles (void getComputedStyle(el).top), then document.getAnimations().forEach(a => a.pause()) and keep the list.a.currentTime = t on every paused animation, re-pause any that started, screenshot. Then a.finish() and a.play() and wait for the navigation to commit before the next step.ffmpeg -f concat -safe 0 -i frames.txt -vsync vfr -c:v libx264 -crf 12 -pix_fmt yuv420p clip.mp4.graphics/build/composite-videos.sh reads the crop's page rectangle from the rendered visual (data-rect on the .crop element) and its on-stage box, crops the recording to that rectangle at the recording's density, scales it to the box and overlays it on the still. Output the MP4 at 30fps and the GIF at 1600 wide, 24 to 30fps, palettegen=max_colors=256: stats_mode=diff and paletteuse=dither=sierra2_4a:diff_mode=rectangle. Nineteen seconds of sidebar came to under a megabyte because the background never changes.
ffmpeg ... select=... tile=8x1) and look at the last frame of every step: the state that should persist must persist.