Guide

Build an Anthropic Claude app without code, and let Claude drive the browser

Almost every guide on this stops at the same place: a Claude model outputs code, a preview pane renders it, you copy it out. mk0r moves the finish line. Inside an E2B Linux sandbox, Claude opens the app it just wrote in a real Chromium, clicks through it with Playwright MCP, reads the browser console, and refuses to say done until the DOM actually shows what you asked for. The stack is in docker/e2b/files/opt/startup.sh.

M
Matthew Diakonov
9 min
4.8from 10K+ creators
Real Chromium, driven by Playwright MCP
DOM snapshot + console read before 'done'
No account to try; no copy-paste step

Where every other guide stops

If you line up the most common articles on this topic, they all describe the same loop. You open Claude. You describe an app. A code block appears. You copy it into a file, or you share an Artifact link. The story ends there. The implicit assumption is that "Claude generated code" equals "an app that works." Anyone who has pasted an AI-written component into a real project knows that gap is where the time goes: a missing import, an undefined state, a typo in a Tailwind class, a 500 on the first fetch. Compile-time green says nothing about any of it.

mk0r makes a different bet. The interesting part of a no-code Claude workflow is not the first token of output. It is what happens after the code is written and before the human has to squint at a preview. That is where a real browser is worth more than a better model.

What mk0r boots when you type the first sentence

The moment you land on mk0r.com, the page fires a prewarm against an E2B sandbox running the mk0r-app-builder template. That template is a Debian image with a very specific process tree. Here is the literal startup sequence. This is the file; nothing is paraphrased.

docker/e2b/files/opt/startup.sh

Five processes matter for this story. Xvfb draws a virtual 1600x1600 display. Chromium attaches to that display and opens CDP on 9222. x11vnc mirrors the display on VNC port 5900; websockify wraps that as a WebSocket on 5901, which is what the mk0r UI opens so you watch Claude work live. Playwright MCP listens on 3001 and speaks CDP to the Chromium on 9222. Vite serves the app the agent is writing on 5173. Five processes, one feedback loop.

How a prompt turns into a working DOM

Your prompt
E2B template
Claude Code ACP
Playwright MCP on 3001
Chromium on display :99
Vite dev server on 5173
Your browser, via :5901

The versions we pin, and why

A build-a-Claude-app workflow with a live browser breaks the second Playwright MCP and Chromium drift apart. That is why every globally-installed runtime is pinned at image build time. The relevant block is short.

docker/e2b/e2b.Dockerfile

If Anthropic releases a new Claude Code build or Playwright MCP ships an incompatible version, mk0r does not inherit it silently. We rebuild the template on purpose. A no-code flow that breaks because a dependency moved underneath it is not a product you can trust with a first-time builder.

A turn, in actual tool calls

This is what a single turn looks like inside the sandbox when you ask for a tiny tool. The terminal is real; the lines are the outputs the agent produced while completing the turn.

Inside the sandbox: one prompt, one verified app

Every line after "Vite HMR applied update" is the part other guides skip. The agent did not finish on "wrote 84 lines." It finished on "the browser shows the right number after I filled the inputs." That is the actual contract.

Two finish lines, laid out

The table below compares the finish line a typical no-code Claude flow uses against the finish line mk0r enforces. Both start with the same first tokens. They diverge sharply at the end.

FeatureTypical no-code Claude flowmk0r
Where the output livesA code block in a chat window, or a sandboxed iframe artifactA Vite + React + TypeScript project at /app/src inside a 4 vCPU / 4 GB Debian VM
Is there a real browser?No. The 'preview' is usually a browser-side eval or an iframe in the chatYes. Chromium on display :99 with CDP on 9222, booted by startup.sh on every session
What 'done' meansThe code block rendered. If it works when you paste it, lucky youPlaywright snapshot returned the expected DOM and browser_console_messages came back clean
When something breaksYou notice, not the agent. You paste the error back into chatThe agent reads the console mid-turn, patches, re-navigates, re-snapshots, and only then reports
How you see the work happenStreaming tokens in the chat. You guess what the DOM looks likeA live VNC of the Chromium the agent is driving, via websockify on :5901
What you do to startSign up, pick a plan, confirm email, set up a workspaceType a sentence on mk0r.com. No account, no card, no preamble

Numbers the stack actually has

Every number below is a real line in a real file in the public mk0r repo. Nothing is rounded for a pitch.

0vCPU per sandbox (e2b.toml)
0MB RAM per sandbox
0Xvfb display, square px
0CDP port on Chromium

Where the interesting constraints live

A real-browser loop is not free to run. The VM has to boot fast, the agent has to know which port does what, and the user has to see something on screen within the first second. Each of those constraints shaped a specific product choice.

Sandbox boots are prewarmed

mk0r.com fires POST /api/vm/prewarm on mount, so a fully booted E2B sandbox is usually waiting by the time you finish typing the first prompt. Cold-booting Xvfb, Chromium, Playwright MCP, and Vite from zero is slower than any human wants to wait for 'my app builder is thinking.'

Chromium uses a persistent profile

The --user-data-dir points at /root/.chromium-profile so cookies, localStorage, and any logins the agent performs survive within a session. A login flow the agent tests once does not have to be re-done on the next turn.

Web traffic goes through a residential proxy

Chromium launches with --proxy-server=http://127.0.0.1:3003, which is a local shim that forwards to a Bright Data residential upstream. Loopback and localhost bypass the proxy so internal services (Vite HMR, Playwright MCP) stay direct.

The UI reads the agent's browser directly

Instead of mirroring screenshots back, mk0r opens a VNC-over-WebSocket connection to websockify on :5901. You watch the actual pixels Chromium is painting inside the sandbox, at 1600x1600, with zero intermediate compression step for debugging.

One prompt, step by step

If you want to trace a single request end to end, this is the shape of it. The timeline below is the order events fire the first time you submit on a cold session.

From first tap on the prompt box to a verified app

1

Land on mk0r.com with no account

A prewarm call to /api/vm/prewarm asks E2B to wake or reuse an mk0r-app-builder sandbox. Firestore holds session state so if Cloud Run restarts, your sandbox is still there.

2

The sandbox process tree comes up

startup.sh brings up Xvfb, Chromium, x11vnc, websockify, brd-proxy, Playwright MCP, the ACP bridge, and Vite. The reverse proxy on :3000 routes /acp, /mcp, /sse, /screencast, /input, and /vnc to the right internal service.

3

The agent reads its rulebook

globalClaudeMd lands in /root/.claude/CLAUDE.md and projectClaudeMd in /app/CLAUDE.md before the first user token is seen. The agent knows /app is the only directory it can write to, and knows the browser is where it verifies.

4

Your sentence hits Claude Code ACP

The @agentclientprotocol/claude-agent-acp bridge receives the prompt. Claude writes files under /app/src; Vite HMR pushes each edit straight into the running Chromium.

5

The agent opens the app in Chromium

Via Playwright MCP: browser_navigate http://localhost:5173, then browser_snapshot to read the DOM, then browser_console_messages to see if anything exploded.

6

It either fixes or finishes

If the snapshot is wrong or the console has errors, the agent edits files and re-verifies. When the DOM matches the prompt and the console is clean, it commits the turn to local git and reports completion.

7

You see the same pixels the agent saw

The mk0r UI streams the VNC-over-WebSocket feed from :5901 throughout. What the agent saw while deciding the turn was done is exactly what appears on your screen.

When Quick mode is the right answer

Not every build-a-Claude-app-without-code request wants a VM. A one-shot tip splitter, a countdown timer, a QR generator, a handful of styled HTML controls; for any of those, the VM boot is overhead. Quick mode is the escape hatch. It streams standalone HTML + CSS + JS directly from claude-haiku-4-5, pinned as FREE_MODEL at src/app/api/chat/model/route.ts line 5, and drops it straight into a preview frame. Typical completion is under thirty seconds. No sandbox boot, no Playwright verification loop; you trade the real-browser safety net for latency.

The rule of thumb we use: if the app has state that the next prompt will mutate, you want VM mode. If the app is a static tool someone will use once and walk away from, Quick mode is the kinder choice.

What this is not

Nothing here replaces an engineer who needs to ship a regulated payments flow or a multi-tenant backend. A sandbox with a Playwright loop is not the same as an on-call rotation. The claim is narrower. Between "I have an idea, I have no code, and I do not want to copy-paste from a chat" and "I will build this properly in a real repo," there is a middle ground, and that middle ground is better when the agent has to look at the output in a real browser before it can call itself done. The no-code version of Claude is more useful when it is not allowed to lie to itself.

Want to watch the real-browser loop on your own prompt?

Book 20 minutes. We open mk0r live, type a sentence together, and walk through the Playwright tool calls the agent makes before it reports done.

Frequently asked questions

What does it actually mean to build an Anthropic Claude app without code on mk0r?

You open mk0r.com with no account, type a sentence like 'budget planner that splits income across buckets,' and a Claude agent running inside an E2B Linux sandbox writes a Vite + React + TypeScript app at /app/src, opens http://localhost:5173 in a real Chromium, takes a Playwright snapshot of the DOM, reads the browser console, and reports back. You never touch a keyboard for code. The agent does not claim done until the browser shows the result.

How is this different from Claude Artifacts or Claude's own no-code app builder?

Artifacts are sandboxed iframes rendered inside the Claude chat. There is no operating system, no filesystem to write to, and no browser the agent can open to verify its work. On mk0r, Claude is the agent and the sandbox is a 4 vCPU, 4,096 MB Debian VM with Chromium on display :99 and Playwright MCP wired into CDP on port 9222. The agent does not preview the app; it uses the app.

How does Claude actually drive the browser?

The sandbox startup script at docker/e2b/files/opt/startup.sh launches Chromium with --remote-debugging-port=9222, then runs `npx @playwright/mcp --cdp-endpoint http://127.0.0.1:9222 --port 3001 --host 0.0.0.0`. Playwright MCP (pinned at @playwright/mcp@0.0.70 in the Dockerfile) exposes navigate, snapshot, click, fill, and console-read tools over MCP. The agent calls those tools the same way it calls file tools. No screenshot scraping, no OCR. Real Playwright on a real Chromium.

Do I see any of this happen?

Yes. x11vnc exports the Xvfb display on rfbport 5900, and websockify bridges that to port 5901 as a WebSocket. The mk0r UI opens a VNC-over-WebSocket connection to that port, so you watch the exact Chromium the agent is driving. When Claude clicks a button, you see the button highlight.

Why does 'the agent verifies in the browser' matter?

Because most no-code generators ship on 'the code compiled.' That is a false finish line: a missing import, a Tailwind class that does not resolve, a 500 on a fetch, or a state bug all pass the compile gate and break the actual page. mk0r's agent rulebook at src/core/vm-claude-md.ts requires the agent to navigate, snapshot, read console, and only then report completion. When the browser is blank, the work is not done.

What if I want a static tool and not a full Vite app?

Quick mode exists for that. It skips the VM and streams standalone HTML + CSS + JS out of claude-haiku-4-5 directly into the preview, with no sandbox boot. Typical completion is under 30 seconds. The tradeoff is that there is no real browser verification loop; you get the output as-is. Use Quick mode for one-shot calculators and static widgets, VM mode for anything that needs routing, state, or a backend.

Is this tied to an Anthropic Claude subscription?

No. The free tier runs claude-haiku-4-5, pinned as FREE_MODEL in src/app/api/chat/model/route.ts line 5, via an anonymous Firebase session. No card, no signup. If you want to switch models or run larger prompts, there is a paid plan, but the first working app costs nothing and needs no Anthropic account of your own.

Can I get the code out?

Yes. Everything the agent writes lives at /app/src inside the sandbox, under a local git repo the agent commits to turn by turn. You can publish the project to a subdomain, or export the files. No proprietary lock-in, no black-box runtime.

What is running inside the sandbox besides Chromium?

Node 20, Xvfb on display :99 at 1600x1600x24, x11vnc on rfbport 5900, websockify on 5901, Playwright MCP on port 3001, an ACP bridge on 3002, a reverse proxy on 3000, and Vite dev server on 5173. There is also a Bright Data residential-IP upstream proxy on 3003 that Chromium routes through for web fetches. All of it boots on template `mk0r-app-builder` (id 2yi5lxazr1abcs2ew6h8).

What kinds of apps has this actually built?

Waitlist sites with a Postgres database wired on turn one via pre-provisioned DATABASE_URL. Internal dashboards with PostHog analytics auto-attached. Small tools like habit trackers and recipe scalers, one-prompt. Landing pages with Resend email capture using a pre-provisioned RESEND_AUDIENCE_ID. The common thread is that the agent never had to ask the user for an API key, because /app/.env was seeded before the first prompt.

No account. No copy-paste. The Chromium that verifies your app is streamed back to you on port 0.

Build a Claude app on mk0r