Kanora
Kanora
All dispatches
Airplay

The AirPlay Button Is the Easy Part

April 26, 20264 min readBy Kanora
kanoraairplayfeature

“Supports AirPlay” is one of those phrases that sounds much more complete than it usually is. In practice, the visible part of the feature is tiny. It is a symbol, a picker, maybe a menu, and a short burst of satisfaction when audio comes out of the right speaker. The problem is that all of the difficult work begins after that moment. If the route changes while something is already playing, does the session stay coherent. If the user moves from one output to another mid-queue, do they keep their place. If the system decides to renegotiate the route, does the app react calmly or does it end up looking like it has forgotten what it was doing.

That is the part I’ve been trying to take seriously in Kanora. I do not want routing to feel like a party trick. It needs to be boring in the best possible way. The app should know what is playing, where it is playing, and how to continue when the destination changes, all without turning the user into an amateur transport engineer. That sounds obvious until you start tracing the actual states a real listening session can pass through: local playback, route discovery, route selection, system-level handoff, queue continuity, interruption, resumption, and the extra complications that appear once DLNA and live input join the conversation.

One decision that has helped here is refusing to split the notion of “current playback” into too many different truths. The playback path still has complexity, but the design intent is clear: one persistent player for local and AirPlay playback, a dedicated route service watching the system, and explicit handling for route changes rather than a collection of unrelated view-level guesses. That shape is not glamorous, but it means the UI is less likely to drift away from the transport underneath it.

This is exactly the kind of problem where AI has been most useful for me. Not because it somehow understands audio routing better than the platform documentation does, but because it is good at exhausting the boring questions once I point it at the right files. When a feature like this feels “mostly working” but not quite settled, the fastest way to waste time is to keep doing ad hoc changes based on hunches. The better loop has been: trace the real execution flow, inventory which service owns what, identify where state can be observed twice or updated out of sequence, and turn that into a ticket or two I can actually finish. AI is very good at helping with that kind of codebase archaeology.

There is also a product judgment underneath all this. I do not want Kanora to behave like a streaming client that happens to tolerate local files. It is a library app first, and the playback chain needs to reflect that. If someone has ripped a stack of discs, tagged them properly, and built a queue with care, sending that music to another room should feel like a continuation of the same listening session, not a mode switch into some looser, more disposable version of the app.

That may sound a bit precious, but I think it matters. A lot of modern software trains you to accept tiny discontinuities because the catalog is effectively infinite and the session is easy to rebuild. Skip, search, replace, move on. Local libraries create different expectations. If I am halfway through something specific and I move the output, I do not want a fresh approximation of the session. I want the same session, just elsewhere.

So yes, the AirPlay button is the easy part. The actual feature is continuity. It is the invisible promise that the app will keep its composure while the operating system, the network, and the user all do slightly unpredictable things around it. If I get that right, nobody will comment on it. They will just keep listening. That is usually how you know this category of engineering work was worth doing.