Video your agent can measure
Cuts, easing, colour and beats from any video, to the frame.
npx -y framelens init
Free, local and MIT. Works in Claude Code, Codex, Cursor and any MCP client.
- Time
- 26.50 s, frame 663
- Shot
- 17 of 20, 0.72 s
- Change
- move, ease-in, 15 frames, cubic-bezier(0.7, 0, 0.84, 0)
- Palette
- #F9E2D6
Frame Lens read our curve back within 0.18 px
What we animated in HyperFrames
cubic-bezier(0.22, 1, 0.36, 1)
640 px in 18 frames at 30 fps
- Measured curve
- cubic-bezier(0.231, 0.951, 0.384, 0.996)
- Start
- 10.600 s, 0 frames off
- Largest gap on any frame
- 0.18 px
- Grade
- exact
The demo above was rendered, then measured by Frame Lens. A spring of stiffness 170 read back as 170.01.
A podcast gets one sheet, a promo gets every frame
“Use this as a reference for my product promo.”
boundariesevery cut, ten samples a secondframesone sheet of the whole pieceframesevery frame around each movestyleshots, palette, moves and curvesexporta starter project, thencompare
“Turn this tutorial into a step-by-step guide.”
transcriptevery word with its timeboundarieseach screen changeframesone frame per screen, with the words said there
“Summarize this three-hour podcast.”
transcriptthe words carry the contentframesone overview sheet, nothing more
“Where does he set up DNS?”
transcriptfind the phraseframesaround the moment it is said
“What is the sound design here?”
audioloudness, hits, tempo, beat grid, spectrogramboundariescuts, to check which land on the beat
Video is measured; Lottie, Rive and websites are read exactly
| Source | How | What Frame Lens read |
|---|---|---|
| The trailer above, a video file | From pixels | 20 shots, 11 cuts, 10 fades and dissolves |
| A live website, makkov-harness | From the browser | 328 animations, mostly 500 ms on cubic-bezier(0.16, 1, 0.3, 1) |
| A Lottie file, our export read back | From keyframes | Its keyframes and curves, back exactly |
| Rive files and Figma prototypes | From the file | Keyframes and transitions |
| YouTube and most video sites | From pixels | Through a pinned yt-dlp |
The measured move in five renderers
Frame Lens writes the curve it measured into each tool. Render yours, then compare names what drifted.
/* motion.css */
#shot-15 h1 { animation: fl-enter 0.542s
cubic-bezier(0.2312, 0.9511, 0.3843, 0.9964)
10.6s both; }
// src/timing.ts
{ id: "shot-15", from: 317,
enterAt: 1, enterFrames: 16,
bezier: [0.2312, 0.9511, 0.3843, 0.9964] }
<!-- compositions/shot-15.html -->
tl.fromTo("#shot-15-title",
{ opacity: 0, y: 60 },
{ opacity: 1, y: 0, duration: 0.542,
ease: flBezier(0.2312, 0.9511, 0.3843, 0.9964) },
0.033); // tracked t1
// framelens-reference.jsx
"keyStart": 10.6, "keyEnd": 11.142,
"bezier": [0.2312, 0.9511, 0.3843, 0.9964],
"positionEase": { "outSpeed": 455.3972,
"outInfluence": 23.12,
"inSpeed": 0.6473, "inInfluence": 61.57 }
// reference.json, frame 318 to 334
"p": { "a": 1, "k": [
{ "t": 318, "s": [960, 600, 0],
"o": { "x": [0.2312], "y": [0.9511] },
"i": { "x": [0.3843], "y": [0.9964] } },
{ "t": 334, "s": [960, 540, 0] } ] }
Tested at 24, 30 and 60 fps against known answers
- 33/33
- cuts found on the exact frame
- 36/36
- easing families named right
- 1.00
- frame, the mean error on where a move starts
Synthetic clips with known cuts and curves. Read the benchmark.
Point it at a video tonight
Star on GitHubNode 20 and ffmpeg on your computer. framelens doctor --prepare checks the rest.
-
This project
npx -y framelens initAdds
.mcp.jsonand the agent skill here. -
Claude Code, everywhere
claude mcp add framelens -s user -- npx -y framelensOn Windows,
cmd /cbeforenpx. -
Any MCP client
"framelens": { "command": "npx", "args": ["-y", "framelens"] }































