Sample News Network
Evening briefing — Tuesday
Project / 02
A phone-sized YouTube remote for an Apple TV, built for one person who found the TV's own interface unusable.
Home
Sample News Network
Evening briefing — Tuesday
Sample Debates
Panel: should cities build more housing?
Sample News Network
Live: budget hearing
Sample Science
How tide gauges actually work
Sample Debates
Interview: the case against speed limits
Sample Science
Why bridges hum in the wind
Snapshot
Section
The YouTube app on a television is hard to use if you are not comfortable with a directional remote. Searching means pecking at an on-screen keyboard, the home screen is a wall of recommendations, and finding the channel you watch every night takes a dozen clicks.
TV Guide replaces the remote. It is a web app you open on a phone — installable to the home screen, where it behaves like a native app — showing the channels you actually subscribe to as a scrollable list of large thumbnails. Tap a video and it starts on the TV. A now-playing bar handles play and pause, skipping, seeking and chapter jumps.
It was built for one specific person: a parent who watches long-form news and debate on an Apple TV. That single-user brief drove every decision — 48-pixel minimum touch targets, 18-pixel minimum type, high-contrast dark mode, no nested modals, no horizontal scrolling, every screen usable one-handed.
Section
Neither half of the app can run in the browser. The Data API key would be exposed in the bundle, and YouTube's Lounge endpoints send no CORS headers, so browser requests to them are blocked outright. Everything therefore goes through the app's own server-side route handlers running as Vercel functions: the phone only ever talks to /api on its own origin, and the secrets never leave the server.
Casting rides the same undocumented protocol YouTube's own mobile app uses. The television shows a pairing code; the server exchanges it for a durable screen ID, that for a lounge token, and commands — set playlist, add video, play, pause, next, seek — are posted into a bind session on Google's BrowserChannel wire protocol.
Three pages sit behind a bottom tab bar: Home, which merges recent uploads across every subscription; Your Channels, a large dropdown of subscribed channels; and Queued, a session-only mirror of what has been lined up. The feed is the signed-in user's real subscription list, not a recommendation algorithm.
Section
The offset counter is the whole ballgame. Every message posted into a bind session carries a running count of messages already sent. Get it wrong and the server does not return an error — it returns 200 OK and silently drops the message, so the television simply does not respond. The session counters are threaded through every round trip and mirrored into local storage, because serverless functions keep no memory between invocations.
Failures escalate in tiers rather than dumping the user back to a pairing screen: retry on a fresh bind session, then re-mint the token from the stored screen ID, and only if that also fails clear storage and ask for a new TV code. The earlier build made you re-pair constantly.
The status reader is deliberately GET-only. It never posts, so it can never touch the offset counter the commands depend on — the part that reads state structurally cannot break the part that sends commands.
Reading status returns three results, not two: now playing, stopped, and no update. No update means nothing was caught this probe, which is the normal result during uninterrupted playback, and the interface has to retain its last known state. Treating it as nothing playing is the flicker bug that took longest to kill.
Subscriptions rather than recommendations, partly by necessity: no public API exposes the recommendation feed any more, and the only route to it is scraping an internal API with logged-in cookies, which violates the terms of service and risks the account. Recent uploads from channels you chose to follow turned out to suit the use case better anyway.
Security and quota shaped the rest. The refresh token lives only in an AES-256-GCM encrypted, http-only cookie — never in local storage, never in a public environment variable. Authenticated API calls are forced past Next's shared response cache so private data cannot enter it. Uploads are fetched through a channel's uploads playlist at one quota unit rather than search at a hundred, against a daily allowance of ten thousand.
Section
The hardest-won knowledge in the app came from tracing YouTube's Lounge back channel against a real Apple TV rather than assuming how it behaves.
It is not a stream: the server sends one short batch and closes the connection in about half a second, even mid-playback, so the reader makes bounded quick reconnects inside an eight-second budget. Between events it sends only heartbeats. And steady playback emits no events at all — a twenty-second trace during untouched playback caught fifteen reconnects and zero playback events. The television speaks only on transitions: play, pause, seek, load, stop.
Because position only arrives at those transitions, the playhead is extrapolated locally: the television supplies an anchor, the client ticks a timer between anchors and re-anchors on the next event or user command. That is what makes the scrubber and the ten-second skips feel immediate.
Section
Deployed on Vercel and in use — the live app is linked below, though it needs a Google sign-in and a paired television to do anything, which is what the demo at the top of this page stands in for. Pairing is one-time: enter the code from the television once and the session persists across reloads.
All Lounge logic is confined to two directories, because the protocol is unofficial and can break without warning — it stays fixable in one place and never leaks into the interface components.
Links
Next project