Skip to content
Back to Articles
ProjectsSystem Design

What Modern Music Apps Get Wrong

Most music apps get the visible ten percent (browse screens, now-playing UI) right, and the invisible ninety percent (playback state staying coherent) wrong. Building Armonia, my own privacy-focused music app, is where I actually learned why.

Apr 15, 202610 min readPawan Choudhary

Most music apps get the visible ten percent right and the invisible ninety percent wrong. The visible part is a browse screen, a now-playing view, some album art, genuinely easy to make look good. The invisible part is whether what's playing, where you are in the track, and what's queued next stays coherent while you actually use your phone: switching screens, backgrounding the app, taking a call. Listeners feel that failure even when they can't name it. It just registers as “this app feels a little broken.”

That word (broken) is doing a lot of work, because it's rarely one dramatic bug. It's usually a dozen small inconsistencies that individually seem minor: the seek bar jumps a little, the mini-player briefly shows the wrong track, resuming after a call takes a beat longer than it should. None of them alone would get a one-star review. Together, they're the entire difference between an app that feels considered and one that feels assembled. Building Armonia, my own privacy-focused music app, is where I actually learned that the hard way, not by reading about it, but by shipping it and running into every one of these problems myself.

Why I Started Armonia

The reason was simple and a little personal. Every music app I used locked essential features behind a subscription, and it bothered me more than it probably should have. I wanted something that felt premium without charging for the basics, an app built around the idea that music should be accessible to everyone, not gated a feature at a time behind a paywall. Some of that frustration was my own. Some of it came from watching friends and family run into the same walls, just wanting to enjoy music without the app getting in the way.

That's the whole premise Armonia is built around, and it's also the reason privacy ended up being a structural decision rather than a policy page nobody reads. If the app is supposed to respect people using it, that has to show up in how it's built, not just in what it promises.

Why Flutter

Flutter was a deliberate choice, not a default. A single codebase covering the app's target platforms meant I wasn't splitting engineering effort across separate native builds before I'd even proven out the hard parts of the app: playback reliability, state consistency, authentication. Choosing a framework that let me focus on those problems once, instead of solving them twice in two different languages, was worth more to me early on than anything native tooling would have offered.

The Part Everyone Notices, The Part That Actually Matters

Design a now-playing screen and you'll have something to show within a day. Keep playback state consistent across every screen and every interruption the operating system can throw at an app, and you're looking at the actual hard problem in a music app, the one that doesn't screenshot well and rarely gets a design review, but is the entire reason the app feels solid or doesn't. I learned that distinction the slow way, by underestimating it.

This is also why so many first music-app projects (including plenty of tutorials) quietly cheat: they only ever demo the happy path, one screen, one uninterrupted session, phone never backgrounded. That's not dishonest so much as it's avoiding the one part of the problem that's actually hard, which is a very human thing to do and a very bad thing to let slide if the goal is a genuinely solid app rather than a good demo.

It's worth being honest about why this imbalance exists: the visible ten percent is what gets built first, shown first, and praised first. A friend, a reviewer, anyone trying the app for the first time looks at the now-playing screen and reacts to it immediately. Nobody opens an app and says “impressive state management.” That asymmetry in feedback is exactly why the invisible part keeps losing the argument for engineering time, on this kind of project and most others like it.

Building Privacy In From the Start

Early on, Armonia stored user information locally on the device for every user, signed in or not. During testing, I found cases where local user state could persist incorrectly across sessions, data from one user context bleeding into another simply because everything lived in the same local store regardless of who was actually using the app at the time. It wasn't catastrophic, but it was exactly the kind of quiet inconsistency that erodes trust in an app that's supposed to be about privacy in the first place.

The fix was architectural, not a patch on top of the existing structure. Guest users stay fully local now, with nothing to synchronize and nothing to leak between sessions. Authenticated users have their data synchronized through Supabase instead of local storage alone. That split is now a core part of how the app is structured, not an exception handled after the fact, and it's the clearest example in the whole project of a bug actually changing the architecture rather than just getting patched around.

Google Sign-In and the Week I Didn't Plan For

I expected Google Sign-In to be one of the easier parts of the app. It wasn't. Getting the correct SHA certificate configuration in place for a real Google Play release, not just a local debug build, consumed close to a full week on its own. Matching the right signing certificate to the right configuration across the Google Cloud and Play Console setup ended up being one of the most frustrating parts of getting Armonia to release, more so than any of the actual application code I'd written.

There's no clever lesson in that one beyond the obvious: the parts of a release that feel like they should take an afternoon are sometimes the parts that eat a week, and platform configuration is very good at hiding exactly where in the chain something's misconfigured.

Why Playback State Breaks Music Apps

The specific ways this breaks are almost always the same: navigate to a different screen and the track position looks wrong when you come back. A phone call interrupts playback and it doesn't resume the way it should. The queue on one screen quietly disagrees with the queue on another. None of these are exotic bugs, they're the predictable result of playback state living in more than one place at once, each screen keeping its own opinion about what's actually happening. I hit versions of all three before playback felt genuinely stable.

The pattern underneath all three examples is the same: state duplication. The moment two different parts of an app each hold their own copy of “what's currently playing,” they will eventually disagree, not because anyone made a mistake, but because keeping two copies of the same fact synchronized forever is a harder promise than it looks like when you first write the second copy.

Getting Playback to Actually Feel Reliable

Playback reliability took several iterations before it held up. Keeping media state consistent everywhere in the app, across the mini player, the full player, and background playback, took significantly longer to get right than I originally expected going in. A lot of that time went into making sure the app's idea of what was currently playing stayed correct no matter where in the app someone navigated, rather than assuming a single screen would always own that state.

Why the architecture came before the polish

This is the reasoning behind building Armonia's state-management approach as its own deliberate piece of the app, rather than something that happened to fall out of whichever screen got built first. Playback state is the one thing every screen needs to agree on, and giving it a single source of truth early is what made the later iterations of the player actually converge instead of chasing a new inconsistency every time.

The hardest problem in this app was never drawing the now-playing screen. It was keeping playback state coherent as the user navigates, backgrounds the app, or gets interrupted, and that's the actual reason getting there took as many passes as it did. It's the layer everything else depends on, and it doesn't forgive being gotten only mostly right.

A single continuous waveform passing unbroken through three overlapping translucent frames
What every screen in a music app has to agree on, whether or not it's designed to.

Redesigning Navigation More Than Once

Navigation went through more redesigns than any other single part of the app, which surprised me at the time. Designing something that felt simple while still being able to scale with features I hadn't built yet took several attempts before I landed on the current structure. A layout that works cleanly for today's feature set can fall apart the moment a new feature needs to fit into it, and I rebuilt Armonia's navigation more than once after realizing the current structure had quietly become the thing constraining what I could add next.

What Retrofitting This Later Would Have Cost

It's worth spelling out exactly why getting this ordering right mattered so much, because “architecture first” can sound like generic engineering advice rather than a specific decision with a specific cost attached. If a now-playing screen gets built first and reads playback state directly off whatever's convenient at the time, that screen's assumptions become load-bearing. The next screen that needs the same state either duplicates those assumptions or reaches back into the first screen's code to borrow them, and either path quietly wires playback logic into places it was never supposed to live.

Unwinding that later isn't a refactor, it's closer to a rewrite of everything that touches playback, which is most of the app, in a music player. Paying that cost up front, before there was anything to show for it, turned out to be cheaper than the alternative would have been once three or four screens had each grown their own opinion about the same piece of state.

How My Expectations Changed

I started Armonia with an optimistic timeline. I genuinely believed that with AI-assisted development and my own programming background, I could get the whole app built in a matter of weeks. That estimate didn't survive contact with the real problems. Local storage contamination, Google Sign-In's certificate requirements, playback reliability, and navigation design each took far longer than planned, often in ways that weren't obvious until testing actually surfaced them.

What actually got Armonia to a production-ready release on Google Play was months of debugging, redesigning, testing, and polishing, not a single breakthrough. Each of the problems above got solved on its own timeline, and the app didn't feel ready until all of them did. That gap between my original estimate and the real one is the single biggest thing that changed how I scope projects now.

What AI Actually Helped With

AI-assisted development is a big part of why I thought a few weeks was a reasonable estimate in the first place, and it's worth being specific about what it did and didn't do for this project. It genuinely accelerated how fast I could learn unfamiliar parts of Flutter and work through boilerplate, and that sped up a lot of the early groundwork.

It didn't do the actual engineering thinking. Understanding why local user state was bleeding across sessions, deciding how the guest-versus-authenticated data split should work, tracing exactly where playback state was disagreeing with itself, working through the SHA certificate configuration until Google Sign-In actually authenticated against a real release build, none of that was something I could hand off. AI made the learning curve faster. It didn't make the debugging and the architecture decisions any less mine to work through, and those were the parts that actually took the months.

What This Means If You're Building Anything Stateful

Music apps just make this problem unusually visible, because audio keeps running whether or not you're looking at the screen that started it, but the underlying question shows up anywhere state needs to survive navigation. A shopping cart that disagrees with itself across two tabs. A multiplayer game state that two clients each think they own. A form that loses an edit because two components each held their own copy of it. The specifics change; the question underneath is always the same one.

  • Is there a single, unambiguous owner for this state, or does more than one screen think it's in charge?
  • What happens to this state when the app is backgrounded or interrupted by the OS?
  • If two screens both read this state right now, would they agree?
  • Would adding a new screen require touching how this state works, or just reading from it?

This isn't really a music-app-specific lesson. Any app with a piece of state that has to stay consistent across screens (audio, video, a shopping cart, a multiplayer session) benefits from answering the single-source-of-truth question before the first screen that touches it gets built, not after the second or third screen has already formed its own opinion.

What I Want to Improve Next

With Armonia live, the focus now is on refining what's already there, playback reliability, navigation, and the overall feel of the app, based on how it actually gets used, rather than adding features for their own sake. The underlying philosophy stays the same as the app grows: music should stay accessible, and premium shouldn't mean paywalled.

Conclusion

None of this is visible in a screenshot, a demo video, or an app store listing, which is exactly why it's so easy to skip. It only becomes visible in actual use (the thirty seconds after a phone call ends, the moment you swipe back to a screen you left mid-song), and by then, whether it was designed on purpose or not is the whole difference between an app that feels considered and one that feels assembled.

Modern music apps don't lose people over a bad color palette. They lose them in the moment the app forgets what it was doing thirty seconds ago, and that's not a UI problem. It's a plumbing problem that was never designed on purpose, in an app where the plumbing was always going to matter more than the paint.

Why I Started Armonia

More Articles