It's been a rough week, not gonna lie. I spent three days debugging a Chrome extension update that Google's review team flagged for "requesting unnecessary permissions" — turns out it was a single leftover activeTab scope I forgot to remove after a refactor two versions ago. Three days, one line.
On top of that, a user emailed me asking why their FTP deploy "silently did nothing," and it took me an embarrassingly long time to realize their hosting provider was rate-limiting anonymous connections from datacenter IPs — not something I'd ever hit testing locally.
Anyway. Not the week I planned. But it did remind me of something I've been meaning to write about.
Every "I built a website with AI" demo skips the same 10 seconds
You've seen the demo a hundred times. Someone opens ChatGPT or Claude, types a prompt, a beautiful landing page appears... and then the video just ends. Cut to a live URL. Cut to "look, it's deployed!"
Nobody shows what happens in between.
That gap gets treated like it's the hard part — like there's some serious infrastructure decision happening off-screen. Framework choice. Server config. DevOps knowledge you're supposed to already have.
There isn't. That gap is maybe 80 lines of very boring code.
What's actually happening in that gap
Strip away the marketing and "AI-generated site → live URL" is three unglamorous steps:
Step 1: Find the HTML.
The AI's response is just text on a page. If it's ChatGPT, Claude, or Gemini, that text is sitting in a <pre><code> block in the DOM, updating as it streams in.
const observer = new MutationObserver(() => {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(scanForHtmlBlocks, 600);
});
observer.observe(document.body, { childList: true, subtree: true });
function scanForHtmlBlocks() {
return [...document.querySelectorAll('pre code')]
.filter(el => /<!DOCTYPE|<html/i.test(el.textContent));
}
That's it. No parsing library. You're just waiting for the DOM to stop mutating, then checking if a code block's text looks like an HTML document.
Step 2: Ask a hosting API to put it somewhere.
Netlify, Vercel, and GitHub Pages all expose plain HTTP APIs for exactly this. A deploy is, at its core, a POST request with your file attached and a token in the header. There's no magic — it's the same shape of request you'd write to hit any REST API.
await fetch('https://api.netlify.com/api/v1/sites', {
method: 'POST',
headers: { Authorization: `Bearer ${token}` },
body: zipBlobOf(html)
});
Step 3: Get a URL back.
The API responds with a live URL. That's the "magic" part of the demo — it's a JSON field.
That's genuinely the whole gap. Nobody shows it because there's nothing to show — it's a fetch call, not a feature.
Where it actually gets annoying
Here's the part that isn't trivial, and it's not the part anyone talks about:
- The DOM you're watching belongs to someone else's frontend, and it changes without warning. ChatGPT, Claude, and Gemini all ship UI updates constantly, and none of them owe you a stable class name. Matching structurally (
<!DOCTYPE,<html>) instead of by CSS class is the only thing that's survived more than a few weeks for me. - A single AI response can contain more than one code block — the full page, plus a CSS tweak explained separately. Grabbing "the first
<pre>tag" picks the wrong one more often than you'd expect. - The moment you preview untrusted HTML before deploying it, you're rendering someone else's
<script>tags. Aniframewith a locked-downsandboxattribute is doing more real work in this whole pipeline than the deploy call itself. - And the question people actually ask first isn't "which host do you support" — it's "where does my FTP password go." That one shapes the architecture more than anything else on this list.
None of this needs a framework. It needs a MutationObserver, a fetch call, and paying attention to the two or three things above that actually bite you in production.
Why I'm telling you this
I ended up building all of the above into a Chrome extension called HTML Deployer — it sits on top of ChatGPT/Claude/Gemini, does the detection + preview + deploy steps above, and lets you push to Netlify, Vercel, GitHub Pages, FTP, or your own self-hosted endpoint, with a ZIP export if you'd rather do it manually.
But I'd rather you walk away from this post knowing how the gap works than just knowing the tool exists. If you've built something similar and hit a different wall than the ones above, I'd genuinely like to hear about it in the comments.
Top comments (0)