For nearly a decade, I’ve treated my home media server like a digital pet. It’s been there through OS updates, hardware migrations, and the slow, creeping realization that “free” is a temporary state of mind in software. Plex was the default for years — reliable, polished, easy. But lately, the price tag has stopped looking like a suggestion and started looking like a toll booth. Features get walled off. The free tier shrinks. It’s the standard SaaS lifecycle: grow, monetize, restrict.
I understand the business logic. Companies need to pay rent. But for the self-hosted community, this shift is an eviction notice. That’s why Jellyfin exists. It’s the open-source answer to the walled garden, and it’s excellent at what it does.
Except for one thing: watching it on a television screen sucks.
The developer landscape for Apple’s platforms — iOS, macOS, iPadOS, and especially tvOS — is unusually hostile to hobbyists. The barrier to entry isn’t just technical; it’s financial and logistical. You need a Mac. You need a developer account. You often need physical Apple hardware to test properly. Compare that to the Android or Linux ecosystems, where you can spin up an emulator on a potato laptop and start coding in five minutes.
The result is a talent gap. Open-source clients for Jellyfin are plentiful on Linux desktops and Android TVs. On tvOS? The options are sparse, often unfinished, or clunky. I looked around for a good client that didn’t feel like it was built by someone who hated remote controls. I couldn’t find one.
So I made Moonberry.
At first, the scope was narrow: I need a better way to browse my movies on my TV. But as I dug into the architecture, the idea expanded. Why stop at video? My server isn’t just movies and shows; it’s also where I keep my personal library of audiobooks and local music files. If I’m building a client that talks to Jellyfin, why not abstract the media type entirely?
The vision shifted from “a better Jellyfin app” to “an aggregated media hub.” A single interface for video, audio, and books. One remote control for my entire digital life.
Here’s the twist: unlike Netflix or YouTube, I have no incentive to keep you in the app if you’re not engaged. Streaming services are optimized for retention. They want you to scroll, hover, and eventually click “Play” on something they recommended. They fight against friction because friction kills watch time.
I don’t care about watch time. I care about retrieval speed.
My goal with Moonberry is the opposite of a streaming homepage. I want the user to find exactly what they want, as fast as possible, and leave the app. The UX challenge becomes: how do you design for exit? How do you make browsing so efficient that the interface disappears?
I’ve borrowed heavily from established streaming UIs — and for good reason. They work because we’ve been conditioned to scan left-to-right: we expect rows of content, and we expect horizontal scrolling to feel deliberate, not jerky. But while I use those patterns, I’m stripping away the noise. No autoplay previews that load slowly on older hardware. No “Because You Watched” algorithms pushing content I don’t own. Just my library, laid out clearly.
It’s ironic, really. I’ve spent hundreds of hours tweaking pixel spacing, optimizing network requests, and debugging focus states on tvOS. And yet, I haven’t actually watched a single movie through Moonberry. The tool is ready; the content consumption is pending. It’s a developer’s purgatory: building the perfect shelf but forgetting to sit in the chair.
The core browsing experience feels solid. The aggregation logic for different media types is coming together. Now comes the hard part: making it feel alive without being intrusive.
Concretely, that means the playback work — HLS variant selection, Dolby Vision device profiles, chapter and skip-intro markers — plus the aggregation layer that makes audiobooks and video feel like citizens of the same library rather than tabs in the same app. The shelf is built. Time to make it worth sitting down for.