A mobile app generator that opens its own output in a real browser before saying done
Most tools in this category write the code and hand you a live preview. mk0r does that, then does one more thing that nobody else on the first page of results does: it runs a Playwright-controlled headless Chromium in the same sandbox as the agent, instructs the agent to navigate to the generated app at phone dimensions, take a DOM snapshot, and read the browser console before reporting completion. Here is where that loop lives and how it changes what gets built.
The gap every other mobile app generator leaves
Read the public roundups of mobile app generators: Adalo's best free list, Figma's AI app builder writeup, Blink's own description of its workflow. They all describe the same loop. Natural language goes in, code comes out, a live preview renders, and you iterate. What is missing in every one of them is a verification step that happens before the preview updates. If the AI writes a broken import, you notice it. If a useEffect throws, you notice it. The person doing the testing is you.
mk0r's sandbox does not assume you are the quality gate. The E2B VM that spins up when you start a new session boots a full Chromium with remote debugging, starts the Playwright MCP server on top of that, and starts a Vite dev server at localhost:5173. The agent is instructed to use all three.
The anchor fact: the agent's own instructions
Inside every mk0r sandbox, at /root/.claude/CLAUDE.md, there is a section called Browser Testing. This file is copied into the E2B template image at build time and is the first thing the agent reads. The relevant block reads exactly like this:
The agent cannot silently skip this. Its whole operating manual lives in that file, and the browser-testing rule is written as a gate on completion. If the generated app is blank, the agent is told to diagnose, not to claim success.
Where the verification loop lives in the VM
The sandbox boots a handful of cooperating services in a single script. Here is the exact sequence that makes self-verification possible:
Three processes, one pipeline. The agent talks to Playwright MCP on port 3001. Playwright drives Chromium on 9222. Chromium loads the app that the agent is writing to /app/src via Vite on 5173. The agent never trusts that the code it wrote actually renders; it asks the browser.
The shape of the loop
One prompt from you fans out into a small constellation of services in the sandbox, then collapses back into the preview you watch.
What your prompt touches
What a single iteration looks like from the inside
Imagine you asked for a habit tracker with a bottom tab bar. After the agent writes the first pass of App.tsx and two components, here is the sequence that actually plays out before the agent tells you anything.
One change, one verification
If the snapshot shows a blank body, the agent is told not to report done. It checks that the new component is imported from App.tsx, re-writes the file, and runs the same loop again.
A real transcript of the agent checking its work
The sandbox's agent speaks over the ACP bridge. The part you do not see directly is its tool calls into Playwright MCP. Condensed into a terminal-shaped view, a single turn looks roughly like this:
In a broken run the console read would surface a React warning about a missing key or an exception from a bad import. The agent would loop back, patch, and re-navigate. Every attempt is logged into the sandbox's session file, so a rerun later can pick up the state.
The checklist the agent runs every turn
The agent's operating manual collapses to a handful of hard rules. These live in the sandbox's CLAUDE.md, and the agent re-reads them each session:
What has to be true before done
- New components are imported from App.tsx or they never render
- Chromium at localhost:5173 renders a non-blank document
- browser_console_messages is empty of runtime errors
- Styles respect the three-color rule: black, white, one accent
- Layout is built mobile-first, with sm and md as progressive enhancements
- .env values are trimmed of trailing whitespace before use
How the preview frame and the bridge cooperate
The agent's verification loop runs inside the sandbox. The preview iframe you watch runs inside your browser tab. They stay in sync because a tiny file called src/_mk0rBridge.ts is appended to the Vite scaffold at image build time and listens to HMR events from Vite itself:
The preview component reads those messages and decides whether the iframe needs a hard reload. If hmr:after arrives within 800ms, nothing reloads. Your scroll position holds, the input you were typing into stays focused, the local component state survives. If the bridge goes quiet, the iframe cache-busts with the previous frame still rendered underneath so the phone frame never flashes white.
The end-to-end shape of a build
Start to end, a single prompt into the mobile frame touches each of these steps:
You type an idea
A prompt like 'a habit tracker with a bottom tab bar and a weekly email digest' is enough. No template picker.
A pre-warmed VM is claimed from the pool
mk0r keeps a Firestore-tracked pool of E2B sandboxes already warmed through the full init sequence. Claiming one is typically faster than a cold boot.
The agent writes to /app/src
Files land in the Vite project tree. _mk0rBridge is already injected, so HMR starts talking to your preview frame immediately.
Playwright navigates to localhost:5173
Inside the same sandbox, the headless Chromium opens the URL, takes a DOM snapshot, and pulls the console message buffer.
The agent decides if it is done
If the snapshot is non-blank and the console is clean, the turn reports success. If not, the agent loops into a fix and re-runs the navigation.
You see the result in the 390 x 844 frame
By the time the message appears in your chat, the browser inside the VM has already confirmed the app renders. You can also copy the preview URL onto your phone and watch the same thing live.
What the verification loop replaces
A side by side of the default mobile app generator workflow and what mk0r does instead.
| Feature | Typical mobile generator | mk0r |
|---|---|---|
| Who checks the generated app renders | You, after the preview updates | The agent, via Playwright snapshot, before it reports |
| Runtime console errors | Visible only if you open devtools | Read from browser_console_messages inside the sandbox |
| Preview viewport | Responsive rectangle, desktop default | 390 by 844, clamped to iPhone 14 Pro dimensions |
| Iteration after a broken change | You spot it, type a correction, wait | Agent sees the blank DOM, patches, re-navigates |
| Backend wiring | Configure database, email, auth separately | Neon, Resend, PostHog already in /app/.env |
| Account required to start | Yes, email and tier | No. Type and build |
| Source ownership | Proprietary format, export tier gated | Plain Vite React project you can download and fork |
The kinds of mobile apps this loop is especially good at
Self-verification is most valuable when the app has moving parts the agent can break in a subtle way. Anything that routes, fetches, stores, or animates. A few examples where the verification loop pulls real weight:
Habit tracker with weekly email digest
Bottom tab nav, local-first state, Resend API for the Sunday morning summary. One prompt because Resend is already in /app/.env.
Bookmark inbox with tags
Neon-backed list, swipe gestures, tag chips. Snapshots catch missing imports before you see them.
Morning standup logger
Text input, PostHog event stream, recap view. Console errors from a bad useEffect surface inside the sandbox.
Recipe flow with timers
Routing between ingredients, steps, and timer screens. The agent verifies each route renders before saying done.
Run log with pace per km
Chart rendering, local storage. Blank charts are the kind of bug that only shows up in a real browser.
Prompt journal with photos
MediaDevices camera access, feed of cards. Permission errors land in the console where the agent reads them.
Want to watch the verification loop run on your idea?
Book a 20 minute call and we will build one of your mobile app ideas live while the sandbox checks its own work between turns.
Book a call →Frequently asked questions
What does mk0r do that a typical mobile app generator does not?
Every other tool in this space writes code and hands you the preview. mk0r also runs a headless Chromium next to the agent inside the same E2B sandbox, and the agent's own instructions at /root/.claude/CLAUDE.md require it to navigate to http://localhost:5173 via Playwright MCP, snapshot the DOM, and read browser_console_messages for errors after any UI change. If the console is red or the page is blank, the agent is told not to report completion.
Is the generated app native iOS or Android, or a web app?
mk0r generates a mobile web app rendered in a 390 by 844 iframe, the CSS pixel dimensions of the iPhone 14 Pro viewport. The output is plain HTML or a Vite plus React plus TypeScript project you can keep editing. You can later wrap it with Capacitor or Expo for a store listing, or install the preview URL as a Progressive Web App from your phone's share sheet.
How does the AI know when something it built is broken?
Inside the sandbox the agent has the Playwright MCP server running at http://127.0.0.1:3001, wired to a Chromium instance on remote-debugging port 9222. After each change the agent navigates to the local Vite dev server, takes a DOM snapshot, and queries browser_console_messages. Runtime errors show up there, so a broken import or a thrown exception in a useEffect is caught by the agent, not dumped on you.
Why is there a phone-sized viewport and an HMR bridge on top of that?
The preview iframe in your browser is framed at 390 by 844. A small script called _mk0rBridge is injected into the generated app and listens to Vite's HMR lifecycle. When a file changes, the bridge posts hmr:before and hmr:after messages to the parent window. The preview component waits up to 800ms for hmr:after. If HMR hits, nothing reloads. If it misses, the iframe cache-busts with the previous frame still rendered underneath, so there is no white flash on the phone frame.
What is pre-wired so I can build a real mobile app in one prompt?
Every sandbox ships with an .env file that already contains DATABASE_URL for a Neon Postgres instance, RESEND_API_KEY for transactional email, VITE_POSTHOG_KEY for analytics, and a GITHUB_REPO_URL for source control. A prompt like 'a habit tracker that emails me a weekly digest' works in one turn because the agent does not need to stop and ask you for credentials.
Do I need to install anything or create an account before I can start?
No. There is no signup, no Xcode, no Android Studio, no SDK. You open mk0r.com, type the idea, and the preview begins filling in. The sandbox is pre-warmed in a Firestore-backed pool, so the VM is usually ready before you finish your first sentence.
Can I see the actual source files the agent runs against?
Yes. The generated project lives at /app inside the sandbox. It is a standard Vite React TypeScript tree with Tailwind v4. You can download it, push to GitHub, or keep iterating. The agent's own operating rules, like 'three colors maximum, no decorative icons, mobile-first breakpoints', are stored in /app/CLAUDE.md and /root/.claude/CLAUDE.md inside the VM, and both files are open source in the mk0r repo.
How does this compare to Appy Pie, Adalo, Bubble, Blink, or Emergent?
Those tools occupy different points on the no-code spectrum. Some are drag-and-drop canvases, some are AI-prompted app builders with a live preview. None of the ones listed in public roundups run a headless browser inside the sandbox and make the AI verify its own output there before saying done. That verification loop is the thing this page is about.
Describe a mobile app. Watch the agent build it, then watch the sandbox open it in its own browser at 390 by 844 before it reports done.
Generate my mobile app