Communitygithub.com

starrandy123/reanimate-video

Copy an animation from a VIDEO into code, frame by frame, with reanimate (github.com/NiazMorshed2007/reanimate), out of the box - game footage, a stock motion graphic, a screen recording, a gif - and show the process as clips.

What is reanimate-video?

reanimate-video is a Claude Code agent skill that copy an animation from a VIDEO into code, frame by frame, with reanimate (github.com/NiazMorshed2007/reanimate), out of the box - game footage, a stock motion graphic, a screen recording, a gif - and show the process as clips.

Works with~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/starrandy123/reanimate-video/tree/HEAD

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

What does reanimate-video do?

reanimate films a live page, rebuilds it in your code, films the copy and compares, round after round, until the numbers match. Its own path for a VIDEO reference works, but a first real run (6 Nov 2026, a free stock countdown) hit seven traps, each silent or slow. This kit removes them and adds the measuring a video needs, because a video has no page to inspect.

Requires reanimate: npx skills add NiazMorshed2007/reanimate -g, then once bash <reanimate>/scripts/setup.sh. Then rv setup once (the measuring tools' numpy and opencv, in a Python of the kit's own: the python3 most Macs have refuses a global pip install). Check with rv doctor. (rv is this skill's rv file.)

The traps, and what the kit does

Trap (reanimate as it ships, from a video)The kit
1a 10-bit video (ProRes, most stock footage) becomes 16-bit PNGs, 10 MB a framerv ref writes 8-bit frames
2sheets inline every frame in one page and time out on 1080p framessheets come from 480-px copies
3a copy opened as file:// cannot load its ES modules: reanimate records a blank page without a wordrv serve + rv round refuses file://, and every recording is checked: BLANK or FROZEN stops the round
4the copy's first frame is recorded before its first animation framethe template draws at load
5comparing 300 frames at 1080p takes minutes a roundrounds compare at half size, the same way every round, sharp and blurred 4 px
6a video has no element tracks or keyframes to readmeasure/: centre, float, spin, arcs, fade, size, segments, typeface, from the pixels
7the virtual clock sums its ticks in floating point and can skip a frame's last tick: that frame shows the page one tick early (17 ms at its default 60 Hz)a copy on the template is recorded by calling its draw at exactly t = k / fps; other pages at a 240 Hz tick
8(the kit's own tools) numpy and opencv cannot be pip-installed into the python3 most Macs have (Homebrew's, PEP 668)rv setup makes the kit's own Python (.venv); rv measure <tool> runs each tool with it

tests/run.sh reproduces every trap on reanimate as it ships (RED), then runs the kit's way (GREEN); tests/e2e.sh runs the whole path on a small case (a copy that IS the reference reads 0 %; a planted wrong colour reads worse). Run both after any change to the kit.

Before anything: may this video be copied?

Copy the LOOK only of a video the user may copy: their own, or a free licence that allows changes (Videezy's Standard License: free for personal and commercial use with the credit "Videezy.com"; Mixkit's free licence; Pexels and Pixabay's licences). Write the licence, its source link and the date into <work>/LICENSE.md, and put the credit on screen wherever the video is shown (<work>/film.json "credit"). A game's footage is copied for its MOTION only (reanimate's default): the look - the words, the font, the colours, the art - stays the user's own.

The steps

  1. rv doctor
  2. rv ref <video> <work> (add --start/--end for a part, --crop x,y,w,h for one element). READ EVERY SHEET in <work>/ref/sheets and the full-rate ones in <work>/ref/sheets_full_rate. Write what moves, and when, before measuring.
  3. Measure each moving element with the tool for its kind of motion (measure/README.md; run each as rv measure <tool> ...), on <work>/ref/frames. Prove each tool on the element before trusting it (its --sheet, or a second frame that must agree).
  4. <work>/spec.md: one row per element per property: start, duration, from -> to, easing or the measured table, and the file the number came from. A row with a "?" is not finished.
  5. Build: copy template/ into the project. animation.js exports draw(ctx, t) (a pure function of time) and DURATION; put every measured number in a module with the file it came from. The canvas is the reference's size (<work>/run.json).
  6. rv serve <copy dir> then rv round 1 <work> --url http://127.0.0.1:8765/index.html. Read <work>/compare-1/report.md and its sheets (compare-1/sheets): the numbers say how far off, the sheets say what. Fix the BIGGEST difference only, then rv round 2 .... Stop at 5 rounds; what still fails is reported with its number and its reason.
  7. rv rounds <work> (the table) and rv film <work> (the process clips, 1080x1920: the video, its frames, every round with what differs in red and its number, the numbers falling, the final in sync).
  8. Report: what was copied and at which fidelity, the files, the rounds table, what stayed off and why.

What copies from a video, and what does not

Copies well, by measurement: shapes, text, rings and arcs that turn, ticks and segments that light, fades and flashes, growth and slides, the order and the timing of all of it. Measure them with measure/ and rebuild them as shapes.

Does NOT copy by measuring: a 3D-rendered scene, footage, particles placed at random, a background whose pieces flash and change. measure/spin.py says so with its AGREEMENT (under about 0.3 the band does more than turn). On 6 Nov 2026 the countdown's tunnel read 0.16-0.6: three rounds of the copy left it a smear (76 % -> 48 % of pixels off) while its HUD matched. Then: copy the element in front, give the background the user's own scene (a game's, a page's), and compare only where the element is, or pick a reference whose background is plain. Never call such a copy pixel-perfect.

Pass bars (reanimate's own, read for a video)

  • Timing: every event (an element's 10/50/90 % rise and fall, measure/curve.py) within 0.08 s of the reference's.
  • Easing: the copy's measured curve within 5 % of the travel (mean absolute difference) of the reference's.
  • Pixels: mean mismatch at most 2 % for a pixel-faithful copy (only on a reference the user may copy the look of, and only where the background is part of what was copied); report the sharp number beside the blurred one.
  • Every round's recording passed the guard (no blank or frozen frames).

Pitfalls learned

  • One float for a whole design is a hypothesis: fit EACH ring's centre on the original frames (polar.py fit) before steadying anything (stabilize.py). On 6 Nov 2026 (Videezy 40314) the first fit locked onto the brightest ring, the white one, which drifts 8.5 px; the ring at 319 moves 0.1 px. Three rounds floated the whole HUD and measured it on frames steadied by the wrong layer; round 4, with only the white ring floating and everything measured again around the still centre, went 12.6 % -> 9.3 %.

  • Try a change on a few frames before spending a round: rv still <work> --url ... --frames 0,60,150 and compare those frames with compare.ts against the last round's code. On 6 Nov two of three candidate changes helped (10.0 -> 9.5 -> 8.9 %) and one made it worse (13.3 %): it never became a round.

  • measure/radial_diff.py shows WHERE a round design is off, radius by radius; it reads still parts only - a floating ring smears in its medians, so judge by compare.ts when they disagree.

  • A still ring is bright at EVERY angle: the 30th percentile over angle keeps it and drops what moves - the way to read the rings' true radii and widths.

  • rv film crops to the subject (film.json "crop"): a showcase shows its subject big.

  • A centre found from "the sharpest rings" wanders when a big white shape is in the middle: use a small bright landmark on several frames, and trust it only when the frames agree (measure/polar.py landmark).

  • A glow thickens every edge: choose the typeface by measuring (measure/fontmatch.py), not by eye.

  • A measured zoom or turn is a hypothesis until a second view agrees: on 6 Nov a log-polar correlation reported a 3 %/s zoom that a radius-by-time strip showed did not exist, and the copy shrank its tunnel by it for a round.

  • An area grows as size squared: a number's size is sqrt(white area above the frames before it), or its box.

  • Free stock footage is often 4K ProRes, 1-2 GB a clip: read it at 1920 wide (rv ref does), keep the original for the record, and mind the disk.

Related Skills