hold real-world events • Supports planning, ticketing, day-of operations, and settlement • First event space: KAIWAI Hatsudai (a bar in Tokyo) • Designed for small events of 5–20 attendees • Talents perform remotely and appear on the in-store screens • Attendees on site can also talk with the talents
remote talents and the bar via Discord • On unstable networks, quality dropped and never recovered • Every reconnect exposed the Discord UI on screen • The UI and cursor couldn't be hidden; fullscreen was awkward • Many screens and devices were hard to manage and operate ▸ So we built our own app
talents and the bar • Multi-party calls and streaming to any screen • Balances quality and latency to fit the content • All on-site devices are operated remotely
SESSION Talents and support staff on a call Cloudflare Relay OBS BRIDGE Receives and relays OBS streams SATELLITE In-store devices Sends camera & mic, shows video
is free during the beta ◦ • Costs stay the same with more screens, even with a rough implementation The infrastructure to manage got simpler (at least for me) ◦ Before: [Containers] Next.js + Redis + LiveKit + LiveKit Ingress ◦ After: [Serverless] Hono + DO + Cloudflare MoQ Relay + moq-relay ▪ • * Only moq-relay runs on Linode Delivery routes can be optimized ◦ Runs on Cloudflare's network ◦ Maybe cluster the on-site devices? (not tried yet)
control delivery quality to some extent ◦ Latency: 50–400 ms ◦ Resolution: 720p–2160p ◦ Bandwidth: up to 10 Mbps Debugging got easier ◦ Stats and logs are streamed over MoQ and checked in the console ◦ Remotely monitor on-site latency during live events
sync ◦ Our own receiver (replacing @moq/watch, not built for calls) broke A/V sync ▸ Implemented buffering that syncs video to audio ▸ Visualized latency to pinpoint the causes • Latency causes audio noise ◦ Frequent click noise during calls ▸ Implemented WSOLA (waveform similarity based overlap-add), inspired by NetEQ ▸ Stretches and shrinks the audio ▸ Shrinks gently, since we also play music
Cloudflare speaks draft-16 • @moq (npm) + moq-relay just worked, but we hit several problems with Cloudflare's draft-16 relay ◦ Couldn't receive the broadcast list (announce) properly ▸ Centralized the list in a DO ◦ Cloudflare's relay has MAX_REQUEST_ID=100 ▪ After a while in a call, you can no longer see others' video ▪ MAX_REQUEST_ID was removed in draft-17 ▸ Manage connection generations; subscribe on a new connection when IDs run out ◦ Tiny OBS audio groups arriving together got dropped, cutting out audio ▸ BRIDGE bundles audio into one group per interval
◦ • • Video shows for a moment, then freezes. No conversation possible WebKit Bugzilla Bug 319818 ◦ [WebTransport] Flow control never refills ◦ https://bugs.webkit.org/show_bug.cgi?id=319818 Can't connect from Android with AdGuard installed ◦ Either way, some environments can't use WebTransport ▸ Also run moq-relay on BRIDGE and use QMux (for now)
for virtual talents' real-world events, on MoQ • Cloudflare's MoQ relay cut cost and infrastructure, and gave us control over quality • In exchange, we built playback (sync, jitter) ourselves and work around draft differences and unreachable networks • Until MoQ is standardized, we'll keep chasing the drafts • Claude Code (mostly) takes care of it