DEV Community

Cover image for One Backend, Every Screen: I Built a Cross-Platform Audio App the 'Wrong' Way
Kornel Maraz
Kornel Maraz

Posted on AI-assisted

One Backend, Every Screen: I Built a Cross-Platform Audio App the 'Wrong' Way

Every "build in public" thread and every YouTube growth guru tells you the same thing: pick the smallest possible surface, ship the lowest-effort thing that gets the biggest effect, validate, iterate. It's good advice. For this project I did almost the exact opposite — on purpose — and I want to be honest about why, and what it actually cost.

I'm building own.audio: a private home for the audiobooks, music, and podcasts a family already owns, playable on every device they actually use, with a real management story for the one person in the house who ends up being the admin. That product — every device, plus admin — is the reason the low-effort path was the wrong path. This is the long way round, and I'd take it again.

The false start: a web app for a thing nobody uses on the web

I started where a web developer starts: a backend in Rust with Axum, and a React front end on top of it. Clean, familiar, fast to move.

Then it hit me that I was building the wrong surface. This whole product lives on phones and speakers — music and audiobooks get consumed on a mobile device, or at least controlled from one while they play on some speaker across the room. The web app was the thing I knew how to build, not the thing people would actually live in. And as I kept pushing features into the backend, the React front end just fell further and further behind. It was a tax I was paying for a surface that wasn't the point.

The sprawl trap

So I tried to do the sensible unified thing: one app that handled books,
podcasts, and music together. But keeping that coherent turned into a mess — the backend started sprouting separate combinations for books, separate for podcasts, separate for music, and the whole thing got bushy. Too many branches, too much special-casing, no clean spine. That wasn't the way either. I could feel it turning into the kind of codebase you dread opening.

That's the moment I made the call that shaped everything after: do the
thorough thing, not the clever-minimal thing
— because a family audio product that has to work on every screen and give the household admin real control doesn't have a lowest-effort version that's actually good.

Mac first — the admin's control room

I started with the Mac app, and starting there was deliberate. It's the control room for the person who manages the family's library: a place to play everything — music, audiobooks, playlists, all of it — and, crucially, to manage it.

That management story is where the interesting work was. The Mac app is also a bridge to the self-hosted world: it connects to Navidrome and Audiobookshelf — play from them, download from them, and upload into the cloud — so someone who has been running their own server, and is tired of it, has a real path to move their collection over instead of starting from zero.

The hard part: the sync client

The centerpiece of the Mac app, and the single hardest thing I've built for this project, is the sync client. The home admin can sync the entire collection on their own terms, and "on their own terms" is doing a lot of work in that sentence. It meant:

  • Bandwidth throttling, so syncing a large library doesn't strangle the household's connection.
  • Resumable uploads, so a transfer picks up where it left off instead of starting over.
  • Robustness across reconnects — the whole thing hardened so that when the connection drops and comes back, it recovers and keeps going rather than giving up.

On top of transfer, it had to understand the files: metadata lookup for music, identification for audiobooks, artist images pulled from Wikipedia (with proper sourcing), and finally the AI pieces — narrating a book, or translating a podcast episode into a narrated version. None of that is glamorous, and all of it is the difference between "a folder of files" and "a library."

Here's the honest bit: sync was miserable until it wasn't. Once it actually worked — reliably, resumably, politely — everything after it was a joy. The hard foundation is what made the rest fun.

Why backend-first paid off

I didn't touch iOS until the Mac client was done and every feature — including full offline playback — worked against the Rust/Axum backend. That patience was the whole point. Because when I finally started the iPhone apps, I could reuse the shared backend code I'd already written for the Mac app. The iOS apps ride on the same backend brain. The thorough, slow foundation turned the second platform from "build it again" into "put a native face on what already works."

One brain, different faces — on purpose

This is the design decision I'm most sure about: the shared backend is one brain, but each app wears a different face, and it should.
You don't listen to music the way you listen to an audiobook, so the apps don't pretend you do. I ended up building two entirely different kinds of player: one for podcasts and audiobooks, and a separate one for music — because music needs FLAC and lossless playback, an advanced equalizer, and crossfade that mixes the last five seconds of one track into the first five of the next. That's not a skin on the same player; it's a different player. Across the three audio apps — books, podcasts, music — the interface genuinely differs, and only the brand ties them together.

That's the opposite of forcing one uniform UI everywhere, and I think it's the honest choice: consistent identity, deliberately native and content-appropriate interfaces.

iPad and iOS: where the platform gives back

Extending to iPad was, thanks to SwiftUI and native components, mostly a matter of fine-tuning details rather than rebuilding. And the advanced testing and automation you get in the Swift world genuinely make this a pleasure — for a developer, it's a treat. Apple's platform gives back the effort you put into learning it.

Android: everything works, and I don't love it

Then Android. And I'll be candid, because pretending otherwise helps no one: the fragmentation — of the libraries and of the language itself — is its own extra adventure. Building and compiling feel, to me, a couple of generations behind iOS. Everything can be done, it just takes longer, and I don't enjoy it as much.

Keeping the design consistent with iOS using standard libraries is genuinely hard, and I refused to go into exotic-library territory to force it. So the Android UI ends up different from the iOS one. I don't love that either — but I believe Android users will appreciate what they get in exchange: reliability, native libraries, and consistency with the other Android apps already on their phone. Fighting the platform to match iOS pixel-for-pixel would have served my sense of tidiness, not the person holding the phone.

The chapter that wrote itself: cars and voice assistants

CarPlay and Android Auto deserve their own chapter, except the chapter is short: Apple and Google have caught this so beautifully that the developer does almost nothing. The same goes for Siri and Gemini. Every so often the platform just hands you a hard problem, already solved. Good job, both of you — genuinely.

And then the web app was easy

Having built all of that, I came back around to a React app on the Rust
backend
— and after everything else, it was basically child's play. The surface I started with, and abandoned as the wrong first move, became the easy last one once the brain behind it was solid.

That's where I stand now. The plan is to get it into production before the end of the year.

Would I do it the "wrong" way again?

Yes. For a lot of products, the guru advice is right — find the smallest surface, ship it, learn. But when the product's whole promise is "your audio, on every screen your family uses, managed properly," there isn't a lowest-effort version of that promise worth shipping. The thorough path — one shared Rust backend, a Mac control room first, a hard-won sync client, and native, deliberately different faces on every platform — wasn't the efficient way to a demo. It was the only honest way to the actual product.


I'm building own.audio solo — a private home for the audiobooks, music, and podcasts you actually own. It's pre-launch; I'm aiming to ship before year-end, and writing the build up as I go.

Top comments (0)