Show HN: Motion-contact-sheet – give a coding agent eyes for motion

kal90001 pts0 comments

GitHub - Kallin/motion-contact-sheet: Give a coding agent eyes for motion: capture a UI animation as a timed burst and tile it into one labeled contact sheet a vision model can read. · GitHub

/" data-turbo-transient="true" />

Skip to content

Search or jump to...

Search code, repositories, users, issues, pull requests...

-->

Search

Clear

Search syntax tips

Provide feedback

--><br>We read every piece of feedback, and take your input very seriously.

Include my email address so I can be contacted

Cancel

Submit feedback

Saved searches

Use saved searches to filter your results more quickly

-->

Name

Query

To see all available qualifiers, see our documentation.

Cancel

Create saved search

Sign in

/;ref_cta:Sign up;ref_loc:header logged out"}"<br>Sign up

Appearance settings

Resetting focus

You signed in with another tab or window. Reload to refresh your session.<br>You signed out in another tab or window. Reload to refresh your session.<br>You switched accounts on another tab or window. Reload to refresh your session.

Dismiss alert

{{ message }}

Kallin

motion-contact-sheet

Public

Notifications<br>You must be signed in to change notification settings

Fork

Star

main

BranchesTags

Go to file

CodeOpen more actions menu

Folders and files<br>NameNameLast commit message<br>Last commit date<br>Latest commit

History<br>1 Commit<br>1 Commit

.claude/skills/contact-sheet

.claude/skills/contact-sheet

demo

demo

scripts

scripts

.gitignore

.gitignore

LICENSE

LICENSE

README.md

README.md

package-lock.json

package-lock.json

package.json

package.json

View all files

Repository files navigation

motion-contact-sheet

Give a coding agent eyes for motion. Capture a running UI animation as a timed<br>screenshot burst, tile it into one labeled contact sheet , and hand that single<br>image to a vision-capable agent so it can actually see how the motion looks, not<br>just that the tests pass.

Coding agents are blind to animation. They read the test log, watch it go green, and<br>never notice that the card snaps the last few pixels into place, or flashes for a frame<br>before it flies. A contact sheet turns "perceive motion over time" (which agents are bad<br>at) into "read one picture" (which they're good at).

Full write-up, with live demos: Giving Coding Agents Eyes for Motion

Install

npm install<br>npx playwright install chromium<br>npm link # optional: exposes `mcs-capture` and `mcs-sheet` on your PATH

Dependencies are just playwright (the burst) and<br>sharp (the tiling).

Use

Two steps: burst-capture the animation against a running page, then tile it.

# 1. capture (slow it down so the frames land on the motion, clip to what moves)<br>mcs-capture --url http://localhost:3000 --clip ".toast" --slowdown 6 --out frames/

# 2. tile into one labeled contact sheet<br>mcs-sheet frames/ --edges

Open frames/contact-sheet.png, or have your agent read it. A .md companion lands<br>next to it with a per-cell timing table that stays readable after the image is downscaled.

The three things that matter

Slow it down. A browser screenshot has a floor around 40-50 ms, so a 200 ms<br>transition gives you two or three usable frames. --slowdown 6 stretches the animation<br>so the burst can sample it; the sheet header records the factor and the labels report<br>native time. Slow it enough that the whole animation fits the capture window:<br>count x interval (wall-clock) needs to exceed duration x slowdown. It slows both<br>CSS/WAAPI animations and a GSAP global timeline (auto-detected), so it works whether your<br>motion is CSS transitions or GSAP tweens. This matters: getAnimations() can't see GSAP<br>tweens, so without the GSAP path a GSAP-driven app would capture at full speed (a blur)<br>while the sheet still claimed the slowdown.

Clip to the motion. --clip "" captures only the region that moves.<br>Without it the motion is a thumbnail in a sea of static chrome. The builder also<br>auto-crops to the bounding box of pixels that actually change.

--edges for end-state bugs. Snaps and flashes live in the first and last few<br>frames, exactly where motion-aware sampling is thinnest. --edges densifies the launch<br>and settle windows so they don't get skipped.

mcs-capture

flag<br>default<br>what

--url<br>(required)<br>page to capture (http(s):// or file://)

--clip<br>none<br>CSS selector for the region to clip each frame to

--play<br>none<br>JS to (re)start the animation, e.g. "document.querySelector('.btn').click()"

--slowdown<br>slow animations by N — both CSS/WAAPI (getAnimations()) and a GSAP global timeline if window.gsap is present (auto-detected, no extra flag)

--count<br>60<br>frames to capture (oversample; the builder keeps the meaningful ones)

--interval<br>80<br>target ms between frames

--viewport<br>1000x700<br>browser viewport, WxH

--out<br>frames<br>output directory

mcs-sheet

flag<br>default<br>what

(required)<br>directory of frame-*.png + a meta.json/timestamps.json sidecar

--select<br>12<br>keyframes to keep (--edges adds a few endpoint/launch/settle anchors on top)

--edges<br>off<br>densify the launch + settle windows (catch flash /...

sheet motion contact capture frames animation

Related Articles