Describing to an agent what you are seeing, or what you want to get fixed when it comes to motion, can be a bit hard. Screenshots work some of the time, but then the agent is working off of those screenshots. That’s good, but not ideal.
With Lapse you can copy the state of a given frame and send it to your agent. This way your agent has much more information to work with, and on top of that, the information is precise, which agents love.
While you’re scrubbing, find the frame that feels wrong and copy the state of the frame. Now instead of describing the problem and hoping the agent finds it, you point at it.
Scrub and export the state of the frame.
Here’s a visualization of this functionality to give you a better idea of what it does. If you scroll down in the export below, you’ll see how the opacity and transform values change as the frame progresses.
Recent activity
328ms · opacity 0.92 · transform done
# Animation State at 328ms into 620ms recordingInteraction state:- Hovered: "Account"- Focused: "Account"## Performance (LoAF tier, recorded at 0.25×, display 120Hz)- No long frames (≥50ms) observed- No dropped-frame clusters detected- Animated property costs: Account menu animates opacity (composite), transform (composite); 6 × Loading row animates transform (composite)## Triggers (evidence-graded, not causation claims)- click on "Account" @ 0ms- data-state changed on Account menu (+4ms, handler) → "opacity", "transform" started## "Account menu" div.menuTransitions: opacity 500ms cubic-bezier(0.7, 0, 0.84, 0)- opacity: 0.92 [1 → 0] (500ms cubic-bezier(0.7, 0, 0.84, 0) @ 8%)…plus 7 ambient animations on 7 uninvolved elements — filter 'active' or 'all animations' lists them.
Our goal here is to fix the fade duration since it hangs for too long after the scale animation finishes. We could of course just ask the agent to fix it by describing the problem, but we can also record the animation, scroll to the wrong frame, and copy the state of that frame.
Thanks to this, we are able to hand the exact state of the frame to the agent, which allows it to fix the problem much more easily, because it can see everything that happened at a given moment.
Here’s a visualization of this process and its advantages:
A screenshot can only see a visual representation of the frame, it can’t say which transition is off. “Heavy” usually means slow, so the reasonable guess is a shorter fade:
The agent can see exactly what is going on: the fade runs 500ms on an ease-in, but it’s still 92% opaque at 328ms that’s 160ms after the transform finished. The curve is the bug, not the length:
# Animation State at 328ms into 620ms recording## Annotations (user feedback pinned to elements at timeline moments)### 1. "feels heavy on the way out" — at 328ms- Element: [data-account-menu] Account menu div.menu- Position: 165×102 at (155, 48) viewport, in its 328ms state- Animation state at 328ms:- opacity (500ms cubic-bezier(0.7, 0, 0.84, 0) @ 8%)- opacity: 0.92 [1 → 0]- transform (160ms cubic-bezier(0.19, 1, 0.22, 1) @ done)- transform: matrix(0.98, 0, 0, 0.98, 0, -4) [translateY(0px) scale(1) → translateY(-4px) scale(0.98)]Interaction state:- Hovered: "Account"- Focused: "Account"## Performance (LoAF tier, recorded at 0.25×, display 120Hz)- No long frames (≥50ms) observed- No dropped-frame clusters detected- Animated property costs: Account menu animates opacity (composite), transform (composite); 6 × Loading row animates transform (composite)## Triggers (evidence-graded, not causation claims)- click on "Account" @ 0ms- data-state changed on Account menu (+4ms, handler) → "opacity", "transform" started## "Account menu" div.menuTransitions: opacity 500ms cubic-bezier(0.7, 0, 0.84, 0)- opacity: 0.92 [1 → 0] (500ms cubic-bezier(0.7, 0, 0.84, 0) @ 8%)…plus 7 ambient animations on 7 uninvolved elements — filter 'active' or 'all animations' lists them.
The export might not be as useful for very simple animations, but it’s incredibly useful when working with complex, multi-step animations. When your agent gets stuck, or can’t find the problem, you can feed it the state of the frame you are referring to and it will know exactly what to fix.
To be even more precise about what you want to get fixed, or changed, you can use the annotate feature to comment on specific elements.
The exported prompt then looks roughly like this:
# Animation State at 2905ms into 7085ms recording## Annotations (user feedback pinned to elements at timeline moments)- 1. "Can we make this thing wiggle a bit more right around this frame?" @ 5172ms — [data-animate="bell"] › path:nth-child(1): transform @ 73% (on ancestor); transform @ 58% (on ancestor); transform @ 31% (on ancestor); transform @ 46% (on ancestor)- 2. "Right now this element is lifting, can we lift it a bit more?" @ 2905ms — [data-animate="bulb"]: transform @ 40% (on ancestor); transform @ 13% (on ancestor); transform @ 33%; opacity @ upcomingInteraction state: hovered "div.layout-root"> Withheld at brief detail: elements (416 tracked), JS timing, performance, triggers — a higher detail level (or the dedicated call) includes them.> Adaptive sampling: frame spacing widened to ≤37ms on this heavy page — scrub granularity is coarser.- g.will-change-transform — [role=img] › g:nth-child(1) > g:nth-child(1): transform (JS)- g.will-change-transform — [role=img] › g:nth-child(2) > g:nth-child(2) > g:nth-child(1): transform (JS)- g.will-change-transform — [role=img] › … > g:nth-child(1) > g:nth-child(1): transform (JS)- g.will-change-transform — [role=img] › g:nth-child(2) > g:nth-child(1) > g:nth-child(1): transform (JS)- g.will-change-transform — [role=img] › g:nth-child(2) > g:nth-child(1) > g:nth-child(2): transform (JS)- g.will-change-transform — [role=img] › g:nth-child(3) > g:nth-child(1): transform (JS)- g.filter-[url(#filter4_i_359_1453)].dark:filter-[url(#filter4_i_368_1560)].will-change-transform: transform (JS)- g.will-change-transform — [role=img] › g:nth-child(4) > g:nth-child(1): transform (JS)- g.filter-[url(#filter7_i_359_1453)].dark:filter-[url(#filter7_i_368_1560)].will-change-transform: transform (JS)- g.will-change-transform — [role=img] › g:nth-child(5) > g:nth-child(2) > g:nth-child(1): transform (JS)- g.filter-[url(#filter6_i_359_1453)].dark:filter-[url(#filter6_i_368_1560)].will-change-transform: transform (JS)- g.will-change-transform — [role=img] › g:nth-child(6) > g:nth-child(1): transform (JS)- g.filter-[url(#filter5_i_359_1453)].dark:filter-[url(#filter5_i_368_1560)]: transform (JS)- g[data-animate='ball'] g: transform (JS)- g[data-animate='pulse'] g: opacity (JS)- g[data-animate='background'] g: transform (JS)- g[data-animate='whole'] g: transform (JS)- path.fill-[#989898].dark:fill-[#D6D6D6]: transform (JS)