Contents
What is Vibe Coding?
Vibe coding is programming at the level of intent. You describe what you want — not how to build it — and a capable AI turns that intent into working software. There are no scaffolding commands, no boilerplate ceremonies, no context-switching into documentation tabs. You stay in the problem domain and the AI stays in the implementation domain.
The phrase was coined by Andrej Karpathy, but the experience it names is hard to appreciate until you try it on a real project. This article documents exactly that: building the interactive SecChats API Demo page from scratch, one natural-language prompt at a time, using Claude as the sole implementer.
The demo page lets a visitor register an account, send a friend request, wait for an acceptance response, and then exchange end-to-end encrypted messages — all wired to the live SecChats JSON API, all inside a single polished HTML file. Here is how it got built.
The Starting Point
The only material given to Claude upfront was the SecChats API documentation — eleven endpoints covering registration, friend requests, messaging, and group chat — and a pointer to the existing site stylesheet. No wireframes. No component sketches. No pseudocode.
What Claude produced in a single response was a fully styled, multi-step demo card with
async fetch calls, disabled-button loading states, error handling for 429
rate-limits, and smooth reveal animations for each result block — all consistent with the existing
glassmorphism design language of the site.
Step 1 — Register
The first step lets the visitor register an ephemeral identity. Clicking Register
fires a POST /register call. On success, the response userId and
authToken surface in the UI and the next step is revealed.
Two quick refinements followed. First, the Friend ID field was pre-filled with
the new userId so testers running a self-send scenario didn't have to copy-paste.
Second, a superfluous cryptoData field was removed from the success display
after a single word of direction:
One sentence each. No further specification needed. Claude located the exact lines, understood the intent, and made surgical edits.
Step 2 — Send a Friend Request
Once registered, the visitor enters a target userId and presses
Connect. This fires a POST /demo_connect carrying a
FriendRequest payload — including a cryptographically random 32-byte
cryptoData value generated in the browser via crypto.getRandomValues().
readonly on the input — is the correct minimal fix.
I also want to keep the visual distinction subtle: readonly inputs should still look
consistent with the rest of the form, so I won't apply any special styling.
Step 3 — Polling for a Response
After the friend request is sent, the client needs to wait for the recipient to accept.
The SecChats API is not push-based — acceptance arrives as a friendResponses
entry in the POST /receive response. Claude wired up a setInterval
poller that fires every three seconds, parses the notification payload, stops itself on
receipt, and displays the response fields.
lastMessageTimestamp cursor so repeated polls don't re-display old data.
The poller should stop itself once a response arrives, and I should also propagate the
timestamp forward so the subsequent message poller starts from the right position.
Scroll behaviour was tuned in two rounds. The first implementation scrolled to the
top of the page. One correction — "Scroll down." — flipped it to
window.scrollTo({ top: document.body.scrollHeight }), pulling the
newly revealed content into view. The same treatment was applied to the register
step with a matching single-sentence prompt.
Step 4 — The Chat Window
The first iteration of the messaging UI was a plain input box and a scrolling list of messages — functional but disconnected from the visual language of the rest of the site. The site already had a floating chat widget used elsewhere as a support bot. One prompt repurposed it entirely:
/demo_postmsg sends, and (5) wiring incoming messages to
the same /receive poller. The widget auto-opens when a friend response
is received. This is a non-trivial refactor but the intent is unambiguous — I should
do it in one pass without asking for clarification.
The result: a polished floating chat panel that appears in line below the three setup steps, pre-labelled with the connected friend's ID, ready to send and receive messages.
Sent and received messages were then labelled explicitly with another one-liner, and the polling strategy was tightened so the client stops polling on receipt and resumes only after the user sends a new message — avoiding unnecessary server load while the conversation is idle.
Refinements: The Back-and-Forth
A recurring theme in vibe coding sessions is that the first output is rarely the last. But the refinement loop is radically compressed compared to traditional development. Here is the complete set of prompts that shaped the final demo, start to finish:
- Create demo.html with Register → Connect flow
- Pre-fill Friend ID with userId
- Remove cryptoData from success display
- Poll /receive after Connect; display friend response
- Scroll down on receiving data
- Scroll down on register as well
- Make the Friend ID field read-only
- Add Send Message input and poll for incoming messages
- Make the send call async (fire-and-forget)
- Replace steps 4 & 5 with the floating chat widget
- Prefix messages with "Sent: " / "Received: "
- Move chat widget into the vertical step flow
- Stop polling on receive; resume on send
Thirteen prompts. Most of them a single sentence. The entire demo — HTML, CSS, and JavaScript — was built and refined in one sitting without writing a single line of code by hand.
The friction that usually separates a good idea from a working prototype has essentially vanished.
What Vibe Coding Feels Like
There is a qualitative shift that happens when the cognitive overhead of implementation drops to near zero. You stop thinking in terms of syntax and APIs and start thinking purely in terms of product behaviour. Every prompt is a design decision. Every response is a deployable artifact.
Claude is not just a code generator. It reasons about tradeoffs. When asked
to make the send call asynchronous, it understood the user experience rationale
— the UI should feel instant — and chose fire-and-forget semantics
with a .then() callback to start polling after delivery, rather
than simply wrapping the call in an async function that still
blocks the UI. When asked to reuse the chat widget, it identified all five
interconnected changes needed and executed them in one pass.
What emerges from a vibe coding session is not a pile of generated snippets stitched together by a human. It is coherent, idiomatic code that reflects a consistent set of design decisions — because the same model held the full context of every previous decision throughout the session.
The SecChats demo was built this way. You can try it live at demo.html.
All prompts in this article are verbatim from the session that produced the demo. No post-hoc editing was applied to the code.