- November 2026
- 13 min read
Capturing a demo in
by hand is slow. You click through the flow, wait for each page to load, check each capture, and redo the ones that came out wrong. When the product changes, you do it all again.
I built a Claude Code plugin that does the clicking. I give Claude the steps or a screen recording of the flow, and it comes back with all of the captures ready to be put into a flow. A 12-step flow takes about 71 seconds from start to finish and costs under two cents.
The plugin is built on jev-browser, an open source repo on GitHub. It adds an Agent Skill file for instructions, along with a number of scripts to help the process along.
This was tested on Built LMS, the Learning Management System I've created with AI for the past year and a half to help learn AI tools, along with the hopes and dreams of getting bought out for a billion dollars. That's not happening, sadly.
The first attempt
When Navattic first released their MCP server, it made me think of trying to let AI take the web captures itself so I could go do other work while it toils away (mostly taking naps).
I tried a few different methods, and they were all very very slow. I want to say I was using Sonnet 4.5 at the time. In Claude Code, I had it create captures for a workflow that had maybe 10 or so steps. It took at least 5 minutes. I don't remember the exact benchmarks, but it was painful. The models are much smarter and faster now, and we have decision models like Jev.
How I set up Navattic for this
I use Shift+click instead of click to capture. With click to capture on, every click takes a capture, so one misclick adds a step you don't want. Shift+click greatly reduces the chance of that, because a capture only happens when Claude means to take one.
It is currently set up to add the captures to an existing demo, because I did all of this on my free account. It can easily be configured to create a new demo and give it a name, however.
Typing steps get a Shift+click on the empty field first, so the demo shows that step before the text appears.
How Claude controls the browser
Claude already has a Chrome extension that can click and type in your browser, so a separate browser looks unnecessary. The problem is that Navattic is also an extension, and Chrome keeps extensions apart.
That means Claude's extension can't press Navattic's button or use its capture bar, because Chrome won't let one extension control another. The plugin isn't an extension, so that rule doesn't apply. It uses Playwright to drive the browser from outside, through Chrome's debugging port, and it can reach everything Navattic adds.
Playwright is Microsoft's framework for testing web apps. Claude uses it to control a real browser: open pages, click, type and read what's on screen.
Decision models
What really makes this cook are decision models. Jev burst onto the scene on September 15, 2026 and created a whole new category of AI models. A decision model picks from a set of options instead of writing text. You give it context and a question, and it returns a probability for each possible answer.
For example, you could use Jev to moderate comments before they post. Give it a comment and ask "Is this spam?" and "Allow, hide or escalate?" It returns a probability for each answer in one call, and if the value is above a certain score, it's marked as spam.
That's what fixes the slowness from my first attempt. Claude no longer has to read the whole page and pick an element at every step. It describes the element, and the decision model picks it.
Using the skill
Building a good prompt is crucial. Here's an example I used in the benchmarks:
Giving clear instructions makes it easier for Jev to follow, and giving details about the element's location helps too.
From a screen recording
You can skip writing steps. Record the flow with any screen recorder and give Claude the file and the app's URL.
video2prompt.mjs sends the whole video to Gemini and gets back a prompt in the same format as the one above. If you don't want to use Gemini, Claude will use ffmpeg to extract a frame per second and create the prompt. But if things happen in between those frames, it might miss something. Gemini has native Video Understanding built in, so its the better choice if possible.
Getting to the first capture
The plugin has three layers: one shared browser, jev-browser to drive it, and the plugin's own scripts and skill on top.
Claude reads the skill, runs the scripts from Bash, and reads one line of output per command. It only looks at the page when a step goes wrong.
setup.sh: one-time install
setup.sh puts everything the plugin needs in ~/navattic-capture and skips anything already there:
- jev-browser, built from GitHub, plus the Chromium build its Playwright version expects
- the Navattic extension from the Chrome Web Store, pinned to its store ID
opts.json, which tells jev-browser to attach to the shared browser instead of launching its own.env, copied from.env.example, for the decision-model key and an optional Gemini key
Ask Claude to set up navattic-capture, and it runs this.
browser.sh: the shared browser
browser.sh launches a developer version of Chrome with the Navattic extension loaded, a persistent profile, and Chrome's remote debugging port open. Every other tool attaches to that port, so they all share the same tabs and the same Navattic session.
The browser runs in headless mode by default. If Navattic needs a login, start.mjs stops and Claude reopens the browser in a window. You log in to Navattic and your app once in that window, and the profile keeps the logins.
start.mjs: getting to the first capture
Starting a capture session by hand takes several clicks: the extension icon, the "Choose a demo" dialog, the demo picker, and "Start capturing". start.mjs does all of it in one command and reports back with one line of JSON.
The details:
- No browser automation tool can click an extension's toolbar icon, because it isn't part of the page.
start.mjsuses Chrome'sExtensions.triggerActioncommand to enable the Navattic extension for that tab. - The "Choose a demo" dialog lives in an iframe the extension injects. Plain Playwright locators don't reach it, so
start.mjsanswers it through jev-browser's snapshot and click.
How Jev finds an element in the plugin
The plugin uses Jev from TypeSafe by default. A call takes a second or two.
jev-browser reads the page's accessibility tree, which lists each control with its role and visible name (button "Save Changes", textbox "Course Title"). It sends that list and Claude's description to Jev, and Jev scores every element. If the top score is 0.7 or higher, jev.mjs clicks it. If nothing clears the bar, the command fails, and Claude runs find, reads the list, and clicks the ref itself.
The description doesn't have to match the label exactly, but the closer it is to what's actually on the page, the better results.
Running the steps
jev.mjs: one short command per action
jev.mjs wraps the jev-browser CLI. Each command does one action, checks it, and prints one line. A failed step prints FAIL and stops the chain, so Claude can send many steps in one Bash call.
| Command | What it does |
|---|---|
tab <url part> | Selects the newest matching tab that shows the capture bar, and reads the counter |
shift "<description>" | Asks the decision model which element matches, then Shift+clicks it |
type "<description>" "<text>" | Shift+clicks the field, waits for the capture counter to go up, then types |
settle | Waits until the page has changed and then stopped changing |
capture | Presses Control+c for a capture with no click |
finish | Presses Finish, finds the "Select your captures" dialog, and closes it or builds the demo |
find "<name>" | The fallback: lists elements whose names contain a term, with refs Claude can click |
One run sent its first four steps as one Bash call:
And got back:
Why settle? Early runs moved faster than Navattic could capture. The plugin would click the next element while Navattic was still recording the last page, and the captures came out wrong. settle waits for the page to change after a click, then waits for it to stop changing, before the next step runs. That usually takes under a second and a half.
The skill: how Claude runs a capture
The skill holds the instructions Claude follows. It covers the capture choices above, the expected count (click and typing steps plus one), and the order of a run:
- Check that the browser is up, and start it if not.
- Run
start.mjswith the app URL and the demo the prompt names. - Chain the steps into as few Bash calls as possible, with
settleafter any step that opens a page or a menu. - After a save,
wait 1so the confirmation toast is in the last capture, thencapture. - Run
finish, then compare the counter and the dialog's "Generating from N captures" with the expected count.
The chaining rule does the most work. Every Claude turn costs a few seconds, and a chain of eight steps is one turn.
The benchmark
The baseline: Claude with Playwright MCP
I wanted to know how much of the speed came from the plugin and how much from Claude. So I gave Claude Haiku the same prompt with Microsoft's Playwright MCP server and no navattic-capture plugin.
How I ran it
Each setup ran the prompt five times, with Claude at low effort.
"Capturing" runs from the first capture to the end of the run. "Total" also includes starting Claude, checking the browser and starting the capture session. Claude's cost comes from Claude Code's own report.
Results
| Setup (median of five runs) | Capturing | Total | Claude cost | Jev cost |
|---|---|---|---|---|
| Claude Haiku, Playwright MCP, no plugin | 1:38 | 2:13 | $0.053 | |
| Claude Haiku, plugin, no decision model | 2:06 | 2:27 | $0.027 | |
| Claude Haiku, plugin + Jev | 0:54 | 1:14 | $0.010 | $0.0065 |
| Claude Sonnet, plugin, no decision model | 1:32 | 1:54 | $0.257 | |
| Claude Sonnet, plugin + Jev | 0:48 | 1:07 | $0.179 | $0.0065 |
Haiku with Jev beat Sonnet without a decision model by 38 seconds of capturing, at about a fifteenth of the cost with Jev included.
Why the gap
The plugin without a decision model was slower than Playwright MCP. That bothered me, so I split each run into time spent in tools and time Claude spent thinking between them:
| Setup (median per run) | Tool calls | Time in tools | Claude thinking | find calls | Tool output Claude read |
|---|---|---|---|---|---|
| Haiku, Playwright MCP | 57 | 52 s | 83 s | 0 | 894,000 characters |
| Haiku, plugin, no decision model | 30 | 76 s | 63 s | 24 | 11,000 characters |
| Haiku, plugin + Jev | 7 | 52 s | 19 s | 0 | 3,000 characters |
| Sonnet, plugin, no decision model | 18 | 60 s | 49 s | 12 | 8,000 characters |
| Sonnet, plugin + Jev | 6 | 53 s | 12 s | 0 | 2,000 characters |
Without a decision model, the plugin can't locate anything from a description, so each step becomes three commands: a shift that fails, a find, and a click on the ref find returned. Claude has to read each find result before the next step, so it can't chain. Each find also starts a fresh Node process, which starts the jev-browser CLI, which reconnects to the browser and snapshots the whole page. That costs 2 to 4 seconds, about 24 times a run.
Playwright MCP keeps one connection open and hands Claude the next page with every click, so its tools are quick. The cost lands on Claude instead: it reads close to a million characters of snapshots per run and spends more time thinking than waiting on tools.
Decision models compared
Jev isn't the only decision model the plugin can use. Each of these ran the same prompt twice with Haiku, on an earlier version of the test form:
| Decision model | Capturing, run 1 | Capturing, run 2 | Cost per run |
|---|---|---|---|
| liquid/d1-ultra | 0:47 | 0:52 | $0.0055 |
| baseten/elephant-1 | 0:56 | 0:49 | $0.0053 |
| liquid/d1.1 | 1:01 | 0:50 | $0.0027 |
| microsoft/microsoft-decision-1 | 1:02 | 1:00 | $0.0048 |
| openai/gpt-6-luna-decisions | 1:00 | 1:08 | $0.0112 |
| perplexity/pplx-decider-v1.2 | 0:54 | 1:16 | $0.0027 |
| perplexity/pplx-decider-flash-v1.2 | 1:11 | 1:33 | $0.0014 |
These were all run from OpenRouter, which the plugin supports by configuring the .env file.
Try it
You need Claude Code, Node.js 22.15 or later, and a Decision model API key.
-
In Claude Code, add the plugin:
-
Ask Claude to set up navattic-capture. It installs the browser and the Navattic extension.
-
Configure your decision model provider, model choice, and API key in
~/navattic-capture/.env. -
In the browser window that opens, log in to Navattic and your app.
-
Give Claude your steps or your recording.
Every run's numbers are in the README.