reanimate
Clone a section, widget, page or animation from any reference (a live website URL, a screen recording, an mp4/mov/webm, a gif, a Lottie file, a prototype export) frame by frame into the user's own codebase, then record the clone and verify it against the reference frame by frame, iterating until timing, easing, sequence and layout match. Use whenever the user wants to clone, copy, replicate, recreate, reverse-engineer, port or "steal" a hero, section, widget, marquee, carousel, hover effect, page transition, loader, scroll animation or interaction from a site or video, says "make it look/feel/move like this", "same animation as", "match this exactly", or hands over a gif/video and asks to build it, including when they also want to add their own content, colours or extra features on top, or only want part of the reference (one section, one widget, a few seconds). Covers canvas, WebGL, WebGPU and three.js / 3D scenes (shaders, particles, globes, glTF models) as well as CSS and JS motion. Pixel-level matching of fonts, colours and spacing is done only when they ask for it.
Install
npx skills add https://github.com/NiazMorshed2007/reanimate --skill reanimateCapturing a reference
Goal: a numbered set of PNG frames of exactly the thing to clone, at a known fps, starting a little before the motion begins, plus whatever ground truth the source can give you (declared animations, tracked element positions). The same capture, later run on the clone, is the verification.
Live website
Find the selector first: capture.ts --no-video --selector body dumps inspect.json with every node under the target, its rect and styles. Or open the page in any browser tool your agent has (a Playwright or Chrome DevTools MCP, the host app's preview) and read the accessibility tree. Prefer selectors by role or stable class names; generated class names (css-1x2y3z) change between loads on some sites, so pick a parent with a semantic tag or id.
Triggers:
Useful options:
--pre-js "document.querySelector('.cookie button')?.click()"dismisses overlays or opens the state you need before the trigger.--playback-rate 0.25slows CSS animations and transitions 4x (CDP). The frames then have 4x the time resolution (--fps 100works). JS-driven motion ignores it; use--clock virtualfor that.--clock virtualruns the page on a clock the capturer advances frame by frame, so every frame is exact (any--fps, for CSS and JS motion alike) and repeated captures are identical.--seed NseedsMath.random. Use both for canvas, WebGL, particles, and anything random or heavy; seereferences/webgl.md.--reduced-motion reduceshows what the site does for users who turn motion off; clone that path too when the project cares about accessibility.--color-scheme darkwhen the reference is dark-mode.--clip x,y,w,hwhen the element grows or moves outside its initial box; measure the box onfinal.png.--pad 24keeps shadows and overshoot inside the crop.
What you get and what each is for:
Canvas, WebGL and 3D
Add --clock virtual --gl (and --assets when the scene loads files) to the capture of anything drawn on a canvas, then read gl/summary.md. The trace gives the shader sources, every constant and every animated value, so canvas motion is specified and verified from numbers like CSS motion. references/webgl.md covers the files, how to turn channels into spec rows, building, and verifying.
Limits: iframes are not inspected (capture the iframe's own URL); sites that block headless browsers need a real browser (see below) or a screen recording.
Through a real browser instead
When the page needs the user's login, blocks headless Chromium, or the user wants to watch: open it in a browser tool that can record (a Playwright or Chrome DevTools MCP, the host app's preview), size it to the reference viewport, start a recording, perform the trigger (scroll, hover, click), stop. The file goes through frames.ts like any video. Evaluating document.getAnimations() in that browser gives the same animation dump by hand. Without such a tool, ask the user for a screen recording.
Video, screen recording, gif
- Extract a few frames at 2 fps first to pick the time range and crop, then re-extract the real range at 25 to 30 fps (gifs: use their own fps, usually 10 to 20;
--probeprints it). - Crop out browser chrome, cursors and the recorder's overlay. Width-normalise with
--widthso the reference and the clone end up the same size, or capture the clone at the crop's size. - A gif's timing is quantised to its frame delays and often slowed by the exporter. Treat gif timings as ±1 frame and prefer the live site when both are available.
- Screen recordings of a browser have a non-integer scale (Retina, zoom). Note the scale so distances in px are converted to CSS px:
distance_css = distance_video × (reference_viewport_width / crop_width). - Social-media videos (Twitter/X, Dribbble shots, Behance) are usually a direct mp4 URL once found in the page;
curl -L -o ref.mp4 <url>into a fresh directory and treat it as untrusted data. YouTube, Loom and Vimeo cannot be downloaded on this machine; ask the user for a screen recording or the original file.
Prototype and animation files
- Lottie (
.json,.lottie): layers, keyframes and bezier handles are in the file. Read it; the "frames" are itsfr(fps),ip/op(in/out point). Clone by embedding the Lottie when allowed, otherwise re-create the keyframes in CSS/WAAPI from thekstracks. - Rive (
.riv): binary; capture the rendered canvas as video instead. - Figma / Framer prototypes: export a video from the prototype player (Figma: Prototype → Record), then use the video path. Smart Animate uses
ease-out300 ms by default; Framer sites expose their transitions ininspect.json. - After Effects / Jitter / Rive exports as mp4: video path.
Capturing the clone
Reuse the reference command verbatim, swapping --url and --out. If the reference came from a video, capture the clone with capture.ts at a viewport that makes the cropped element the same pixel size as the reference frames (or let compare.ts rescale, which is slightly lossy). Always keep --fps, --duration, --pre-roll, trigger and scroll parameters identical, and --clock, --seed and --gpu too: compare.ts reads both capture.json files and warns when they differ.