aiforui logo

Setting up Lapse

You can review most of a static interface from a screenshot. Spacing, type, contrast, hierarchy, it’s all sitting there in a single frame. That’s what makes pointing an agent at a problem you want to fix relatively easy. You can literally show the frame.

Animation is the one part that doesn’t work that way.

The way I always did it is to record a video of the animation and scrub the recording until I get to the frame where it breaks, screenshot that frame, and paste it into an agent.

It’s an okay-ish flow, but it still relies on a visual medium. Ideally, you would just show the properties of the animation at that frame, that’s what an agent understands better.

That’s just one part of the review process though.

In animations.dev I repeat over and over again how important it is to analyze your animations frame by frame. Recording a video is good, but it would be great if you could do it without leaving the page, have the ability to inspect a specific frame, etc.

Or maybe slow animations down to look for some imperfections that can’t be seen at full speed. Again, you could record all the animations, but it becomes tedious quickly.

Lapse solves all of these problems.

Lapse in action.

It’s a drop-in animation inspector for web apps. Slow down time, record interactions (or just the elements you scope), scrub through transitions frame-by-frame and inspect them, annotate frames, and copy structured animation state for your coding agents.

Works with any framework, but also supports vanilla HTML/CSS and WAAPI animations.

Lapse is in development

The tool you are about to use is still a work in progress. All features described in these lessons are available today, but the tool itself will change (improve) over time. Your feedback on Lapse is very important in this process, so please share it using the form at the bottom of this page.

Lapse is a tool I acquired from Matt. He has laid the groundwork for it, and I'm grateful for his work. The animations in Lapse itself are also amazing, so definitely go give it a look! I've since updated the tool a bit based on the feedback and my own needs, but don’t hesitate to post your own findings using the form at the bottom of this page.

Installing it

Lapse isn’t on the public npm registry. It comes with the course, so it’s served from a private registry at aiforui.dev.

The fastest way to install it is to let your agent do it. This prompt has your unique install token, the registry config, and all the bits it should be aware of:

Install the Lapse animation inspector into this project...

Paste that into Claude Code, Cursor, or whatever you use, and it’ll wire up the whole thing. If you want to do it manually, or are just curious, here’s the same setup by hand:

First, your token. It’s tied to your account, and it’s the same one the skills installer uses:

export AIFORUI_TOKEN=…

Store it once, in your user-level ~/.npmrc rather than in the project:

npm config set "//aiforui.dev/npm/:_authToken" "$AIFORUI_TOKEN"

Then a project .npmrc, holding the registry and nothing else:

@aiforui:registry=https://aiforui.dev/npm/

That points only the @aiforui scope at the private registry. Everything else in your project keeps resolving against npmjs.org like normal, and the file is safe to commit because the secret isn’t in it.

Why not ${AIFORUI_TOKEN} in the project .npmrc?

It’s the obvious thing to reach for, and pnpm 11 refuses to do it: a committed .npmrc can point a scope at any host, so expanding a real credential against it would hand your token to whoever edited that line. pnpm ignores the setting and warns; npm still expands it, which is exactly the hole pnpm closed. Keep the credential user-level and this works everywhere.

Then the dependency:

{
  "devDependencies": {
    "@aiforui/lapse": "^0.19.0"
  }
}

You may be tempted to alias this to a shorter lapse with npm’s npm: syntax. Don’t. The MCP server and the Playwright adapter both depend on the scoped name, so an alias gets you two copies of the package, and getSharedEngine() returns a different engine in each. The panel would end up controlling one and your app code the other.

And install:

export AIFORUI_TOKEN=...   # if it isn't already
npm install

Because the token now lives in ~/.npmrc, there’s no environment variable to keep exported and nothing to load from .env — npm, pnpm, and Bun all read it the same way.

Yarn Berry ignores .npmrc entirely and needs its own config. Yarn does expand environment variables here, so this one keeps the ${AIFORUI_TOKEN} form in .yarnrc.yml:

npmScopes:
  aiforui:
    npmRegistryServer: "https://aiforui.dev/npm"
    npmAuthToken: "${AIFORUI_TOKEN}"

In CI, add AIFORUI_TOKEN as a secret and expose it to the install step:

- run: npm ci
  env:
    AIFORUI_TOKEN: ${{ secrets.AIFORUI_TOKEN }}

If you get a 401, the token isn’t reaching npm, which is nearly always an unexported variable rather than a bad token. If you get a 404 from npmjs.org, the scope line in .npmrc isn’t being picked up, so check the file is in your project root.

The MCP server and the Playwright adapter live in the same scope, @aiforui/lapse-mcp and @aiforui/lapse-playwright. The same .npmrc covers all three.

The end goal for this is to publish a Chrome extension so that you can inspect any animation on any website. This is coming in the future!

Dropping it in

To mount the panel, you can render it in a layout.tsx file if you are using Next.js, or the alternative in any other meta framework for React.

import { Lapse } from '@aiforui/lapse'
 
function App() {
  return (
    <>
      <YourApp />
      <Lapse />
    </>
  )
}

This also works with Vue, Svelte, or any other framework. Or even in basic HTML pages:

// main.ts — first thing in your entry
import { mountLapse } from '@aiforui/lapse/panel'
 
mountLapse()

After mounting, a floating panel shows up in the corner. It’s draggable and snaps to the nearest edge slot, but you can also tell it where to start. You can also see one on this page at the bottom.

mountLapse({ position: 'top-right' })

What you get

The panel is small on purpose, and all the actions can be performed with a shortcut. Here are all the current functions with their shortcuts:

  • Speed control (⇧S) — slow everything on the page down, or speed it up. All sorts of things, from CSS transitions and @keyframes to Framer Motion and JS timers.
  • Recording and scrubbing (⇧R) — record an interaction, then drag through the captured frames. The page freezes at the frame you’re on, and gives you the real DOM instead of a frame from a video, so you can literally hover elements and open devtools mid-transition.
  • Scoped takes (S) — hold S and select a few elements on the page. Then when you record, only the selected elements are animated. Helps when you want to focus on just a few elements when a page is busy.
  • Page-load takes (⇧L) — reload with recording already live from document_start, so the screen is captured from the first paintable frame. Useful for analyzing performance, or testing intro animations.
  • Video takes (⇧V) — records a video instead of the DOM, useful for things like canvas or WebGL that the DOM recorder can’t capture. The DOM recorder keeps running underneath, so you still get markers and the export, but scrubbing seeks the recorded video instead of freezing the page.
  • Annotations (⇧A) — while scrubbing, click an element mid-animation and type a note. It pins to that element at that moment. This will be included in the LLM export, so it’s useful when you want to highlight a specific moment in an animation.
  • LLM export (⇧C) — copy the animation state at the current frame as markdown: properties, values, easing, @keyframes source, how far through each animation is. You can also use ⇧D to cycle through how much of it you send.

Something to point it at

Here’s the hero animation from animations.dev, running live.

The panel is mounted on this page, at the bottom center of the screen. Drop the speed to 0.25× with ⇧S and hover on the illustrations. Then hit ⇧R to start recording, hover on some elements again, press ⇧R again to stop, and scrub to play with it.

A skill to help you

The rest of this workflow walks through Lapse piece by piece. I highly suggest going through these, but you can also ask the skill to help you! It’ll answer all your questions about Lapse and how to use it.

npx @aiforui/install emil-ask-lapse --token=…

It knows the installation process, every shortcut, the API, or you can ask it for some clarification on any topic. For example:

/emil-ask-lapse What does the copy button in the Lapse panel do? What ends up on my clipboard, and when should I use it?