No-account entry
A shared link should open directly into a room with the composer ready.
This walkthrough is simulated locally and does not send data.
Send the prepared message
PROBLEM
The design problem was not simply “make a chat app.” It was balancing instant participation with explicit privacy and moderation boundaries.
A shared link should open directly into a room with the composer ready.
Moderation authority should stay explicit without broadening access beyond the scoped evidence required for review.
The durable write defines the record before realtime delivery accelerates it.
Remove account friction, not product boundaries.
FROM PERSONAL ROOM TO PLATFORM
yap. began as a single-channel personal page built with Vanilla JS and Supabase. Rather than porting that implementation, I used its behavior as a specification and redesigned the product as a multi-tenant platform on Next.js, Cloudflare Workers, D1, Durable Objects, and R2.
This required explicit channel ownership, separate anonymous and authenticated identities, platform-admin authority, media lifecycle rules, and operational monitoring.
I later selectively migrated the original production channel, preserving its history while keeping legacy anonymous identities pseudonymous and unlinked. Reply and gallery references, along with record counts, were verified before and after the move.
Vanilla JS and Supabase established the product behavior.
Anonymous, authenticated, owner, and admin authority became separate states.
References and record counts were checked before and after migration.
Migration integrity checks
KEY PRODUCT SOLUTIONS
Hosts create a channel and share its URL. Guests can enter public rooms immediately, while optional passcodes add friction only when the conversation needs it.
Open the room and join instantly without signup.
Owners can shape entry, pacing, and message rules without leaving the conversation. Passcodes, chat freezes, and word filters change the room immediately.
A visitor can message the channel owner privately without leaving the room. The sender and owner see the same exchange; other visitors continue to see only public messages.
A host can open a separate live session for a shared moment, then end it cleanly. Live messages and reactions disappear while the normal room remains.
SYSTEM DESIGN
Next.js owns the web experience and session boundary. The Worker independently authorizes API access, D1 stores durable records, Durable Objects coordinate room presence and delivery, and R2 stores protected media.
Authoritative writes commit to D1 before realtime fan-out. Public messages and authorized private threads can share one ordered timeline without widening DM visibility, while media access remains separately signed and scoped.
Hover, focus, or click to trace
↩ Realtime fan-out to Browser
D1 defines the durable conversation record; Durable Objects accelerate delivery, while R2 stores protected media behind scoped access.
PLATFORM OPERATIONS
Beyond room-level moderation, yap. needed an operational layer for monitoring service health, triaging reports, and handling support. This dashboard brought incident awareness, queue management, and escalated user issues into one place.
| Channel owner | Platform admin |
|---|---|
| Room-level moderation | Escalated abuse |
| Local notices | Global notices |
| Channel restrictions | Platform restrictions |
The admin surface connected incident awareness, queue management, escalated reports, and support handling in one operational view.

Platform-wide health, failures, slow paths, and alert status.
Problem routes surface where errors and slow responses are accumulating.
Open, unread, stale, and reply-needed work is summarized for fast prioritization.
Abuse reports move beyond room-owner handling into platform review.
One-to-one user issues are handled in the same operational surface.
INCIDENTS THAT CHANGED THE ARCHITECTURE
~80 ms is a limited post-cutover Worker sample, not a long-term percentile.
PRODUCTION EVIDENCE
COUNTING NOTEScope: public conversations and private DMs. Created-message totals include messages later deleted by users.
CALCULATION NOTEMedian and adjusted average use the September 5–11, 2026 (UTC) window; the adjusted average excludes the largest event-driven spike.
LISTENING
yap. asks for feedback on each user's 10th visit — late enough for the response to be grounded in real experience. I review what returning users report, identify recurring themes, and ship changes from that evidence instead of guessing at improvements.
| User feedback | What I changed |
|---|---|
| New messages pulled me back down while I was scrolling up to read earlier messages. | Held the reader’s position until they return to the bottom, alongside anchor-based scroll correction for long histories. |
| Users wanted notifications so they could keep up with new activity without continuously watching the chat. | Added notifications for new chat activity. |
| Users wanted clearer time context when reading and returning to conversations. | Added timestamps to chat messages. |
| The interface felt visually tiring: the colors were hard on the eyes, text felt large, and messages felt cramped. | Refined the interface as one system with a softer palette, a calmer type scale, and more space between messages. |
| User feedback | What I changed |
|---|---|
| Rapid repeat taps and retries after a lost server response could create duplicate messages during slow or interrupted connections. | Combined stable send IDs, idempotent retries, database duplicate prevention, and persistence-based UI updates so one send attempt converges on one stored message—even after reconnecting, re-entering the channel, or losing the original response. |
TAKEAWAYS
yap. was the first product that pushed me past interface design and into the realities of running a system: durable state, realtime delivery, moderation, support, route health, and operator tooling. The biggest shift was realizing that product design does not stop at the user flow. It extends into how the product fails, recovers, and gets operated.
Once messages, DMs, reactions, live state, and moderation actions had to survive reconnects, retries, and partial failure, I stopped treating realtime delivery as the system itself. yap. taught me to separate transport from the durable record and design the interface around recoverable sync rather than perfect immediacy.
Once room entry, messaging, previews, authentication, and moderation were live, failures stopped being abstract engineering problems and became user experience problems. Building yap. pushed me to think in route-level health, alerting, queue triage, and reversible incident response, not just feature completion.
Anonymous chat created edge cases around freezing rooms, passcodes, banned words, reports, deletion, and appeals. yap. taught me that enforcement should not be hidden operator intervention. It works better when actions are legible, stateful, and reversible where appropriate for both room owners and platform operators.