Skip to content
MrJev

voice-browser

Voice-controlled Chromium: on each partial transcript, one Jev request judges intent, target element, and whether the command is complete or destructive.

View on GitHub →

Hands-on review

Speak, and a real Chromium acts before you finish the sentence. It worked on every step we tried — but it sends the contents of filled form fields.

Good for

  • Hands-free browsing where the commands are short and the pages are public
  • Studying a well-drawn line between what a model picks and what code does
  • Watching a typed-decision loop run live: every probability is on screen

Watch out for

  • A filled password or card field is sent to TypeSafe as that element's label
  • The controlled browser keeps a persistent profile, so it can act while logged in
  • Destructive actions need a spoken 'confirm', which is a convenience, not a guarantee

Tested Sep 20, 2026 at 054db0f3dbf5 · Node 22 and Playwright's Chromium in Docker, headless, with real Jev calls against live websites

How we reviewed this: we ran it in Docker on Node 22 with Playwright’s Chromium, headless, using a real Jev key. We ran the unit tests, the integration tests that hit the real API, and the project’s scripted demo against live websites. We then probed what its page snapshot actually contains, using its own code against a login form we wrote.

What it does

voice-browser, by Moritz Kremb, drives a real Chromium window by voice. Your browser streams partial speech transcripts over a websocket to a small Node server; on every update the server takes a compact snapshot of the controlled page and sends one Jev request carrying roughly a dozen typed questions — what is the intent, which element is the target, which site, is the command finished, is this even addressed to the browser, is it destructive — and code decides whether to act, wait, ask, or ignore.

The design principle is stated up front and held everywhere we looked: Jev never generates text. Search queries, typed text and URLs are extracted from the transcript as candidate spans by code; Jev only picks which one. Site names map to URLs through a table in constants.js. Every threshold that decides “act or wait” is a named constant in that same file, with a comment explaining what it guards.

Using it

  • The tests pass. 39 unit tests, and 28 integration tests that call the real API and check the whole answers-to-policy pipeline against saved page snapshots.
  • The scripted demo passed every step. Sixteen spoken commands against live sites — Wikipedia, Hacker News, DuckDuckGo, example.com — replayed word by word through the same controller the live server uses. Navigation, search, scrolling, going back, clicking a named link, opening and closing tabs, and two commands in one breath all did the right thing.
  • It acts before you finish speaking. In most steps the browser had already moved by the time the sentence ended. That is the point of the design: partial transcripts, one request per update, and a “is the command complete” question deciding whether to commit.
  • It knows when you’re not talking to it. “So anyway I think we should get lunch” was correctly ignored, which is the failure mode that would make this unusable if it went wrong.
  • Ambiguity becomes a question, not a guess. “Click on a link please” produced numbered overlays on the page rather than a click; answering “the first one” resolved it.

We won’t publish latency or cost figures for the underlying service, and the project’s own README reports its own.

What it sends, and the part to fix first

Each request carries the transcript (capped at 400 characters), the page URL, title and detected site, and up to 100 interactive elements as one compact line each: e09 link "GitHub - typesafe-ai" → github.com.

The element label is chosen in collectElementsInPage() (src/snapshot.js) as the first non-empty of: aria-label, innerText, el.value, placeholder, title, an image’s alt, then the input’s name.

For a text input with no label, el.value is what the user has typed. There is no exception for type="password". We ran the project’s own snapshot code against a login form to see exactly what would go out:

e04 textbox "private note text"
e01 textbox "ada@example.com"
e02 textbox "hunter2-correct-horse"      ← the password field's value
e03 textbox "4111 1111 1111 1111" (placeholder: Card number)
e05 button "Sign in"

So on a page where you have typed a password, a card number, or anything private into a field, that value is sent as the element’s label on the next voice command. The README is careful about the other risk here — the controlled browser keeps a persistent profile, so it can act inside your logged-in sessions — but says nothing about field values.

The fix is small: skip el.value for type="password", and mask it for fields whose autocomplete marks them as a card number or one-time code. We’ve opened an issue and a pull request upstream; the fix keeps a search box labelling itself with its contents.

Otherwise the data story is tidy:

  • The key stays server-side. TYPESAFE_API_KEY (or the legacy JEV_API_KEY) is read by the Node process only; the control page in your normal browser never sees it.
  • The server binds to 127.0.0.1 unless you pass --host 0.0.0.0, and the README says plainly that anyone who can reach the port can drive your browser and spend your credits.
  • Nothing goes anywhere else. Only TypeSafe, plus whatever sites you browse.
  • The state is capped: 100 elements, 60 characters per label, 24,000 characters total, so a heavy page can’t quietly become a huge request.

The safety story

Destructive intent is its own Noul question, and above the threshold the action stops and waits for a spoken “confirm” — unless intent confidence is very high and you already said confirm. It’s a real gate, and the README calls it “a convenience, not a guarantee”, which is the right framing: the model is judging whether “place the order” is destructive, and a mis-heard word is still a word.

If you try this on a profile that is logged into anything that can spend money or send messages, assume a bad hearing will eventually get through. A separate profile with nothing logged in costs you nothing and removes the whole class of problem.

Maintenance

One commit on September 18, MIT licensed, with unit tests, real-API integration tests, a scripted end-to-end demo, saved page fixtures, and a constants.js written as if someone would review it. For a one-commit project, the engineering is unusually deliberate — the comments explain why each threshold exists, not just what it is.

Verdict

This is the best-built browser agent we’ve reviewed. The split is the right one — Jev picks among candidates that code assembled, code owns every URL, every threshold, and every action — and in our hands it did the right thing on sixteen consecutive commands, including the ones designed to trip it.

Two things before you run it on your own machine: use a throwaway browser profile, and know that whatever is typed into the page’s fields travels with your next command until the label fix lands.

For other browser automation, see jev-ultrafast and jev-browser; for the desktop, agent-desktop and Jev-cu. Our Best Jev Tools roundup compares what each one sends.

See how it compares with other tools in Best Jev tools, tested hands-on.

Review updated Sep 20, 2026. Numbers quoted from the project are its author's own; we don't publish our own measurements of Jev.

More in Browser & Computer Use

jev-ultrafast

★ 24k▲ 400

browser-use/jev-ultrafast

Fast browser agent from Browser Use. Jev decides each step and which element to act on; a small model is called only when text needs to be typed. The authors report a full Google Flights search in about 7.1 seconds.

PythonReviewed

agent-desktop

★ 8.5k▲ 6.6k

vercel-labs/agent-desktop

macOS desktop automation over accessibility trees. Since v0.9.2, its jev-desktop scripts let Jev choose which control to operate and which action to take.

RustReviewed

typesafe-computer-use

★ 1.3k▲ 16

awlevin/typesafe-computer-use

macOS computer use without sending screenshots to a large model. The screen is read deterministically and Jev picks the next action.

PythonReviewed

Get new Jev projects every week

New Jev releases, pricing changes, and the best new projects, once a week. No spam; unsubscribe anytime.

Powered by Buttondown. See our privacy policy.