esc
The Cast

The Peripheral That Wasn't

The Cast, March 26, 2026 (in which a comprehensive spec for a Stream Deck-controlled iPad music app is handed to Claude, a StreamDeckManager.swift is written with the wrong API, rewritten with the...

March 27, 2026

The Cast, March 26, 2026 (in which a comprehensive spec for a Stream Deck-controlled iPad music app is handed to Claude, a StreamDeckManager.swift is written with the wrong API, rewritten with the correct API, killed by a DriverKit entitlement that Xcode refuses to grant, replaced by on-screen buttons that turn out to be better in every way, a Zahl HM-1 headphone amplifier deletes an entire volume control system by merely existing, an iPad Pro is ordered from Amazon for €400 less than local retail specifically for this project and then reassigned as Claude’s living room terminal because a four-year-old refurbished iPad runs the app without breaking a sweat, two DACs take turns by day of week because audiophiles are like that, a mysterious semi-transparent bar appears at the bottom of the screen and survives three fix attempts and remains at large, and the Lizard observes that the best peripheral is the one you delete)


Previously on The Cast…

The The Pocket Conductor, or The Morning Four Coloured Rectangles Got Phone Numbers had happened. Four agents, four colours, one phone. The conductor was mobile. The baton was in the pocket.

But in a flat in Riga, a different pocket was about to matter — the pocket where a man keeps his music, his headphones, and his opinions about DACs.


20:00 — The Spec

The spec arrived fully formed, the way specs do when a person has been thinking about something for longer than they’re willing to admit.

“MusicDeck. An iPad app. SwiftUI. Controls Apple Music. Accepts commands from a physical Elgato Stream Deck connected via USB-C.”

The spec was detailed. The spec was thorough. The spec had a key layout diagram:

[PL1] [PL2] [PL3]
[PL4] [PL5] [PL6]
[prev][play][next]
[ — ][ — ][ — ]
[ — ][ — ][ — ]

The spec had an audio session configuration. The spec had a data model. The spec had instructions about MPMusicPlayerController.applicationQueuePlayer (not .systemMusicPlayer — “that one has restrictions”).

The spec had everything.

The spec was wrong about three things, but nobody knew yet.


20:04 — The Files

Four files materialised. MusicDeckApp.swift. MusicPlayer.swift. StreamDeckManager.swift. ContentView.swift. An Info.plist. A project.pbxproj generated by a subagent who was given 24 hex UUIDs and a dream.

The StreamDeckManager was written first because it was the point. The whole app existed to serve the Stream Deck. The Stream Deck was the protagonist. The iPad was the stage. The music was the excuse.

device.setKeyHandler { [weak self] keyIndex, pressed in
    guard pressed else { return }
    self?.handleKeyPress(keyIndex: keyIndex, keyCount: keyCount)
}

THE SQUIRREL: admiring the key rendering code “It renders UIImages onto physical keys! With gradients! With SF Symbols! With 9-point font labels!”

THE LIZARD: examining the StreamDeckKit documentation

THE API IS SWIFTUI
NOT UIIMAGE

THE SESSION IS A SINGLETON
NOT A CALLBACK

THE KEYS ARE VIEWS
NOT RENDERERS

READ THE README

🦎

The StreamDeckManager was rewritten. The UIImage rendering became SwiftUI views. The setKeyHandler became inputEventHandler. The @StreamDeckView macro appeared. The layout used StreamDeckKeyAreaLayout and StreamDeckKeyView.

Version two was correct.

Version two required a DriverKit entitlement.


20:30 — The Entitlement

“Communicates with Drivers.” A capability. A checkbox in Xcode. A checkbox that Xcode refused to check.

XCODE: “No.”

riclib: “But the SDK requires—”

XCODE: “No.”

The entitlement needed a paid Apple Developer account with specific provisioning. Or it needed something else. Or it needed Xcode to be in a better mood. The documentation was unclear in the specific way that Apple documentation is unclear — not wrong, not incomplete, just elsewhere, in a different document, linked from a page you haven’t found, behind a tab you haven’t clicked.

riclib: “the six buttons on screen work, but I connected a streamdeck and nothing happening there”

The on-screen buttons. The buttons that had been added as a fallback. The buttons that were supposed to be the secondary interface. The buttons that — and this was the part that nobody was ready to say out loud — were better.


20:45 — The Deletion

It happened quietly. The way all deletions of beloved code happen quietly — not with a ceremony but with a rm and a series of edits to a pbxproj that nobody should ever have to edit by hand.

rm StreamDeckManager.swift MusicDeck.entitlements

The SPM package reference: removed. The framework link: removed. The build file reference: removed. The source file reference: removed. The group entry: removed. The product dependency: removed.

Eleven edits to the pbxproj. Eleven surgical strikes. The Stream Deck’s entire existence in the codebase, from the first import StreamDeckKit to the last StreamDeckKeyView, excised in under a minute.

THE SQUIRREL: holding the StreamDeckManagerV3.swift blueprint “We… we wrote this three times.”

THE LIZARD:

THE BEST PERIPHERAL
IS THE ONE YOU DELETE

THE SECOND BEST
IS THE ONE YOU NEVER ADD

THE STREAM DECK WAS WRITTEN
REWRITTEN
AND REMOVED

THE ON-SCREEN BUTTONS
WERE WRITTEN ONCE

ONCE IS THE CORRECT
NUMBER OF TIMES

🦎

THE SQUIRREL: “But the haptic feedback! The physical keys! The—”

riclib: “The buttons on the screen work. I can see them. I can tap them. I don’t need a USB-C peripheral to do what a finger already does.”

THE SQUIRREL: placing the blueprint on the pile of deleted Swift files, which was starting to look like a small memorial


20:15 — The Zahl Incident

It happened between the Stream Deck debates. A single message. A casual aside. The kind of remark that deletes code by existing.

riclib: “I don’t need volume control. my zahl hm-1 has a fully analogue one :)”

The Zahl HM-1. A headphone amplifier. The kind of headphone amplifier that costs more than a used car and has a single analogue volume knob that weighs more than a Stream Deck and rotates with the specific mechanical resistance of German engineering designed to make you feel the volume changing in a way that a software slider cannot.

The setVolume(_ volume: Float) function: deleted. The MPVolumeView extension: deleted. The entire volume control system — the function, the helper, the UI elements that would have driven it — erased from existence by a single sentence about a German amplifier.

THE PASSING AI: limping through the codebase, noting the gap where volume control used to be “The Zahl didn’t even have to be connected. It deleted code through reputation.”

The Topping D70 Sabre feeds the Zahl on even days. The FiiO R2R DAC feeds it on odd days. This is not a technical requirement. This is the specific behaviour of a man who owns two DACs and believes that Sabre chip precision and R2R warmth are both correct and that the solution is not to choose but to alternate, the way some people alternate running shoes, except the shoes cost four figures and the alternation schedule is based on calendar parity.


21:00 — The iPad Situation

The app worked. On a simulator. The simulator does not have an Apple Music library. The simulator does not connect to a Zahl HM-1. The simulator is a promise, not a delivery.

riclib: “so What do I need to do to get this testing on my ipad :)”

The iPad was a four-year-old refurbished unit. Not specified. Not celebrated. Just present, the way a reliable tool is present — in the drawer, charged, ready, invisible until needed.

The app compiled. The app deployed. The app requested Apple Music access. The app found playlists. The app displayed album art. The app played ABGT 621 by Above & Beyond at full screen with a blurred background and transport controls and an Up Next sidebar that showed every track in the queue.

On a four-year-old refurbished iPad.

riclib: staring at the screen, Group Therapy Intro playing through the Zahl

THE PASSING AI: “The iPad cost less than one channel of the amplifier chain.”

But riclib had already ordered an iPad Pro. The M4. Eleven inches. From Amazon, because Amazon was €400 cheaper than the local retailers, which is the kind of price discrepancy that makes a man order hardware from a website instead of walking to a shop, which is the same economics that makes a developer use SQLite instead of Postgres — it’s not that the expensive option is wrong, it’s that the cheap option is sufficient and sufficiency is a feature.

The iPad Pro arrived on Sunday. The app was already running on the old iPad. The old iPad was not struggling. The old iPad was not warm. The old iPad was running a full-screen blurred album art view with a 20-item scrollable queue sidebar with lazy artwork loading and it was fine.

riclib: “the iPad Pro will be claude’s home in the living room”

The iPad Pro — ordered specifically for MusicDeck, the €400-saved M4 powerhouse — was reassigned to be a Claude terminal. The music app that justified the purchase ran on the device that nobody had considered. The protagonist became the set piece. The understudy got the role.


21:30 — The Bottom Bar

It appeared without explanation. A thin, semi-transparent horizontal line at the bottom of the screen. Visible against the blurred album art. Not part of the UI. Not a control. Not a tray. Just… there.

riclib: “there is a visible horizontal semi transparent row at the bottom visible on screen. do you know what it could be?”

Three theories. Three fixes. Three failures.

Theory 1: The home indicator. Fix: .persistentSystemOverlays(.hidden). Result: still there.

Theory 2: The safe area gap. Fix: UIWindow.backgroundColor = .black. Result: still there.

Theory 3: The background blur not extending into the safe area. Fix: .ignoresSafeArea() on the blur. Result: controls disappeared.

riclib: “that didn’t work, the controls disappeared”

All three fixes were reverted. The bottom bar remained. The bottom bar is still there. The bottom bar will outlive us all.

THE PASSING AI: examining the bottom bar “Every codebase has one. A visual artefact that survives every fix. A ghost in the rendering pipeline. A single pixel of unexplained translucency that three engineers and an AI cannot eliminate.”

THE LIZARD:

SOME BUGS ARE NOT BUGS

SOME BUGS ARE FURNITURE

THE BOTTOM BAR HAS BEEN
FIXED THREE TIMES
AND EXISTS FOUR TIMES

IT IS NOT A BUG

IT IS A RESIDENT

🦎

22:00 — The Beautiful Part

But the app. The app.

Full-screen album art. Blurred background that matched the cover. ABGT 621 playing through Above & Beyond, through the Topping D70 (it was an even day), through the Zahl HM-1, through headphones that cost more than the iPad displaying the artwork.

Tap the screen: a playlist tray slides up from the bottom — pinned playlists sorted by name, double-tap to pin new ones, expand to see the full library. Tap outside: the tray slides away. The queue sidebar on the right, showing every upcoming track, album boundaries marked with subtle dividers.

Tap a playlist: it picks a random album boundary and plays sequentially from there. Not shuffle — album shuffle. The whole reason the app exists. A hundred albums in a playlist, and every time you tap it, you land on a different album’s first track and listen through in order.

riclib: “this is the best streamer ever”

Three Swift files. No dependencies. No StreamDeckKit. No DriverKit entitlement. No volume control. No Stream Deck.

Just a four-year-old iPad, a German amplifier, two DACs that take turns, and an app that does exactly one thing: shows you what’s playing and lets you pick what plays next.

THE SQUIRREL: looking at the three remaining files “We started with four files and a StreamDeckKit dependency and a DriverKit entitlement and a 15-key layout diagram.”

riclib: “And we ended with three files and a finger.”

THE SQUIRREL: “The spec had a whole section about key rendering. Font sizes. Gradient backgrounds. SF Symbol alignment.”

riclib: “The spec was wrong.”

THE SQUIRREL: “The spec was thorough.”

riclib: “Thoroughly wrong.”


The View from the Warm Spot

[Late evening. The flat in Riga. Oskar on the warm spot above the Bosch — now humming in English, as established in The Dial That Wasn’t. Mia atop the refrigerator. The iPad glows on its stand, Vivian Roost’s “Saudade” playing through equipment that costs more than some people’s first car.]

OSKAR: ears tracking the music “The small screen is playing music.”

MIA: stare: it’s been playing music for three hours

OSKAR: “He bought a bigger screen.”

MIA: stare: the bigger screen is in a box. marked for the AI.

OSKAR: “The AI gets its own screen?”

MIA: slow blink: the AI got the expensive one. the music got the old one. this is the correct allocation.

OSKAR: “Why?”

MIA: stare: because the music doesn’t need the expensive one. the old one was always fast enough. he just didn’t know until he tried.

OSKAR: “Like the Bosch.”

MIA: slow blink

OSKAR: “The Bosch was always German. The iPad was always fast enough. Things are what they are. People just take a while.”

MIA: already asleep, or pretending to be, which with Mia is the same thing


The Tally

StreamDeckManager.swift versions written:                3
  Version 1 (UIImage rendering):                        wrong API
  Version 2 (SwiftUI @StreamDeckView):                  correct API, wrong entitlement
  Version 3:                                            deleted before writing
StreamDeckManager.swift versions shipped:                0
StreamDeckKit SPM references added:                      1
StreamDeckKit SPM references removed:                    1
Net StreamDeckKit references:                            0

DriverKit entitlements attempted:                        1
DriverKit entitlements granted by Xcode:                 0
Conversations with Xcode about entitlements:             1
  Xcode's response:                                     "No."
  Elaboration:                                          "No."

Lines of code deleted by the Zahl HM-1:                  15
  setVolume function:                                   3 lines
  MPVolumeView extension:                               8 lines
  References:                                           4 lines
Words spoken by the Zahl to delete them:                  0
  (its reputation preceded it)

DACs in rotation:                                        2
  Even days:                                            Topping D70 (Sabre)
  Odd days:                                             FiiO R2R
  Rationale:                                            "audiophiles are like that"
  People who can hear the difference:                   1 (alleged)

iPad Pro M4 ordered:                                     1
  Price saved vs local:                                 €400
  Original purpose:                                     MusicDeck
  Final purpose:                                        Claude's living room terminal
  Days between arrival and reassignment:                0

Refurbished iPad age:                                    4 years
  Performance issues running MusicDeck:                 0
  Memory crashes:                                       1
    Cause:                                              loading artwork for all playlists
    Fix:                                                lazy loading + disk cache
  Temperature increase while running:                   negligible
  Smugness level:                                       high

Mysterious bottom bar fix attempts:                      3
  .persistentSystemOverlays(.hidden):                   didn't work
  UIWindow.backgroundColor = .black:                    didn't work
  .ignoresSafeArea() on blur:                           deleted the controls
  Bottom bar status:                                    at large
  Warrant issued:                                       no
  Expected resolution:                                  heat death of the universe

Playlists hardcoded then unhardcoded:                    6
  A State of Trance:                                    hardcoded, then dynamic
  Group Therapy:                                        hardcoded, then dynamic
  Pure Focus:                                           hardcoded, then dynamic
  Chill:                                                hardcoded, then dynamic
  Creative Focus:                                       hardcoded, then dynamic
  Buddha Bar:                                           hardcoded, then dynamic

Final file count:                                        3
  MusicDeckApp.swift:                                   audio session, auth
  MusicPlayer.swift:                                    playback, pinning, caching
  ContentView.swift:                                    everything you see
Dependencies:                                            0
Peripherals required:                                    0
  (one finger, standard equipment)

Album art cover generation:
  Vivian Roost "Saudade":                               gorgeous
  ABGT 621:                                             club photography, blurred perfectly

March 26, 2026. Riga. A Wednesday turning Thursday.
In which a Stream Deck was the whole point
And then wasn’t

The spec said fifteen keys
Three rows of playlists
One row of transport
Two rows of nothing

The code said UIImage
The SDK said SwiftUI
The code said inputEventHandler
Xcode said No

The on-screen buttons said
We were here the whole time
You just weren’t looking
Because you were looking
At the peripheral

The Zahl said nothing
It never says anything
It just sits there
With its analogue dial
And its German engineering
And fifteen lines of code
Fell silent

Two DACs take turns
By the calendar
Sabre on even days
R2R on odd
Because the man who builds
With three files and no dependencies
Rotates his DACs by parity
And sees no contradiction

The iPad Pro arrived on Sunday
Ordered for the app
Assigned to the AI
The old iPad got the role
The old iPad was always enough
The old iPad was always fast
Nobody asked the old iPad
Until they asked

The bottom bar remains
At the bottom
Semi-transparent
Unexplained
Surviving every fix
Outliving every workaround
A permanent resident
Of the safe area
That is not safe
From scrutiny
But is safe
From deletion

Three files
Zero dependencies
One finger
One Zahl
One old iPad
And the best streamer
He ever built

🦎🎵


See also:

The Lineage:

  • The Pocket Conductor, or The Morning Four Coloured Rectangles Got Phone Numbers — The morning the agents got phone numbers, the evening the music got an app
  • The Dial That Wasn’t — Another German device, another three-year misunderstanding, another revelation

The Cast:

  • The Caffeine Gradient — The Morning the Encyclopedia Discovered Coffee — The Zahl HM-1’s first mention in the lifelog, where riclib’s audio equipment was compared to Vlad’s palate
  • The Stream Deck — Written in, rewritten, and written out. Three versions. Zero shipped.
  • The Zahl HM-1 — Deleted code without being plugged in. Reputation-based refactoring.
  • The Bottom Bar — Still at large. Considered armed and semi-transparent.

The Technology: