The fully hallucinated operating system
Recreating VibeOS
VibeOS is a satirical proof-of-concept demoed at Microsoft Build 2026 by Steve Sanderson, the Microsoft engineer known for Blazor.
There is no application code at all. The AI hallucinates the entire UI and behaviour in real time. This page is a recreation built with only Anthropic Claude and caffeinum/vibe-os.
How the recreation works
Sanderson's original ran a real Alpine Linux image in Hyper-V with the GitHub Copilot SDK. This version keeps the idea — no app code; the model draws every app's UI live — but uses a much lighter web stack.
- 1
The kernel is one API route
A single Next.js route, POST /api/hallucinate, asks Claude (claude-sonnet-4-5 via the Anthropic SDK) to act as the "rendering kernel" and stream back one complete HTML document — and nothing else.
- 2
Each window is a sandboxed iframe
The streamed HTML is dropped into a sandboxed iframe, painting progressively as it arrives. The dreamed-up app can't touch the page around it.
- 3
A bridge fakes the behaviour
Claude tags every button and field. Once a frame finishes streaming, a small injected script captures clicks and typing and reports them back to the host.
- 4
Re-dream the next frame
That action is added to the window's history, and the route is called again with the full history. Claude renders the resulting (sometimes wrong) state.
The loop, step by step
- 1You launch an app
- 2POST /api/hallucinate asks Claude for the next frame
- 3The streamed HTML renders in a sandboxed iframe
- 4A bridge captures what you clicked or typed
- ↺That action is added to the history — then it loops back
What it's made of
- Next.js 15 + React 19 on the Bun runtime, in Docker behind Traefik
- Anthropic SDK with a single ANTHROPIC_API_KEY — no Copilot SDK, no VHDX image
- The concept (live-rendered AI apps) borrowed from caffeinum/vibe-os
- A ⌘K launcher and a dock where every icon hallucinates its app
How to roll your own
Don't be fooled by how short the Docker setup is — that's the trivial part. The real work is a handful of files that turn Claude into a window manager. Here are the pieces you actually have to build.
- 1
The kernel — one streaming API route
This is the heart of it, and the hardest part to get right. You prompt Claude to BE the operating system: output one complete HTML document and nothing else, tag every control with data-vibe, and reflect the interaction history in the rendered state. Then stream the text deltas straight to the browser.
src/app/api/hallucinate/route.ts
const SYSTEM_PROMPT = `You are the rendering kernel of "VibeOS". Output ONE complete, self-contained HTML document and NOTHING else. Put a data-vibe attribute on EVERY interactive element. Reflect the interaction history in the rendered state.`; export async function POST(req: Request) { const { app, history } = await req.json(); const stream = anthropic.messages.stream({ model: "claude-sonnet-4-5", max_tokens: 8000, system: SYSTEM_PROMPT, messages: [{ role: "user", content: buildPrompt(app, history) }], }); // pipe Claude's text deltas to the browser as the HTML frame } - 2
The window — a sandboxed iframe + an injected bridge
The dreamed HTML has no real logic, so you fake interactivity. Render the frame into a sandboxed iframe, then inject a tiny script that turns every data-vibe click into a postMessage back to the host. The nonce stops other frames from talking to you.
src/components/hallucinated-window.tsx
<iframe sandbox="allow-scripts allow-forms" srcDoc={dreamedHtml + bridgeScript} /> // bridgeScript, injected into every dreamed frame: document.addEventListener("click", (e) => { const el = e.target.closest("[data-vibe]"); if (el) parent.postMessage( { __vibe: NONCE, kind: "action", desc: el.dataset.vibe }, "*" ); }); - 3
The loop — feed each action back as history
When an action arrives, append it to this window's history and dream the next frame. The whole "state" of every app is just a growing list of plain-English actions you keep handing back to Claude. Dreams upon dreams.
src/components/hallucinated-window.tsx
window.addEventListener("message", (e) => { if (e.data.__vibe !== NONCE) return; setHistory((prev) => { const next = [...prev, e.data.desc]; dream(next); // POST /api/hallucinate again return next; }); }); - 4
The shell — a desktop, a dock, and a ⌘K launcher
Around the engine you build a real window manager: a macOS-style desktop, draggable/resizable windows, z-index focus, and a Spotlight launcher. Every dock icon and launcher entry just spawns another hallucinated window — nothing runs real code.
src/app/demo/page.tsx + hallucination-layer.tsx
// ⌘K opens the launcher; dock icons hallucinate instead of run useHotkey("mod+k", () => setLauncherOpen(true)); launchHallucinatedApp("Calculator"); // real app launchHallucinatedApp("Encarta '98"); // real-but-fake launchHallucinatedApp("Dating app for houseplants"); // invented - 5
Only now: the easy part (env + Docker)
Once the app exists, packaging it is trivial. Add your Anthropic key, build the Bun image, and run it. This is maybe 10 minutes of the whole effort.
.env + Dockerfile
cp .env-example .env # ANTHROPIC_API_KEY=sk-ant-... docker compose -f docker-compose.local.yml up --build # open http://localhost:3000/demo
That's the catch: there's no real source repo to clone (github.com/caffeinum/vibe-os is a 404). To reproduce the image, you rebuild these files yourself — the engine, the iframe bridge, the dreaming loop, and a full desktop shell — then wrap them in the Dockerfile above.