Clone a live web page into a verified, self-contained React app — capture it with a headless browser, distill a Design Genome (tokens + motion + interaction state machines), reconstruct it deterministically, and prove the result per node and per interaction state by visual diff. Built entirely on technology that exists today (Playwright, WAAPI, MutationObserver, esbuild, pixelmatch). No LLM in the loop: every output is inspectable and reproducible.
This project is also packaged as a Claude Skill — an AI agent should start from SKILL.md and the deeper docs in
. This README is the human-facing overview.
references/
Why 0.x:the CLI flags, genome schema, and report format may still change. 0.x is a statement of API instability, not of dishonest numbers — every number below is reproducible from a clean clone.
Requires Node.js ≥ 18. No other machine state is assumed.
git clone <this-repo> && cd mirrorframe
npm install # postinstall downloads the Playwright Chromium binary
node scripts/mirrorframe.js run --url https://your-site.example --out ./mf-out/yours
node scripts/mirrorframe.js run --dir ./fixtures/tabs --out ./mf-out/tabs
npm run verify # regression gate: full pipeline on all 12 bundled fixtures
Open the reconstruction at <out>/recon-app/index.html
— a self-contained React app
(app.jsx
, styles.css
, bundle.js
) with all assets bundled locally in <out>/assets/
.
Capture→capture.json
: headless Chromium walks the DOM recording computed styles per node, plus every motion/interaction signal listed below, and downloads all referenced assets.Genome→genome.json
: clusters design tokens, compiles motion DNA and interaction DNA (recovered state machines), annotates the structure.Reconstruct→recon-app/
: deterministic React + CSS codegen with the recovered state machines re-wired as real listeners/observers/animations.Converge→convergence.json
+summary.json
: re-renders the reconstruction at the identical viewport, verifies per node and per state, auto-corrects residual drift, and replays every recovered interaction and scroll state.
- Full computed-style capture per node (layout, color, type, borders, shadows, radii, filters, transforms) at a configurable viewport
Images(
<img>
) andCSS background-images downloaded and rewritten to local bundled copies (8MB/asset, 192MB/capture budgets; misses reported, never hidden)Web fonts: every@font-face
is downloaded, bundled, and re-declared locallySVG passthrough: inline SVG markup is preserved verbatim(new in 0.2): source + poster bundled locally (64MB/asset budget), playback attributes (autoplay/loop/muted/playsinline/controls) preserved, real<video>
<video>
re-emitted; degrades to poster/first-frame still if the source is unfetchable — never an empty box(new in 0.2): the currently painted frame is snapshotted and re-emitted as a pixel-true still<canvas>
-
Same-origin iframes captured; cross-origin content masked with a fixed reason
-
CSS
@keyframes
animations and transitions (recovered via WAAPIgetAnimations()
) :hover
rules recovered verbatim from same-origin stylesheets;JS-driven hover effects recovered by a synthetic hover probePointer choreography(new in 0.3): mouse-movement effects are recovered by sampling the page over a 3×3 pointer grid and fitting every transform-matrix component as a plane over the pointer (least squares, R² ≥ 0.85) — parallax layers, 3D tilt cards (matrix3d
), magnetic offsets, pointer-linked scale.Smoothing/lag is recovered too: the exponential time constant of the "ultra-smooth" trailing chase is measured from a pointer-jump step response and replayed with the same lerp in the reconstruction. Non-planar pointer physics is reported as unclassified, never guessedScroll choreography: scroll-position–linked style tracks are sampled at multiple scroll depths and re-emitted; verified by scroll-state replay at 25/50/75%Sequential scroll-reveal stagger(new in 0.3): reveal mutations are timestamped, so elements that fade/slide in one by one are replayed with their observed per-element offsets; each participant's own computedtransition
(duration, easing curve,transition-delay
) is re-emitted verbatim — CSSnth-child
delay staggers and JS-timer staggers both survive the cloneChronograph frame sampler(new in 0.2): time-driven motion withno declared animation object(rAF loops, JS tickers) is detected and recorded atevery animation frame for a bounded window (2400ms, ≤120 tracks) — transform, opacity, filter, clip-path per frame — then replayed verbatim as looping WAAPI animations built from the sampled offsets
Recovered from the page by synthetic probes — never assumed or hardcoded:
reveal
— IntersectionObserver scroll-reveal (hidden → visible class machines)toggle
— second click reverses the first (accordions, expand/collapse)exclusive
— one class moves between siblings (tabs, segmented controls)pair
— one trigger adds the class, another removes it (modal open/close, including triggers only reachableinsidethe open state)- Ambient (timer-driven) mutations such as autoplay carousels are detected during an input-free watch and excluded from learning rather than mislearned
- A probe that crashes on a page-script conflict records
probe-error
for that candidate and the sweep continues (new in 0.2)
Per-node: every tracked node gets its own similarity score and one status —pass
/style-verified
/corrected
/failed
/animated-unstable
/hidden-at-capture
/time-varying-replicated
(new in 0.2) /skipped(reason)
Residual auto-correction: failing nodes get their reconstructed computed styles diffed against capture, patched with a per-node override, and re-verified onceLayout convergence: rendered rects measured against captured rects; drifting nodes get authored sizes baked in bounded passes** Behavioral replay**: every recovered interaction state is replayed with real clicks and pixel-diffed against a ground-truth screenshot of the originalScroll replay: the page is verified at multiple scroll depths, not just the fold** Pointer replay**(new in 0.3): ground-truth screenshots at fixed pointer checkpoints are re-shot on the reconstruction with the same real mouse moves (settle time scaled to the largest recovered smoothing tau) and pixel-diffed; replay shots are settle-aware, waiting until consecutive frames match so staggered reveals and lag-smoothed effects finish before the diffTime-varying honesty(new in 0.2): bundled video/canvas and frame-sampled nodes have theirmechanismreplicated, but pixels depend on the playback instant — they are masked from diffs with the fixed reasontime-varying-media
, itemized, never averaged in or hiddenFixed skip-reason taxonomy(scripts/lib/reasons.js
, 10 reasons): every skipped node/probe/page carries exactly one reason; skipped elements keep their layout footprint via placeholders and are masked from the diff- Bounded everything: navigation fallback, probe budgets, node/candidate caps, 300s pipeline watchdog with distinct exit codes (0 ok / 3 page skipped / 4 crash)
All 12 bundled fixtures pass with 0 failed nodes, 0 failed states, 0 failed
scroll states, and 0 failed pointer states — including stress-stagger
(CSS-delay + JS-timer sequential reveals, new in 0.3), stress-pointer
(parallax/tilt/smooth-follower pointer choreography, new in 0.3), stress-frames
(rAF inline-style motion + animated canvas), stress-scale
(230 nodes / 120
interactive), stress-edgecss
(container queries, :has()
), stress-iframe
,
stress-flaky
(404/hung assets), and stress-carousel
(autoplay → excluded, not mislearned).
The pipeline has also been spot-validated against complex real-world production pages (hundreds of nodes; heavy video, scroll choreography, and rAF motion) with aggregate similarities ≥ 99.9%, all scroll states passing, media and web fonts fully bundled, and zero crashes — any sub-threshold node is itemized in the per-node report rather than averaged away. Nothing from third-party pages is bundled in this repo.
Success = zero failed
nodes and zero failed states. style-verified
,
animated-unstable
, hidden-at-capture
, time-varying-replicated
, and
skipped(reason)
are acceptable statuses when explained — see SKILL.md
for the
full interpretation guide. The one-screen summary is summary.json
; the full
per-node table is convergence.json
; residual maps are diff-*.png
.
| File | Meaning |
|---|---|
capture.json |
|
| evidence bundle: node tree, computed styles, animations, scroll/frame tracks, probes | |
genome.json |
|
| Design Genome: tokens, motion DNA, interaction DNA, annotated structure | |
assets/ |
|
| locally bundled images, fonts, videos, posters, media stills | |
recon-app/ |
|
| generated React app (jsx + css + bundle + html) | |
original-*.png / state-*.png / scroll-*.png / pointer-*.png |
|
| ground-truth screenshots | |
recon-*.png / diff-*.png |
|
| reconstruction screenshots + pixelmatch residual maps | |
convergence.json |
|
| per-node table, correction log, state/scroll replay results, aggregates | |
summary.json |
|
| one-screen quantitative summary |
SKILL.md skill entrypoint: trigger description + agent workflow
scripts/mirrorframe.js CLI: capture → genome → reconstruct → converge
scripts/verify-all.js release verification: all fixtures + results table
scripts/batch-verify.js batch validation against a list of live URLs
scripts/lib/props.js shared tracked-property list + color normalization
scripts/lib/reasons.js fixed skip-reason taxonomy (10 reasons)
scripts/lib/assets.js asset down/bundler (images, fonts, video)
scripts/lib/capture.js capture substrate: DOM/style walk, motion + interaction probes
scripts/lib/genome.js genome compiler: tokens, tracks, behavior classification
scripts/lib/reconstruct.js deterministic React + CSS codegen (state machines re-wired)
scripts/lib/converge.js per-node + aggregate diff, auto-correction, state replay
references/behavior-patterns.md recovered-pattern catalog (reveal/toggle/exclusive/pair)
references/limitations.md hard scope limits + expected non-pass statuses
references/roadmap.md forward planning, feasible-now vs research-level
fixtures/ 12 self-authored test pages (4 core + 8 stress)
- Canvas/WebGL draw streamsare not reconstructed — a canvas becomes a pixel-true still of its captured frame, not a live re-render - Pointer physics beyond a planar matrix-component model (proximity-gated magnets, springs with overshoot) is reported as unclassified, not approximated; recovered smoothing is a single exponential time constant per node
- No general physics/inertia/spring recovery; frame tracks replay observed motion verbatim
- Behavior graphing covers class-toggle machines driven by click/scroll/hover — no drags, keyboard machines, or DOM insertion/removal machines
- One output target: React + plain CSS
- Responsive breakpoints are not re-captured at multiple viewports (single-viewport clone)
- "Any website" is explicitly not claimed — read the per-node report before trusting output
MIT (see LICENSE
). All fixtures are authored for this repo (system font stacks, no external assets). Runtime dependencies: playwright (Apache-2.0), esbuild (MIT), pixelmatch (ISC), pngjs (MIT), react / react-dom (MIT) — all permissive.
Only run against pages you own or that are permissively licensed; cloning third-party commercial sites raises IP/ToS issues this tool does not resolve for you.
- Recover constraints, not pixels: fixed
width
/height
are never baked into base rules — bold text isallowedto widen a tab (layout convergence bakes sizes only for measured drift, as a reported correction). - Recover state machines, not frames — except where frames
arethe ground truth: the Chronograph tier replays sampled per-frame motion verbatim and says so. - Every claim in this README maps to a check in
npm run verify
; anything that failed or could not be observed is reported per node inconvergence.json
rather than averaged away.