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 reanimateCanvas, WebGL and 3D
A canvas only shows pixels, but everything that made them went through a few hundred API calls: shader sources, uniform values, draw calls, textures, a scene graph. capture.ts --gl records those calls, and --clock virtual makes the recording repeatable frame for frame. Together they turn a WebGL scene into the same kind of evidence a CSS animation gives you: exact constants to copy, curves to fit, and a comparison that can fail.
Capture
Without --clock virtual, a heavy scene in software GL drops frames and its timing drifts. Random content then differs on every load, so you can only judge it against a second recording of the reference.
What you get
Reading the trace into the spec
Spec rows come from channels the same way DOM rows come from track.json:
- Fixed uniforms are constants to copy, not guess:
u_twistFrequencyX=0.059,u_glowAmount=3.86, andu_resolution(which tells you the drawing-buffer size the shader expects). - Find the code that drives a channel:
grep -l u_scatterPower assets/scripts/*and read around it. Bundled code keeps uniform names and constants even when everything else is minified. - Pointer-follow motion in WebGL is usually a lag,
x += (target − x) · kper frame or1 − exp(−rate·dt)per ms. It looks like an ease-out whose length depends on how far the pointer moved. Fit it witheasing.tsover a single pointer leg. Ifkis per frame, the motion depends on frame rate, which--raf-hzcontrols. - Render cadence: a program at 0.5 draws per frame renders every other frame. Copy it, because it changes how lagged and stepped motion looks.
- Passes:
framebuffer f4 → texture t5followed by a screen pass samplingt5is a post-processing chain. Rebuild it as render targets, or as anEffectComposer/ pass chain in the stack you use. - Glb / gltf scenes with an
AnimationMixer: the clip is the spec, as with Lottie. Load the same file fromassets/models/and play the clip names,timeScale, loop mode and weights shown in the summary. - Spline, Rive, Unicorn Studio and Lottie on canvas: the scene file is the spec. Embed the runtime when the licence allows. Otherwise rebuild from the file.
Building
- Same engine and version when the user's stack allows (
three.js r178in the summary). three.js changed colour management in r152 (outputColorSpace, sRGB textures) and lighting units in r155, so a different revision changes colours even with identical shaders. CopyoutputColorSpace,toneMapping,toneMappingExposure, clear colour and alpha, andautoClearfrom the summary. - Canvas setup: copy the context attributes (
antialias,alpha,premultipliedAlpha) and the pixel ratio. A ratio of 1 on a 2x screen means the site caps it (setPixelRatio(Math.min(devicePixelRatio, …))). - Shaders: port the user-authored part. For three.js that is
vertexShader/fragmentShaderinthree/scene-N.json. For raw WebGL, regl or OGL it is the source ingl/shaders/as it is. Keep uniform names unless the project has a convention, sincegl.ts comparematches channels by name (--mapotherwise). -
Blend state → three.js material:
- Geometry:
PlaneGeometry{…}andSphereGeometry{radius, widthSegments…}give constructor parameters. CustomBufferGeometrygives its attribute names and vertex count; dump it with--gl-bufferswhen it is data (point clouds) rather than code. - Randomness: port the generator with its calls in the same order and both pages see the same seeded
Math.random, so particles land in the same places. When that isn't possible, load the dumped buffer for verification only, or judge withcompare.ts --blur. - Frame-rate independence: when the reference lerps per frame, keep the per-frame constant at 60 Hz, or convert it to
1 − (1 − k)^(dt / 16.67)and say so in the report.
Verifying
Capture the clone with the reference command, changing only --url and --out, then:
- With
--clock virtualthe reference's noise floor is 0 %: two captures are identical, so every mismatch is the clone's. gl.ts comparefails a channel when its start or end is more than 80 ms off, its kind differs, or its mean error is over 5 % of the reference's range. It also lists canvas attribute, renderer setting and draw-structure differences: a missing pass, a different primitive count, a blend mode, a render cadence.- When the content is stochastic and you did not port the generator,
compare.ts --blur 4compares density, colour and motion, not dot positions. Report both numbers, sharp and blurred.
Limits that remain
- Workers are seeded and traced (OffscreenCanvas included), but their clocks run in real time and workers started by workers are not reached. Main-thread waits on worker replies are still made deterministic.
- Canvases inside iframes are recorded in their frame but not exported. Capture the iframe's URL directly.
- WebGPU uniforms are raw buffer floats (
gpu:gb3@0[4]) without names. Match them to WGSL struct fields by offset. - Compute passes and GPU-side simulation (particles advanced in a shader) show up as their inputs only (uniforms and buffers). The motion lives in the shader you copy.
- Spector.js (
captureCanvas()returns a JSON of one frame with full state per call) complements this for single-frame deep dives. The trace here is built for motion over time. - Shaders, models and textures belong to their authors, like fonts and images. Read them to understand the effect, and copy them only when the user may.