<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>ars.md</title><description>Where curiosity meets the command line.</description><link>https://www.ars.md/</link><language>en-us</language><atom:link rel="hub" href="https://pubsubhubbub.appspot.com/"/><atom:link rel="self" type="application/rss+xml" href="https://www.ars.md/rss.xml"/><item><title>Cable or 2Go? Two ways to feed a Hugo 2 from an iPhone</title><link>https://www.ars.md/blog/iphone-to-hugo-2-cable-vs-2go/</link><guid isPermaLink="true">https://www.ars.md/blog/iphone-to-hugo-2-cable-vs-2go/</guid><description>Two ways to play music from an iPhone into a Chord Hugo 2 — a USB cable, or UPnP to a 2Go bolted onto it. Both are bit-exact. Here&apos;s what actually differs, and which one is optimal.</description><pubDate>Fri, 05 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you own a Chord Hugo 2 and an iPhone, there are two good ways to get music from one into the other. You can run a USB cable straight from the phone to the Hugo 2. Or you can bolt a &lt;a href=&quot;https://www.ars.md/blog/chord-2go-iphone-setup/&quot;&gt;Chord 2Go&lt;/a&gt; onto the back of the Hugo 2 and have the phone send music to it over your network. People ask me which is better often enough that it’s worth laying out properly — including the part where the honest answer is “it depends, and not for the reason you’d guess.”&lt;/p&gt;
&lt;p&gt;I build &lt;a href=&quot;https://www.ars.md/1-bit/&quot;&gt;1-bit&lt;/a&gt;, the iOS player I’ll use for the walkthrough, and it does both paths. So I’ve spent a lot of time A/B-ing them on my own Hugo 2. Here’s what I’ve found.&lt;/p&gt;
&lt;h2 id=&quot;the-thing-nobody-points-out&quot;&gt;The thing nobody points out&lt;/h2&gt;
&lt;p&gt;Both paths end at the &lt;em&gt;same input&lt;/em&gt; on the Hugo 2.&lt;/p&gt;
&lt;p&gt;The Hugo 2 is the DAC in both cases — it does the decoding, the conversion to analogue, all the parts that make a Chord sound like a Chord. And the 2Go doesn’t bypass any of that. The 2Go is a &lt;strong&gt;transport&lt;/strong&gt;: it pulls a file off your network and feeds the Hugo 2 over the very USB input your cable would otherwise plug into. Same DAC, same digital input, same decoding.&lt;/p&gt;
&lt;p&gt;So the question was never “which sounds better, the Hugo 2 or the Hugo 2.” It’s narrower and more interesting than that: &lt;strong&gt;what should be driving that input — your phone, or the 2Go?&lt;/strong&gt; That’s the entire decision.&lt;/p&gt;
&lt;h2 id=&quot;path-a--the-cable-phone-as-transport&quot;&gt;Path A — the cable: phone as transport&lt;/h2&gt;
&lt;p&gt;The simplest version. A USB cable from the iPhone to the Hugo 2, and 1-bit sends the audio straight down it, bit-exact, DSD wrapped in DoP if that’s what you’re playing. I wrote the full how-to for this in &lt;a href=&quot;https://www.ars.md/blog/how-to-play-dsd-on-iphone/&quot;&gt;How to play DSD on an iPhone&lt;/a&gt; — the cable rig is exactly that setup.&lt;/p&gt;
&lt;p&gt;What’s good about it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;It works anywhere.&lt;/strong&gt; No network, no bridge, no setup. Plane, train, hotel, a friend’s couch — if the phone and the Hugo 2 are in the same bag, you have a full hi-fi.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It’s rock solid.&lt;/strong&gt; There’s no discovery, no multicast, no router to misbehave. You plug in and it plays.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It’s genuinely portable.&lt;/strong&gt; This is the rig the Hugo 2 was arguably designed for — a battery DAC and a phone, untethered from the wall.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What’s not:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The phone is in the audio path.&lt;/strong&gt; It’s the transport now, which means it has to stay physically attached, it drains its battery doing the work, and it ties up the only port you’ve got.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The phone is electrically noisy.&lt;/strong&gt; It’s a general-purpose computer full of radios and switching supplies, and the cable couples some of that to the DAC. Hold that thought — it’s the whole case for the other path.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;path-b--the-2go-phone-as-remote&quot;&gt;Path B — the 2Go: phone as remote&lt;/h2&gt;
&lt;p&gt;Now the network version. The 2Go is attached to the Hugo 2 and joined to your Wi-Fi. Your music lives on a &lt;a href=&quot;https://www.ars.md/1-bit/bridge/&quot;&gt;1-bit bridge&lt;/a&gt; somewhere on the same network. In 1-bit you pick the 2Go as the output and press play. The full click-by-click is in &lt;a href=&quot;https://www.ars.md/blog/chord-2go-iphone-setup/&quot;&gt;Setting up a Chord 2Go with an iPhone&lt;/a&gt;, but here’s the part that matters for this comparison:&lt;/p&gt;
&lt;p&gt;The phone never touches the audio. It hands the 2Go a link to the file on your bridge, and the &lt;strong&gt;2Go fetches and plays it itself&lt;/strong&gt;. The phone drops out of the path entirely and becomes a remote control.&lt;/p&gt;
&lt;p&gt;What’s good about it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;You can walk away.&lt;/strong&gt; Lock the phone, leave the room, let the battery sit at 100%. The 2Go keeps playing because it’s the one doing the playing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The DAC is isolated from the phone.&lt;/strong&gt; This is the real audiophile argument for the 2Go, and it’s a fair one: the noisy general-purpose computer is no longer galvanically tied to your DAC. The transport feeding the Hugo 2 is now a purpose-built, battery-powered streamer instead of a phone.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It’s a tidy home rig.&lt;/strong&gt; Once it’s set up, the experience is “open app, press play” from anywhere in the house.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What’s not:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;It needs your network and a bridge.&lt;/strong&gt; The music has to live on a bridge for the 2Go to have something to fetch — on-device files and SMB shares can’t be cast this way. And the whole thing depends on your LAN behaving.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It can have network gremlins.&lt;/strong&gt; Multicast quirks, client isolation, a flaky mesh node — none of it fatal, all of it covered in the 2Go guide’s troubleshooting, but it’s complexity the cable simply doesn’t have.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-bits-are-identical--so-set-your-expectations&quot;&gt;The bits are identical — so set your expectations&lt;/h2&gt;
&lt;p&gt;Here’s the part the format wars won’t tell you: &lt;strong&gt;both paths are bit-exact.&lt;/strong&gt; The cable sends the original file’s bits down USB with no resampling. The 2Go fetches the original file and plays it untouched. DSD works on both — over the cable it goes as DoP, and the 2Go receives the whole &lt;code&gt;.dsf&lt;/code&gt; and hands it to the Hugo 2 intact.&lt;/p&gt;
&lt;p&gt;So the same album, played both ways, arrives at the Hugo 2 as the same data. Anyone promising you a dramatic sound-quality leap from switching is selling something. If there’s a difference, it lives entirely in that one remaining variable — electrical noise from the transport — and how audible &lt;em&gt;that&lt;/em&gt; is on any given system is a genuinely contested question. I can hear myself wanting to hear it, which is exactly when I trust it least.&lt;/p&gt;
&lt;h2 id=&quot;where-they-actually-differ&quot;&gt;Where they actually differ&lt;/h2&gt;
&lt;p&gt;Strip away the bits, which are equal, and you’re left with this:&lt;/p&gt;
&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem; margin: 1.5rem 0 2rem; font-size: 0.95rem;&quot;&gt;&lt;ul style=&quot;margin: 0; padding-left: 1.25rem;&quot;&gt;&lt;li&gt;&lt;strong&gt;The phone’s job.&lt;/strong&gt; Cable: it’s the transport, tethered and working. 2Go: it’s a remote, free to lock and pocket.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Isolation.&lt;/strong&gt; Cable: phone noise reaches the DAC. 2Go: a purpose-built transport sits between them.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Dependencies.&lt;/strong&gt; Cable: none. 2Go: your network, plus a bridge holding the library.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Reliability.&lt;/strong&gt; Cable: as reliable as plugging in a cable. 2Go: as reliable as your Wi-Fi.&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Where it shines.&lt;/strong&gt; Cable: on the go. 2Go: a settled home system.&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
&lt;h2 id=&quot;so-which-is-optimal&quot;&gt;So which is optimal?&lt;/h2&gt;
&lt;p&gt;The non-cop-out answer: &lt;strong&gt;they’re optimal for different lives, and most people who own both end up using both.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;At home, the 2Go is the nicer way to live. The phone is free, the battery’s full, the DAC is fed by a dedicated transport instead of a pocket computer, and “press play and walk off” is just a better daily experience than being leashed to a cable. If you have a fixed listening spot and a network you trust, this is the one to set up.&lt;/p&gt;
&lt;p&gt;On the move — or any time you want zero failure points — the cable wins, and it gives up nothing on the bits to do it. It’s the more reliable path and the only portable one. When the Hugo 2 is in a bag rather than on a shelf, there’s no contest.&lt;/p&gt;
&lt;p&gt;What I’d gently steer you away from is buying a 2Go &lt;em&gt;expecting it to sound better&lt;/em&gt; than a clean USB cable into the same Hugo 2. That’s not what you’re paying for. You’re paying for the phone to leave the audio path — for the freedom and the isolation, not for a different stack of bits. If those are worth it to you, it’s a lovely upgrade. If you mostly listen on the go, save the money and keep using the cable; you already own the bit-exact path.&lt;/p&gt;
&lt;hr/&gt;
&lt;p&gt;The reassuring thing about a Hugo 2 is that you can’t really get this wrong. Cable or 2Go, the DAC at the end is the same and the bits reaching it are the same. Pick the path that fits where you listen, and let the Chord do the part it’s good at. If you haven’t set up either yet, start with the &lt;a href=&quot;https://www.ars.md/blog/how-to-play-dsd-on-iphone/&quot;&gt;DSD-over-cable guide&lt;/a&gt; — it’s the five-minute version — and graduate to the &lt;a href=&quot;https://www.ars.md/blog/chord-2go-iphone-setup/&quot;&gt;2Go&lt;/a&gt; when you want the phone out of the loop.&lt;/p&gt;</content:encoded><media:content url="https://www.ars.md/og/iphone-to-hugo-2-cable-vs-2go.png" medium="image" type="image/png"/><category>1-bit</category><category>iOS</category><category>audio</category><category>Chord</category><category>audiophile</category><category>DLNA</category></item><item><title>Setting up a Chord 2Go with an iPhone</title><link>https://www.ars.md/blog/chord-2go-iphone-setup/</link><guid isPermaLink="true">https://www.ars.md/blog/chord-2go-iphone-setup/</guid><description>How to play music from an iPhone to a Chord 2Go over your own network — bit-exact, no transcoding — using DLNA and a self-hosted 1-bit bridge. Plus the security caveat nobody mentions.</description><pubDate>Thu, 04 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;The Chord 2Go is a lovely, slightly fiddly little network streamer. Getting an iPhone to send music to it — the real bit-exact version, not some down-sampled AirPlay copy — is one of those tasks that’s simple once you know the path and baffling until you do. This is the path I use.&lt;/p&gt;
&lt;p&gt;Fair warning up front: I make &lt;a href=&quot;https://www.ars.md/1-bit/&quot;&gt;1-bit&lt;/a&gt;, and version 1.5 added the feature this guide leans on. The networking ideas apply to any DLNA setup, but the click-by-click is 1-bit plus its bridge. If you run a different player, skim for the concepts and ignore my screenshots-in-prose.&lt;/p&gt;
&lt;h2 id=&quot;what-the-2go-actually-is&quot;&gt;What the 2Go actually is&lt;/h2&gt;
&lt;p&gt;It’s a network streamer. It pulls music off your network (or an SD card) and feeds a Chord DAC — it bolts onto a Hugo 2, or works with other DACs through the 2Yu adapter. It speaks several protocols: Roon, AirPlay, DLNA/UPnP, MPD. The one we want is &lt;strong&gt;DLNA/UPnP&lt;/strong&gt;, because it can move your files bit-for-bit with no re-encoding on the way.&lt;/p&gt;
&lt;h2 id=&quot;why-not-just-airplay&quot;&gt;Why not just AirPlay?&lt;/h2&gt;
&lt;p&gt;You can AirPlay to a 2Go, and for background listening it’s fine. But AirPlay re-clocks and caps the stream — it isn’t the bit-exact path you bought a Chord for. DLNA is different in a subtle but important way. With AirPlay, the phone &lt;em&gt;pushes&lt;/em&gt; a stream to the 2Go. With DLNA, the phone just tells the 2Go &lt;em&gt;where the file lives&lt;/em&gt;, and the 2Go fetches and plays the original itself. The phone stops being in the audio path. That’s the part that makes it bit-exact.&lt;/p&gt;
&lt;h2 id=&quot;what-you-need&quot;&gt;What you need&lt;/h2&gt;
&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem; margin-bottom: 2rem; font-size: 0.95rem;&quot;&gt;&lt;ul style=&quot;margin: 0; padding-left: 1.25rem;&quot;&gt;&lt;li&gt;A &lt;strong&gt;Chord 2Go&lt;/strong&gt; on your network (with a Hugo 2, or via 2Yu with another DAC)&lt;/li&gt;&lt;li&gt;An iPhone with &lt;strong&gt;1-bit 1.5 or newer&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;A &lt;a href=&quot;https://www.ars.md/1-bit/bridge/&quot;&gt;1-bit bridge&lt;/a&gt; running on your network, with your library on it&lt;/li&gt;&lt;li&gt;All three on the same LAN — same Wi-Fi, same subnet&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
&lt;p&gt;One thing to get clear before you start: &lt;strong&gt;this is a bridge feature.&lt;/strong&gt; The 2Go plays by fetching a file from a URL, so the music has to live on a bridge for it to have something to fetch. Tracks on an SMB share or sitting on the phone itself can’t be cast this way — there’s no address for the 2Go to pull from.&lt;/p&gt;
&lt;h2 id=&quot;step-1--get-the-2go-onto-your-network&quot;&gt;Step 1 — Get the 2Go onto your network&lt;/h2&gt;
&lt;p&gt;Use Chord’s GoFigure app to join the 2Go to your Wi-Fi (or wire it in). That part is Chord’s territory and their docs cover it well, so I won’t re-tread it. Just confirm the 2Go is actually on your network and reachable before going further — most of the “it doesn’t work” emails I get are really “the 2Go was never online.”&lt;/p&gt;
&lt;h2 id=&quot;step-2--run-a-bridge-with-your-library&quot;&gt;Step 2 — Run a bridge with your library&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.ars.md/1-bit/bridge/&quot;&gt;1-bit bridge&lt;/a&gt; is the free, open-source companion server — Mac, Windows, Linux, or a Raspberry Pi next to your music drive. Point it at your library, pair your phone with the QR code from the admin console, and you’re set. If you already listen to 1-bit through a bridge, you’ve done this step and can move on.&lt;/p&gt;
&lt;h2 id=&quot;step-3--turn-on-the-bridges-dlna-mediaserver&quot;&gt;Step 3 — Turn on the bridge’s DLNA MediaServer&lt;/h2&gt;
&lt;p&gt;This is the piece that makes the whole 2Go path exist, and it’s &lt;strong&gt;off by default&lt;/strong&gt;. Switch it on and the bridge advertises your library on the LAN as a DLNA server that the 2Go can read.&lt;/p&gt;
&lt;div style=&quot;border: 1px solid var(--color-brand-border); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem; margin: 1.5rem 0 2rem; font-size: 0.95rem;&quot;&gt;&lt;p&gt;&lt;strong style=&quot;font-family: var(--font-sans);&quot;&gt;One thing worth saying plainly:&lt;/strong&gt; UPnP has no authentication. While that MediaServer is running, &lt;em&gt;anyone&lt;/em&gt; on your network can browse and stream that library. So it only ever binds your local network — never a public address — a public-mode bridge refuses to start it at all, and you should only turn it on for a network you trust. Leave it off on café or shared Wi-Fi.&lt;/p&gt;&lt;/div&gt;
&lt;h2 id=&quot;step-4--pick-the-2go-in-1-bit&quot;&gt;Step 4 — Pick the 2Go in 1-bit&lt;/h2&gt;
&lt;p&gt;Open Now Playing, tap the output picker, and the 2Go shows up as a renderer. Other DLNA control apps you might already use — mconnect, BubbleUPnP, Linn Kazoo — work the same way; the 2Go is just the one I built this against. Select it.&lt;/p&gt;
&lt;h2 id=&quot;step-5--press-play&quot;&gt;Step 5 — Press play&lt;/h2&gt;
&lt;p&gt;Here’s what happens under the hood: 1-bit hands the 2Go the track’s metadata and a link to the file on your bridge. The 2Go fetches the audio itself over your LAN and plays it, bit-exact, into your Chord DAC. Nothing routes through me or any server I run — at that point the phone is basically a remote control, and you can lock it and walk off while the music keeps going.&lt;/p&gt;
&lt;p&gt;DSD works too, by the way. Because the 2Go receives the original file rather than a stream from the phone, a DSD track arrives intact and your Chord setup decodes it the way it always does. The 2Go does the work; the phone is just pointing at the bytes.&lt;/p&gt;
&lt;h2 id=&quot;when-the-2go-wont-show-up&quot;&gt;When the 2Go won’t show up&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Check that everything is on the same subnet.&lt;/strong&gt; Guest networks and a router setting usually called “client isolation” or “AP isolation” will hide your devices from each other. This is the number-one cause, by a wide margin.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Confirm the DLNA MediaServer is actually on&lt;/strong&gt; in the bridge admin console.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mind multicast.&lt;/strong&gt; DLNA discovery uses SSDP, which rides on multicast, and some mesh Wi-Fi systems drop multicast between bands or nodes. If discovery is flaky, getting the phone and bridge onto the same band or node often fixes it.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;when-it-shows-up-but-wont-play&quot;&gt;When it shows up but won’t play&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The track has to live on the bridge.&lt;/strong&gt; If you’ve selected the 2Go but you’re playing from an SMB share or on-device files, there’s nothing for it to fetch. Add (or move) the music to the bridge and try again.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr/&gt;
&lt;p&gt;Chord spent years of firmware getting the 2Go to this point; it took me one release to plumb the iPhone side into it. And the reward for all of it is that the setup disappears: open the app, pick the 2Go, press play. That’s the bar for gear like this — once it’s working, you should never have to think about it again.&lt;/p&gt;</content:encoded><media:content url="https://www.ars.md/og/chord-2go-iphone-setup.png" medium="image" type="image/png"/><category>1-bit</category><category>iOS</category><category>audio</category><category>Chord</category><category>DLNA</category><category>audiophile</category></item><item><title>How to play DSD on an iPhone</title><link>https://www.ars.md/blog/how-to-play-dsd-on-iphone/</link><guid isPermaLink="true">https://www.ars.md/blog/how-to-play-dsd-on-iphone/</guid><description>iPhones don&apos;t play DSD out of the box, and most apps quietly convert it to PCM. Here&apos;s what actually playing DSD takes — a USB DAC, DoP, the right file, and a player that gets out of the way.</description><pubDate>Thu, 04 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Some version of this lands in my inbox most weeks: &lt;em&gt;“I put my DSD files on my iPhone and nothing plays them right. What am I doing wrong?”&lt;/em&gt; Usually the answer is nothing. The iPhone just doesn’t play DSD the way you’d expect, and a lot of apps cover for that in a way that quietly defeats the point.&lt;/p&gt;
&lt;p&gt;I build &lt;a href=&quot;https://www.ars.md/1-bit/&quot;&gt;1-bit&lt;/a&gt;, an iOS player that plays DSD properly, so I’ve spent more hours in this corner than is strictly healthy. Here’s the whole picture in plain terms — what you need, what trips people up, and how to check you’re actually hearing DSD and not a polite imitation of it.&lt;/p&gt;
&lt;h2 id=&quot;what-dsd-actually-is&quot;&gt;What DSD actually is&lt;/h2&gt;
&lt;p&gt;DSD is Direct Stream Digital — the format behind SACD. Instead of the 16 or 24 bits per sample that PCM uses (CDs, FLAC, most of what you own), DSD stores audio as a single bit, sampled millions of times a second: 2.8 MHz for DSD64, 5.6 for DSD128, 11.2 for DSD256. It’s a different way of describing the same sound. On disk it usually shows up as a &lt;code&gt;.dsf&lt;/code&gt; file, occasionally &lt;code&gt;.dff&lt;/code&gt;. If you want the longer version of why it exists, I wrote about it when &lt;a href=&quot;https://www.ars.md/blog/1-bit-is-out/&quot;&gt;1-bit launched&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;the-catch-nobody-mentions&quot;&gt;The catch nobody mentions&lt;/h2&gt;
&lt;p&gt;iOS has no built-in DSD playback. Apple Music won’t do it. The Files preview won’t do it. Out of the box, the iPhone is a PCM device, full stop.&lt;/p&gt;
&lt;p&gt;So every app that claims to “play DSD” on iOS is doing one of two things. Most convert it to PCM first — easy, works everywhere, and sounds fine to almost everyone. A few wrap it in something called DoP and send the untouched bits to a DAC built to decode them. The second one is what you’re actually after, and it needs specific hardware.&lt;/p&gt;
&lt;h2 id=&quot;what-you-need&quot;&gt;What you need&lt;/h2&gt;
&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem; margin-bottom: 2.5rem; font-size: 0.95rem;&quot;&gt;&lt;ul style=&quot;margin: 0; padding-left: 1.25rem;&quot;&gt;&lt;li&gt;An iPhone — USB-C (15 or newer) makes life easier; Lightning works with a camera adapter&lt;/li&gt;&lt;li&gt;A &lt;strong&gt;USB DAC that supports DSD over DoP&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;Your DSD files (&lt;code&gt;.dsf&lt;/code&gt;) somewhere the app can reach&lt;/li&gt;&lt;li&gt;A player that sends DSD as DoP &lt;strong&gt;without converting it&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;
&lt;h2 id=&quot;you-need-a-real-dac--the-phone-cant-do-this-alone&quot;&gt;You need a real DAC — the phone can’t do this alone&lt;/h2&gt;
&lt;p&gt;DSD has to be decoded by hardware made for it. The built-in output, Bluetooth, AirPlay — none of them carry a true DSD stream. Bluetooth and AirPlay are lossy or PCM by the time they reach the other end, so “DSD over Bluetooth” isn’t DSD anymore; it’s a conversion with extra steps.&lt;/p&gt;
&lt;p&gt;What works is a USB DAC that lists DSD or DoP support. DACs from Chord, iFi, FiiO, Topping and the like generally handle it — check the spec sheet for the words “DSD” or “DoP” before you buy. And to be clear: a cheap USB-C-to-headphone dongle &lt;em&gt;is&lt;/em&gt; a DAC, but a PCM-only one. It’ll play your music; it won’t lock to DSD.&lt;/p&gt;
&lt;h2 id=&quot;what-dop-is-and-why-you-keep-seeing-it&quot;&gt;What DoP is, and why you keep seeing it&lt;/h2&gt;
&lt;p&gt;DoP stands for DSD over PCM, and it’s the trick that makes any of this possible. iOS won’t hand a raw, native DSD stream to a USB DAC. So the whole industry does the same workaround: take the 1-bit DSD stream, pack it inside PCM-shaped frames, and add a little marker that tells the DAC “this looks like PCM but it isn’t — it’s DSD wearing a coat.”&lt;/p&gt;
&lt;p&gt;The DAC reads the marker, unwraps the frames, and decodes the original bits. Nothing is lost in the wrapping; it’s the same data, just couriered in a container iOS is willing to carry. The only requirement is that your DAC understands DoP. Most modern USB DACs do.&lt;/p&gt;
&lt;h2 id=&quot;the-trap-playing-a-file-vs-actually-playing-dsd&quot;&gt;The trap: “playing” a file vs actually playing DSD&lt;/h2&gt;
&lt;p&gt;This is the part that fools people, including careful ones.&lt;/p&gt;
&lt;p&gt;An app can play your DSD album, show the right title, scrub through it like normal — and be silently converting it to 48 kHz PCM the entire time. It plays. It just isn’t DSD anymore. That’s the default behaviour of a surprising number of iOS players, because conversion is the path of least resistance.&lt;/p&gt;
&lt;p&gt;Here’s how to know which one you’ve got: &lt;strong&gt;look at the DAC, not the app.&lt;/strong&gt; A DAC decoding real DSD lights up a DSD or DoP indicator, or shows the rate outright — &lt;code&gt;DSD64&lt;/code&gt;, say. If it reads &lt;code&gt;PCM 44.1&lt;/code&gt; or &lt;code&gt;PCM 48&lt;/code&gt; while you’re “playing” a DSD file, you’re hearing a conversion. The DAC is the honest witness here. The app’s screen will tell you whatever the app wants to tell you.&lt;/p&gt;
&lt;h2 id=&quot;doing-it-step-by-step&quot;&gt;Doing it, step by step&lt;/h2&gt;
&lt;p&gt;Since 1-bit is my app I’ll use it for the walkthrough, but most of these steps apply to any player worth using.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Plug the DAC into the iPhone.&lt;/strong&gt; USB-C to USB-C on a 15 or 16. On older Lightning iPhones, you need Apple’s Lightning-to-USB Camera Adapter.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Put your DSD files where the app can see them&lt;/strong&gt; — an SMB share or NAS, a &lt;a href=&quot;https://www.ars.md/1-bit/bridge/&quot;&gt;1-bit bridge&lt;/a&gt;, or dropped straight into the app’s folder over Finder or Files.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Play a DSD album.&lt;/strong&gt; 1-bit reads the &lt;code&gt;.dsf&lt;/code&gt;, wraps it in DoP, and sends it to the DAC with no mixer and no resampling in the way.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Check the DAC.&lt;/strong&gt; It should report a DSD/DoP lock — &lt;code&gt;DSD64&lt;/code&gt;, &lt;code&gt;DSD128&lt;/code&gt;, whatever the file is. That’s the whole game. If you see that, you’re done.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;a-word-on-lightning-usb-c-and-power&quot;&gt;A word on Lightning, USB-C, and power&lt;/h2&gt;
&lt;p&gt;On USB-C iPhones a plain cable usually just works. On Lightning iPhones the bare Camera Adapter is fine for low-power DACs, but a hungrier desktop DAC may pull more than the phone wants to give — that’s when you want the version of the adapter with the extra Lightning power port, so the DAC isn’t running off the phone. If a DAC connects and then drops out a few seconds later, suspect power before anything else.&lt;/p&gt;
&lt;h2 id=&quot;when-it-doesnt-work&quot;&gt;When it doesn’t work&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No sound at all.&lt;/strong&gt; Either the DAC doesn’t do DoP, or your cable is charge-only (a classic — swap the cable first).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It plays, but the DAC says PCM.&lt;/strong&gt; The app is converting. Look for a “bit-exact” or “no resampling” setting, or switch to a player that doesn’t convert in the first place.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The DAC won’t lock, or clicks between tracks.&lt;/strong&gt; Some DACs — Chord’s FPGA-based ones especially — need a moment to re-lock. A good player builds that settling time in so you don’t hear it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The app says “no DAC” or doesn’t recognise your gear.&lt;/strong&gt; That’s almost always iOS hiding the DAC’s identity from the app, not an actual fault. I went down that whole rabbit hole in &lt;a href=&quot;https://www.ars.md/blog/when-no-dac-isnt-no-dac/&quot;&gt;a separate post&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;do-you-even-need-dsd&quot;&gt;Do you even need DSD?&lt;/h2&gt;
&lt;p&gt;Honest answer, since someone always asks: the gap between DSD and good high-res PCM is smaller than the format wars make it sound. If you have DSD files — SACD rips, native DSD recordings — play them as DSD; there’s no reason to throw bits away converting them. If you don’t have any, you’re not missing some hidden tier of sound by listening to clean FLAC.&lt;/p&gt;
&lt;p&gt;The point of all this was never that DSD is magic. It’s that if you went to the trouble of owning it, your phone ought to play it exactly as it is. Get the three things right — a DAC that speaks DoP, files the app can reach, a player that doesn’t quietly convert — and the iPhone makes a genuinely good DSD transport.&lt;/p&gt;</content:encoded><media:content url="https://www.ars.md/og/how-to-play-dsd-on-iphone.png" medium="image" type="image/png"/><category>DSD</category><category>iOS</category><category>audio</category><category>DoP</category><category>audiophile</category><category>1-bit</category></item><item><title>1-bit 1.5, the one with Chord 2Go support</title><link>https://www.ars.md/blog/1-bit-1-5-chord-2go/</link><guid isPermaLink="true">https://www.ars.md/blog/1-bit-1-5-chord-2go/</guid><description>1-bit 1.5 is on the App Store. Play bit-exact to a Chord 2Go or any DLNA renderer on your network, back up your playlists to your own bridge, and keep a private listening history. Bridge 0.1.5 ships alongside with a DLNA MediaServer. Same DSD over DoP, no transcoding.</description><pubDate>Wed, 03 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://apps.apple.com/us/app/1-bit/id6762529497&quot;&gt;1-bit 1.5 is on the App Store&lt;/a&gt;. Same URL, new build, free update.&lt;/p&gt;
&lt;p&gt;The easter-egg card in Settings → About keeps the multi-line habit it picked up in 1.4. This release’s verse is &lt;em&gt;“One hundred three commits are past, / The music breathes, unfettered, vast. / Pure bit-perfect sound takes flight, / To fill the room with deep delight. / Let every note and chord revive, / In beautiful version 1.5.”&lt;/em&gt; That fifth line is doing double duty — the headline feature is &lt;strong&gt;playing to a Chord 2Go&lt;/strong&gt;.&lt;/p&gt;
&lt;h2 id=&quot;whats-new&quot;&gt;What’s new&lt;/h2&gt;
&lt;div style=&quot;display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1rem; margin-bottom: 2rem;&quot;&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;Play to a Chord 2Go (and other DLNA renderers)&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;Cast a track to a DLNA / UPnP renderer on your network — the Chord 2Go is the target I built this for, and generic renderers (mconnect, BubbleUPnP, Linn Kazoo) work too. Pick it from the Now Playing output picker. The app hands the renderer the track metadata and a link to the file on your own &lt;a href=&quot;https://www.ars.md/1-bit/bridge/&quot;&gt;bridge&lt;/a&gt;; the renderer fetches and plays the audio itself, bit-exact, over your LAN. Nothing routes through a server I operate.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;Playlist backup to your bridge&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;Off by default, per bridge. The playlists you build in the app can now back up to the bridge you run, so they survive a reinstall or a re-pair. They go only to your bridge — never to me, never to a third party. Tracks that come from another bridge or from local / SMB storage stay as opaque references.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;Listening history to your bridge&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;Off by default, per bridge. Turn it on and the app reports each play — track, when, how long, codec, and where available the output device and DAC sample rate — to your own bridge. It’s your data: it lives on the bridge host, shows up on the admin console’s new Data page (histograms, most-played, export), and the bridge never forwards it anywhere.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;On-device diagnostics&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;A small daily performance summary — CPU/energy, memory, playback runtime — pulled from Apple’s on-device MetricKit and surfaced in the in-app diagnostics screen. It never leaves your phone. Handy when something feels off and you want to see whether the app is the culprit.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;a-note-on-the-chord-2go-path&quot;&gt;A note on the Chord 2Go path&lt;/h2&gt;
&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.5rem 1.75rem; margin-bottom: 2rem;&quot;&gt;&lt;p style=&quot;margin: 0;&quot;&gt;DLNA casting is a &lt;strong&gt;bridge feature&lt;/strong&gt;, not a standalone one. The renderer pulls the audio straight from your bridge over the LAN, so the track has to live on a bridge — an SMB share or the on-device library can’t be cast this way, because the renderer needs a URL to fetch from. And one thing worth saying plainly: &lt;strong&gt;UPnP has no authentication.&lt;/strong&gt; While the bridge’s DLNA MediaServer is on, any device on your LAN can browse and stream that library. So it’s off by default, it only ever binds your local network (never a public IP), and a public-mode bridge refuses to start it at all. Turn it on for a network you trust, and leave it off on shared Wi-Fi.&lt;/p&gt;&lt;/div&gt;
&lt;h2 id=&quot;and-on-the-bridge-side&quot;&gt;And on the bridge side&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.ars.md/1-bit/bridge/&quot;&gt;Bridge 0.1.5&lt;/a&gt; ships alongside — 45 commits since 0.1.4, iOS wire protocol unchanged at v1.&lt;/p&gt;
&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.5rem 1.75rem; margin-bottom: 2rem;&quot;&gt;&lt;p style=&quot;margin: 0;&quot;&gt;The big one is the &lt;strong&gt;DLNA / UPnP MediaServer&lt;/strong&gt; that makes the Chord 2Go path possible: an &lt;em&gt;All Tracks&lt;/em&gt; list plus a &lt;em&gt;Folders&lt;/em&gt; hierarchy with free-text search, bit-exact audio, cached upscale variants surfaced as alternate streams. Then the two storage features that land on the bridge: &lt;strong&gt;per-device playlist backup&lt;/strong&gt; and an &lt;strong&gt;opt-in listening history&lt;/strong&gt;, both viewable on the admin console’s new &lt;strong&gt;Data&lt;/strong&gt; page with JSON / CSV export. Plus a public-mode pairing-QR fix (the QR now carries the served Let’s Encrypt cert fingerprint and the public dial URL, so phone pairing no longer trips the TLS pin), a &lt;code&gt;sudo bridge update&lt;/code&gt; fix for FUSE-mounted libraries, and honest variant accounting across the admin views. Full release notes on the &lt;a href=&quot;https://github.com/acoseac/1-bit-bridge/releases/latest&quot;&gt;bridge releases page&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;
&lt;h2 id=&quot;the-data-we-collect-is-still-none&quot;&gt;The data we collect is still none&lt;/h2&gt;
&lt;div style=&quot;border: 1px solid var(--color-brand-border); padding: 1.5rem 1.75rem; margin-bottom: 2rem;&quot;&gt;&lt;p style=&quot;margin: 0;&quot;&gt;Two of the new features move your data off the phone — but only to a server &lt;em&gt;you&lt;/em&gt; run, and only if you turn them on. Playlist backup and listening history are off by default and go solely to your own bridge. DLNA playback stays on your LAN. When backup or history is enabled, the app mints a random per-device identifier (stored in the Keychain, not linked to your Apple ID or any ad identifier) so the bridge can keep each device’s backups separate. The MetricKit summary never leaves the device. No new third-party services, and aside from the multicast capability that SSDP discovery needs, no new permissions. The &lt;a href=&quot;https://www.ars.md/1-bit/privacy/&quot;&gt;privacy policy&lt;/a&gt; spells all of it out, and the App Store nutrition label stays “Data Not Collected”.&lt;/p&gt;&lt;/div&gt;
&lt;h2 id=&quot;same-audio-path&quot;&gt;Same audio path&lt;/h2&gt;
&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.5rem 1.75rem; margin-bottom: 2rem;&quot;&gt;&lt;p style=&quot;margin: 0;&quot;&gt;No transcoding. No resampling. No mixer in the path. DSD goes out as &lt;strong&gt;DoP&lt;/strong&gt; to your USB DAC; PCM goes out at the file’s native sample rate; and when you cast to a DLNA renderer, the renderer fetches the original bytes from your bridge and does its own job. The phone moves bytes and metadata; the hardware does the rest. &lt;a href=&quot;https://www.ars.md/1-bit/#dsd-playback&quot;&gt;How DSD playback is handled →&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;
&lt;h2 id=&quot;the-unromantic-version&quot;&gt;The unromantic version&lt;/h2&gt;
&lt;p&gt;If you have 1-bit installed: open the App Store and tap update. Your SMB shares, bridge pairings, and Settings carry over. DLNA casting, playlist backup, and listening history are all off by default — you turn them on if and when you want them. To play to a Chord 2Go, enable the DLNA MediaServer on your bridge first, then pick the renderer from Now Playing.&lt;/p&gt;
&lt;p&gt;If you’re new: it’s a &lt;a href=&quot;https://apps.apple.com/us/app/1-bit/id6762529497&quot;&gt;free download&lt;/a&gt;, iPhone or iPad, iOS 26.1 or later. Bring a NAS or a &lt;a href=&quot;https://www.ars.md/1-bit/bridge/&quot;&gt;bridge&lt;/a&gt;, a USB DAC for DSD (or a Chord 2Go on the network), and music you actually like.&lt;/p&gt;</content:encoded><media:content url="https://www.ars.md/og/1-bit-1-5-chord-2go.png" medium="image" type="image/png"/><category>1-bit</category><category>iOS</category><category>DSD</category><category>audio</category><category>release</category></item><item><title>1-bit 1.4, the one that goes in the car</title><link>https://www.ars.md/blog/1-bit-1-4-finally-stand/</link><guid isPermaLink="true">https://www.ars.md/blog/1-bit-1-4-finally-stand/</guid><description>1-bit 1.4 is on the App Store. CarPlay browse + Now Playing on the head unit, offline downloads, an in-app log viewer, and an optional DSD-domain DSP chain. Bridge 0.1.4 ships alongside with public-mode hosting and CarPlay-optimized variants. Same bit-exact DSD over DoP, no transcoding.</description><pubDate>Tue, 26 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://apps.apple.com/us/app/1-bit/id6762529497&quot;&gt;1-bit 1.4 is on the App Store&lt;/a&gt;. Same URL, new build, free update.&lt;/p&gt;
&lt;p&gt;Last release I &lt;a href=&quot;https://www.ars.md/blog/1-bit-1-3-forgot-the-codename/&quot;&gt;shipped a codename that lied about the version&lt;/a&gt;. This time I went the other way. Open Settings → About in 1.4, tap the easter-egg card, and instead of a single-line codename you get a small poem: &lt;em&gt;“One hundred three commits deep, while the tired engineers sleep. Offline downloads finally stand, CarPlay grids across the land. Tap the text and clear the view, version 1.4 is ready for you.”&lt;/em&gt; The two main features are right there in the verse, so I’ll keep the writeup short.&lt;/p&gt;
&lt;h2 id=&quot;whats-new&quot;&gt;What’s new&lt;/h2&gt;
&lt;div style=&quot;display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1rem; margin-bottom: 2rem;&quot;&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;CarPlay&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;Browse, search, tap-to-play, and a Now Playing view on a CarPlay head unit — reading the same on-device library that’s on your phone. Uses Apple’s CarPlay Audio entitlement, so it’s a real CarPlay app, not a screen mirror. DSD is hidden from CarPlay because DoP markers can’t survive head-unit playback; everything else plays.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;Offline downloads&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;Pick a track, an album, a playlist, save it to the iPhone. Files live in app-private storage, are hidden from Files, and are excluded from iCloud backup (so they don’t quietly fill your iCloud quota). Delete the download, delete the share, or uninstall the app — they’re gone.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;Diagnostic logs in Settings&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;A rolling buffer of recent log lines lives at &lt;strong&gt;Settings → Logs&lt;/strong&gt;. The logs stay on the device. A Share button is there for the rare case I ask you to send them — never automatic, never on by default.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;DSD-domain DSP (Beta)&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;An optional on-device chain — decimator, sigma-delta modulator, volume stage — that stays in the DSD domain instead of converting to PCM first. Off by default. Beta because thermal headroom on older iPhones can still surface the odd crackle, and I’d rather call that out than pretend it’s settled.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;and-on-the-bridge-side&quot;&gt;And on the bridge side&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.ars.md/1-bit/bridge/&quot;&gt;Bridge 0.1.4&lt;/a&gt; ships alongside — 44 commits since 0.1.3, wire protocol unchanged at v1.&lt;/p&gt;
&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.5rem 1.75rem; margin-bottom: 2rem;&quot;&gt;&lt;p style=&quot;margin: 0;&quot;&gt;Two big themes. &lt;strong&gt;Public-mode hosting:&lt;/strong&gt; &lt;code&gt;bridge init —public&lt;/code&gt; spins up a bridge intended for a small VPS, with native Let’s Encrypt provisioning via autocert, a single-user admin auth layer, and an endpoints filter so the public listener only exposes &lt;code&gt;/v1/health&lt;/code&gt; and the &lt;code&gt;/v1/share/…&lt;/code&gt; family. &lt;strong&gt;CarPlay-optimized variants:&lt;/strong&gt; a new &lt;code&gt;optimize-…&lt;/code&gt; variant class and a two-channel priority queue so on-demand CarPlay transcodes don’t sit behind a lifetime Upscale backlog. Plus a tile-grid Library Inspector, a Prometheus &lt;code&gt;/metrics&lt;/code&gt; endpoint on the admin listener, and a background orphan-sidecar GC sweeper. Full release notes on the &lt;a href=&quot;https://github.com/acoseac/1-bit-bridge/releases/latest&quot;&gt;bridge releases page&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;
&lt;h2 id=&quot;what-got-refined&quot;&gt;What got refined&lt;/h2&gt;
&lt;div style=&quot;border: 1px solid var(--color-brand-border); padding: 1.5rem 1.75rem; margin-bottom: 2rem;&quot;&gt;&lt;p style=&quot;margin: 0;&quot;&gt;A small carve-out in the iOS pinning policy: bridges deployed with a custom domain and a public Let’s Encrypt cert are now ATS-trusted, while LAN, Tailscale-magic-DNS, IP-literal, and mDNS hosts still pin against the fingerprint captured at pairing. The shape of what the app sends — paths, byte ranges, MusicBrainz IDs — is identical. The Settings screen got an IA pass to make room for the v1.4 surfaces. The deployment target is now iOS 26.1.&lt;/p&gt;&lt;/div&gt;
&lt;h2 id=&quot;same-audio-path&quot;&gt;Same audio path&lt;/h2&gt;
&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.5rem 1.75rem; margin-bottom: 2rem;&quot;&gt;&lt;p style=&quot;margin: 0;&quot;&gt;No transcoding. No resampling. No mixer in the path. DSD goes out as &lt;strong&gt;DoP&lt;/strong&gt; to your USB DAC; PCM goes out at the file’s native sample rate. The phone moves bytes; the DAC does the DAC’s job. &lt;a href=&quot;https://www.ars.md/1-bit/#dsd-playback&quot;&gt;How DSD playback is handled →&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;
&lt;h2 id=&quot;the-unromantic-version&quot;&gt;The unromantic version&lt;/h2&gt;
&lt;p&gt;If you have 1-bit installed: open the App Store and tap update. Your existing SMB shares, bridge pairings, and Settings carry over. Offline downloads, the DSP chain, and the log viewer are all off by default — you turn them on only if and when you want them. CarPlay shows up the moment you plug into a head unit.&lt;/p&gt;
&lt;p&gt;If you’re new: it’s a &lt;a href=&quot;https://apps.apple.com/us/app/1-bit/id6762529497&quot;&gt;free download&lt;/a&gt;, iPhone or iPad, iOS 26.1 or later. Bring a NAS or a &lt;a href=&quot;https://www.ars.md/1-bit/bridge/&quot;&gt;bridge&lt;/a&gt;, a USB DAC for DSD, and music you actually like.&lt;/p&gt;</content:encoded><media:content url="https://www.ars.md/og/1-bit-1-4-finally-stand.png" medium="image" type="image/png"/><category>1-bit</category><category>iOS</category><category>DSD</category><category>audio</category><category>release</category></item><item><title>1-bit 1.3, the one where we forgot to change the codename</title><link>https://www.ars.md/blog/1-bit-1-3-forgot-the-codename/</link><guid isPermaLink="true">https://www.ars.md/blog/1-bit-1-3-forgot-the-codename/</guid><description>1-bit 1.3 is on the App Store. Opportunistic background library scans, composer/conductor/work fields for classical, a Listening Tips guide, a refined cellular streaming gate — and an easter-egg card that still calls this version &apos;Too Late, Too Soon&apos;. Bridge 0.1.3 ships alongside. Same bit-exact DSD over DoP, no transcoding.</description><pubDate>Sat, 16 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://apps.apple.com/us/app/1-bit/id6762529497&quot;&gt;1-bit 1.3 is on the App Store&lt;/a&gt;. Same URL, new build. Update on your iPhone and it’ll be there.&lt;/p&gt;
&lt;p&gt;Open Settings → About in the new version and tap the easter-egg card. You’ll find: &lt;strong&gt;Version 1.3, codename “Too Late, Too Soon”&lt;/strong&gt;. Which was 1.2’s codename. The release checklist has a step for this — three sites in &lt;code&gt;AboutView.swift&lt;/code&gt; that have to move together every release — and the checklist quietly didn’t get run this time. Once a build is uploaded to App Store Connect, the easter-egg version is frozen in the binary; the only fix is a fresh build and a re-submission, which is not what 1.3 needed. So the codename slip ships as-is, and the iOS repo’s &lt;code&gt;CLAUDE.md&lt;/code&gt; now opens the release-checklist section with this exact story so the next release doesn’t repeat it. Honest changelog: a codename that lies about the version, fixed in 1.4.&lt;/p&gt;
&lt;p&gt;The actual shape of 1.3 is the boring kind of release: the scanner becomes less demanding of your attention, the library starts understanding music that isn’t pop-rock, and a Listening Tips guide collects the things that make this app sound the way it should.&lt;/p&gt;
&lt;h2 id=&quot;whats-new&quot;&gt;What’s new&lt;/h2&gt;
&lt;div style=&quot;display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1rem; margin-bottom: 2rem;&quot;&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;Opportunistic background scan&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;Your first scan of a big library can keep going after you background the app. iOS schedules a one-shot task; the work runs only when the device is charging, idle, and on Wi-Fi. The whole thing stays on your phone — no traffic beyond the SMB or bridge connection you’d already authorised. Off-switch in Settings → Refresh.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;Classical metadata&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;Composer, conductor, and work fields land in the library. Opera, symphonic, and concert recordings group the way the music does instead of being flattened against &lt;code&gt;ALBUMARTIST&lt;/code&gt;. Bridge 0.1.3 extracts the same fields server-side; SMB sources read them out of the file tags directly.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;Listening tips, in one place&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;A new in-app guide collects the things that make 1-bit sound the way it does: which DAC, what DoP actually is, why DSD pre-caches by default, when gapless does and doesn’t work, when to bypass crossfeed. Open from Settings → Listening Tips.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;Long-press a folder&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;Hold any folder in the browser to play it, queue it next, append to a playlist, or jump to its album — without leaving the browse view. Small thing; surprising how often you reach for it once it’s there.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;and-on-the-bridge-side&quot;&gt;And on the bridge side&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.ars.md/1-bit/bridge/&quot;&gt;Bridge 0.1.3&lt;/a&gt; ships alongside — 62 commits since 0.1.2, wire protocol unchanged at v1, every change additive. The headline:&lt;/p&gt;
&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.5rem 1.75rem; margin-bottom: 2rem;&quot;&gt;&lt;p style=&quot;margin: 0;&quot;&gt;A new &lt;strong&gt;Library Inspector&lt;/strong&gt; in the admin console: folder-first browse, FTS5 search, cursor-paginated virtualized scroll, inline metadata edit, per-track variant management, a stats drawer, and a Jobs page for upscale runs. Classical extractors (composer / conductor / work / original year / BPM) across FLAC, MP4, AIFF, WAV, DSF, and DFF. Multi-value &lt;code&gt;ARTIST&lt;/code&gt; / &lt;code&gt;ALBUMARTIST&lt;/code&gt; preserved on FLAC and MP4 instead of being collapsed. ALAC source bit depth read from the MP4 &lt;code&gt;alac&lt;/code&gt; atom rather than inferred. A configurable &lt;code&gt;upscale.variantsDir&lt;/code&gt; with source-mirrored layout plus a &lt;code&gt;bridge upscale move&lt;/code&gt; CLI. And a Tailscale Let’s Encrypt cert-expiry warning at startup so the iOS ATS 398-day re-pair cliff stops being a surprise. Full release notes on the &lt;a href=&quot;https://github.com/acoseac/1-bit-bridge/releases/latest&quot;&gt;bridge releases page&lt;/a&gt;.&lt;/p&gt;&lt;/div&gt;
&lt;h2 id=&quot;what-got-refined&quot;&gt;What got refined&lt;/h2&gt;
&lt;div style=&quot;border: 1px solid var(--color-brand-border); padding: 1.5rem 1.75rem; margin-bottom: 2rem;&quot;&gt;&lt;p style=&quot;margin: 0;&quot;&gt;The v1.2 per-share cellular gate now also stops the next-track preloader from quietly reaching for cellular when you’ve picked &lt;em&gt;Warn before streaming&lt;/em&gt; or &lt;em&gt;Wi-Fi only&lt;/em&gt;, and Low Power Mode is honoured the same way. Artist Detail filters orphan albums so the view doesn’t show ghosts of moved files. Stale &lt;code&gt;albumID&lt;/code&gt; / &lt;code&gt;artistID&lt;/code&gt; on tracks are canonicalised on the next access, so a re-scan that re-grouped an album doesn’t leave the queue pointing at the old ID. Album headers gain a mixed-format subtitle (“FLAC 24/96 · DSF DSD64”) for compilations that mix encodings.&lt;/p&gt;&lt;/div&gt;
&lt;h2 id=&quot;same-audio-path&quot;&gt;Same audio path&lt;/h2&gt;
&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.5rem 1.75rem; margin-bottom: 2rem;&quot;&gt;&lt;p style=&quot;margin: 0;&quot;&gt;No transcoding. No resampling. No mixer in the path. DSD goes out as &lt;strong&gt;DoP&lt;/strong&gt; to your USB DAC; PCM goes out at the file’s native sample rate. The phone moves bytes; the DAC does the DAC’s job. &lt;a href=&quot;https://www.ars.md/1-bit/#dsd-playback&quot;&gt;How DSD playback is handled →&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;
&lt;h2 id=&quot;the-unromantic-version&quot;&gt;The unromantic version&lt;/h2&gt;
&lt;p&gt;If you have 1-bit installed: open the App Store and tap update. Your existing SMB shares keep working. Your existing bridge pairings keep working. The cellular default stays &lt;em&gt;Always allow&lt;/em&gt;; the background scan is on by default but iOS still controls whether and when it runs. Nothing else changes until you say so.&lt;/p&gt;
&lt;p&gt;If you’re new: it’s a &lt;a href=&quot;https://apps.apple.com/us/app/1-bit/id6762529497&quot;&gt;free download&lt;/a&gt;, iPhone or iPad, iOS 18.6 or later. Bring a NAS or a &lt;a href=&quot;https://www.ars.md/1-bit/bridge/&quot;&gt;bridge&lt;/a&gt;, a USB DAC, and music you actually like.&lt;/p&gt;</content:encoded><media:content url="https://www.ars.md/og/1-bit-1-3-forgot-the-codename.png" medium="image" type="image/png"/><category>1-bit</category><category>iOS</category><category>DSD</category><category>audio</category><category>release</category></item><item><title>1-bit 1.2: too late, too soon</title><link>https://www.ars.md/blog/1-bit-1-2-too-late-too-soon/</link><guid isPermaLink="true">https://www.ars.md/blog/1-bit-1-2-too-late-too-soon/</guid><description>1-bit 1.2 is live on the App Store. Admin-approved bridge pairing, on-bridge upscaled audio variants, per-share cellular streaming, and a quieter audio engine — same bit-exact DSD over DoP, native-rate PCM, no transcoding.</description><pubDate>Sat, 09 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://apps.apple.com/us/app/1-bit/id6762529497&quot;&gt;1-bit 1.2 is on the App Store&lt;/a&gt;. Same URL as before, new build. Update on your iPhone and you’ll see it.&lt;/p&gt;
&lt;p&gt;Too late: 1.2 took a beat longer than planned. Too soon: there’s already more I want to ship. The shape of this release is the gap between the two — the pieces that had to land together, the ones that proved themselves on TestFlight, the ones that didn’t make the cut and roll into 1.3.&lt;/p&gt;
&lt;p&gt;The headline: a more honest &lt;a href=&quot;https://www.ars.md/1-bit/bridge/&quot;&gt;bridge&lt;/a&gt; you can actually feel safe handing to other people on your network, and a per-share cellular gate so DSD doesn’t quietly torch your data plan.&lt;/p&gt;
&lt;h2 id=&quot;whats-new&quot;&gt;What’s new&lt;/h2&gt;
&lt;div style=&quot;display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1rem; margin-bottom: 2rem;&quot;&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;Admin-approved pairing&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;Bridges can now require explicit operator approval before a new device pairs. The authorized-device list lives on the bridge you run, not on a server I operate. Pairings now feel like the front door of your house — you decide who walks in.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;Discover on network&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;A new tap-to-pair sheet finds nearby bridges, fires the join request, waits for approval, and persists the share automatically. No tokens to type, no QR scanning if you don’t need it.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;Optional upscaled variants&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;Bridges can generate optional upscaled PCM variants on request. Generation runs locally on your bridge and never leaves the network you control. Toggle per share; switch source ↔ upscale per track from the Now Playing wand.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;Per-share cellular control&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;Pick &lt;em&gt;Always allow&lt;/em&gt;, &lt;em&gt;Warn before streaming&lt;/em&gt;, or &lt;em&gt;Wi-Fi only&lt;/em&gt;. The gate covers bridge and SMB equally — Tailscale-fallback hosts can still reach cellular, so this is the right knob to refuse a 5 GB DSF file on a metered link.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;what-got-quieter&quot;&gt;What got quieter&lt;/h2&gt;
&lt;div style=&quot;border: 1px solid var(--color-brand-border); padding: 1.5rem 1.75rem; margin-bottom: 2rem;&quot;&gt;&lt;p style=&quot;margin: 0;&quot;&gt;A fully lock-free render path (no syscalls during the audio callback). Hardened DSF parsing (the sort of belt-and-braces that only matters when something corrupted breaks through). Smoother variant switching mid-track. PCM gap recovery on long screen-off / sleep cycles. Same bit-exact output, fewer edges to catch on.&lt;/p&gt;&lt;/div&gt;
&lt;h2 id=&quot;same-audio-path&quot;&gt;Same audio path&lt;/h2&gt;
&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.5rem 1.75rem; margin-bottom: 2rem;&quot;&gt;&lt;p style=&quot;margin: 0;&quot;&gt;No transcoding. No resampling. No mixer in the path. DSD goes out as &lt;strong&gt;DoP&lt;/strong&gt; to your USB DAC; PCM goes out at the file’s native sample rate. The phone moves bytes; the DAC does the DAC’s job. &lt;a href=&quot;https://www.ars.md/1-bit/#dsd-playback&quot;&gt;How DSD playback is handled →&lt;/a&gt;&lt;/p&gt;&lt;/div&gt;
&lt;h2 id=&quot;the-unromantic-version&quot;&gt;The unromantic version&lt;/h2&gt;
&lt;p&gt;If you have 1-bit installed: open the App Store and tap update. Existing SMB shares keep working. Existing bridge pairings keep working. The cellular default is &lt;em&gt;Always allow&lt;/em&gt;, so nothing changes until you say so.&lt;/p&gt;
&lt;p&gt;If you’re new: it’s a &lt;a href=&quot;https://apps.apple.com/us/app/1-bit/id6762529497&quot;&gt;free download&lt;/a&gt;, iPhone or iPad, iOS 18.6 or later. Bring a NAS or a &lt;a href=&quot;https://www.ars.md/1-bit/bridge/&quot;&gt;bridge&lt;/a&gt;, a USB DAC, and music you actually like.&lt;/p&gt;</content:encoded><media:content url="https://www.ars.md/og/1-bit-1-2-too-late-too-soon.png" medium="image" type="image/png"/><category>1-bit</category><category>iOS</category><category>DSD</category><category>audio</category><category>release</category></item><item><title>1-bit 1.1, from the heart</title><link>https://www.ars.md/blog/1-bit-1-1-is-out/</link><guid isPermaLink="true">https://www.ars.md/blog/1-bit-1-1-is-out/</guid><description>1-bit version 1.1 is on the App Store: a free open-source bridge for your music library, a stack of hard-won launch-week fixes, and the same bit-exact DSD and PCM playback to your USB DAC.</description><pubDate>Wed, 29 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://apps.apple.com/us/app/1-bit/id6762529497&quot;&gt;1-bit 1.1 is live on the App Store&lt;/a&gt; — same URL, new version. Update on your iPhone and you’ll see it.&lt;/p&gt;
&lt;p&gt;Most of 1.1 doesn’t show up in a bullet list. It’s the rough edges from launch week — pairing flows that didn’t quite hold, scanner behaviour on bigger libraries, the long tail of small fixes you only find once real people start using the thing. The headline addition on top of all that polish: the 1-bit bridge — a free, open-source companion server that gets your library off SMB and onto a transport designed for the app.&lt;/p&gt;
&lt;h2 id=&quot;whats-new&quot;&gt;What’s new&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1-bit bridge, properly launched.&lt;/strong&gt; A free, open-source companion server you can run on a Mac, a Windows PC, a Linux box, or a Raspberry Pi sat next to your music drive. Pair an iPhone in three steps with a QR code from the admin console, and the phone talks to your library over HTTPS instead of SMB. It’s noticeably faster than SMB — first scan after pairing finishes in seconds — and unlike SMB, it works over &lt;a href=&quot;https://tailscale.com/&quot;&gt;Tailscale&lt;/a&gt; or WireGuard, so your library is reachable from anywhere your phone has signal. Same bit-exact audio path on the iPhone side: the bridge ships bytes; the phone still owns DSD, DoP, gapless, all of it. &lt;a href=&quot;https://www.ars.md/1-bit/bridge/&quot;&gt;Bridge page&lt;/a&gt; has the details.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bonjour for bridges.&lt;/strong&gt; Nearby bridges show up automatically in the Add-Source sheet. Type the bearer token (or scan the QR) and you’re paired.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The unglamorous half of 1.1.&lt;/strong&gt; The launch-week fixes that don’t make a feature list — an app-quits-on-launch trace pinned to root services rebuilding on every parent re-eval, a first-launch mass-delete trap on libraries larger than ~500 albums or artists, and auto-revive when iOS 26 silently stops &lt;code&gt;AVAudioEngine&lt;/code&gt; ~600 ms after a tap with a USB DAC attached. Settings also got a proper home: theme override, alternate app icons, ReplayGain on PCM (DSD always stays bit-exact), Bauer crossfeed for headphones, a defer-next-track-preload toggle to quiet shuffle and repeat clicks, and a per-DAC resync-delay override for unknown hardware.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A privacy policy that’s actually been audited.&lt;/strong&gt; Three documents now move together — the in-app screen, the &lt;a href=&quot;https://www.ars.md/1-bit/privacy/&quot;&gt;web policy&lt;/a&gt;, and the &lt;a href=&quot;https://acoseac.github.io/1-bit-bridge/privacy.html&quot;&gt;bridge’s own privacy page&lt;/a&gt;. Zero personal data collected, by design — which inherently complies with the GDPR (EU), the Swiss FADP, and the CCPA. The policies and the code are checked against each other before every release.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Same audio path.&lt;/strong&gt; No EQ, no loudness, no resampling, no dithering, no funny business. DSD goes out as DoP to your USB DAC; PCM goes out at the file’s native sample rate. The phone moves bytes; the DAC does the DAC’s job.&lt;/p&gt;
&lt;h2 id=&quot;the-unromantic-version&quot;&gt;The unromantic version&lt;/h2&gt;
&lt;p&gt;If you already have 1-bit installed: open the App Store and tap update. Your existing SMB shares keep working. Your scan history keeps working. Your Now Playing keeps working. The bridge is additive — you only opt in if you want it.&lt;/p&gt;
&lt;p&gt;If you’re new: it’s a &lt;a href=&quot;https://apps.apple.com/us/app/1-bit/id6762529497&quot;&gt;free download&lt;/a&gt;, iPhone or iPad, iOS 18.6 or later. Bring a NAS, a USB DAC, and music you actually like.&lt;/p&gt;</content:encoded><media:content url="https://www.ars.md/og/1-bit-1-1-is-out.png" medium="image" type="image/png"/><category>1-bit</category><category>iOS</category><category>DSD</category><category>audio</category></item><item><title>Sonogram&apos;s April Glow-Up: A Vinyl, A Particle Storm, and A Landscape That Won&apos;t Stop Pulsing</title><link>https://www.ars.md/blog/sonogram-april-2026-glow-up/</link><guid isPermaLink="true">https://www.ars.md/blog/sonogram-april-2026-glow-up/</guid><description>Two weeks, three new music visualizers, and one absurdly satisfying landscape. Here&apos;s what changed in Sonogram — generative audio art for your eyeballs.</description><pubDate>Mon, 27 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We blinked, two weeks vanished, and somehow the codebase grew three new visualizers, a personality, and a tan. Here’s the field report.&lt;/p&gt;
&lt;h2 id=&quot;three-new-ways-to-look-at-sound&quot;&gt;Three new ways to look at sound&lt;/h2&gt;
&lt;div style=&quot;display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1rem; margin-bottom: 2rem;&quot;&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;🪩 Landscape landed (and then learned to drive)&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;It started life as a humble flat city of bokeh lights. Then it wanted depth. Then traffic. Then a Waze-style chase-cam following a music-reactive car around a tilted MapLibre night map, with shockwaves rippling out on the bass drops. We let it cook. The result is a drone-eye view of a city that breathes with your music. Try it on something with a low end you can feel in your chest.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;💿 The Vinyl visualizer spins back in&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;Because some songs deserve to be watched on a turntable, even a virtual one. Grooves, glow, the whole vibe. Hi-fi nostalgia for an era that mostly existed in our heads.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;✨ A new music-reactive Particle visualizer&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;Spectrum reactivity, shockwaves, bloom, light trails. Basically: dump a track in, get a music video out. Save it as a 4K PNG — yes, that button works now (see below).&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;-the-whole-site-got-a-glow-up&quot;&gt;🧴 The whole site got a glow-up&lt;/h2&gt;
&lt;div style=&quot;border: 1px solid var(--color-brand-border); padding: 1.5rem 1.75rem; margin-bottom: 2rem;&quot;&gt;&lt;p style=&quot;margin: 0;&quot;&gt;A new palette, friendlier loading and empty states, a site-wide accessibility pass, and a refreshed About page that finally admits Landscape exists. Mobile fullscreen no longer bullies your iPhone. The &lt;strong&gt;Save 4K PNG&lt;/strong&gt; button no longer dares you to double-click it.&lt;/p&gt;&lt;/div&gt;
&lt;h2 id=&quot;-we-got-serious-about-being-found&quot;&gt;🔍 We got serious about being found&lt;/h2&gt;
&lt;div style=&quot;border: 1px solid var(--color-brand-border); padding: 1.5rem 1.75rem; margin-bottom: 2rem;&quot;&gt;&lt;p style=&quot;margin: 0;&quot;&gt;&lt;code&gt;robots.txt&lt;/code&gt;, a real sitemap, canonical URLs, structured data — the works. If you’ve been Googling &lt;em&gt;“audio visualizer that doesn’t look like 2008,”&lt;/em&gt; we’d like to introduce ourselves.&lt;/p&gt;&lt;/div&gt;
&lt;h2 id=&quot;️-and-the-boring-but-important-stuff&quot;&gt;🛡️ And the boring-but-important stuff&lt;/h2&gt;
&lt;div style=&quot;border: 1px solid var(--color-brand-border); padding: 1.5rem 1.75rem; margin-bottom: 2rem;&quot;&gt;&lt;p style=&quot;margin: 0;&quot;&gt;Rate limits where they should be. An audit sweep across uploads and steganography. A tighter CSP. A service worker that finally understands cross-origin. Faster admin pagination. Fewer orphaned audio files in the bin out back. The sort of changes nobody asks for and everyone benefits from.&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;Take it for a spin at &lt;a href=&quot;https://sonogram.ars.md&quot;&gt;sonogram.ars.md&lt;/a&gt;.&lt;/p&gt;</content:encoded><media:content url="https://www.ars.md/og/sonogram-april-2026-glow-up.png" medium="image" type="image/png"/><category>Sonogram</category><category>Three.js</category><category>WebGL</category><category>music visualizer</category><category>audio visualization</category><category>generative art</category><category>changelog</category></item><item><title>When &apos;no DAC&apos; isn&apos;t no DAC</title><link>https://www.ars.md/blog/when-no-dac-isnt-no-dac/</link><guid isPermaLink="true">https://www.ars.md/blog/when-no-dac-isnt-no-dac/</guid><description>An iOS app can&apos;t tell whether your Lightning or USB-C output is a $9 dongle or a $250 Audeze Cipher cable. Why Apple&apos;s audio API is opaque, and how 1-bit handles the gap honestly.</description><pubDate>Mon, 27 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem; margin-bottom: 2.5rem; font-size: 0.95rem;&quot;&gt;&lt;div style=&quot;font-family: var(--font-sans); font-weight: 700; font-size: 0.8rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--color-brand-muted); margin-bottom: 0.5rem;&quot;&gt;Who this is for&lt;/div&gt;&lt;p style=&quot;margin: 0 0 0.5rem 0;&quot;&gt;Readers who care about iOS audio plumbing, audiophile gear, and the friction between Apple’s privacy abstractions and high-end-audio expectations.&lt;/p&gt;&lt;p style=&quot;margin: 0;&quot;&gt;&lt;strong&gt;Skip this if&lt;/strong&gt; you write iOS code for a living (a third of this will be familiar) or you don’t own — and don’t plan to own — an external DAC of any kind.&lt;/p&gt;&lt;/div&gt;
&lt;p&gt;A &lt;a href=&quot;https://www.ars.md/1-bit/&quot;&gt;1-bit&lt;/a&gt; user plugged Audeze SINE headphones with the Cipher Lightning cable into an iPhone 13, opened the app, and reported that it “acted as if there was no DAC.”&lt;/p&gt;
&lt;p&gt;A note before we start: both the SINE and the Cipher cable are discontinued — Audeze stopped making the SINE around 2019, and the Cipher cable has been listed as discontinued by retailers since. Used units still circulate on the secondhand market, and a non-trivial number of audiophiles are still using them daily. The specific hardware in this story is fading out, but the technical predicament I’m about to describe applies to every Lightning DAC ever made and to every USB-C audio dongle on shelves today. iPhones moved to USB-C with the iPhone 15, and the iOS-side problem moved with them.&lt;/p&gt;
&lt;p&gt;The Cipher cable &lt;em&gt;is&lt;/em&gt; a DAC. The Lightning port on iPhones is purely digital — every Lightning headphone or audio adapter contains its own digital-to-analog converter, by definition. Audeze’s Cipher goes further: 24-bit / 48 kHz DAC plus a small DSP that runs Audeze’s own EQ for the SINE / iSINE / LCD-i headphones. The cable retailed for about $250 when new.&lt;/p&gt;
&lt;p&gt;So when an audiophile-focused music player tells the user “no DAC,” it’s wrong on the facts. But it’s also reflecting something real: from the app’s perspective, this hardware is indistinguishable from a $9 plastic dongle.&lt;/p&gt;
&lt;p&gt;Here’s why.&lt;/p&gt;
&lt;h2 id=&quot;what-ios-gives-you&quot;&gt;What iOS gives you&lt;/h2&gt;
&lt;p&gt;When an audio device is connected, iOS exposes it through &lt;code&gt;AVAudioSession&lt;/code&gt;. The output port has three string fields a third-party app can read:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;portType&lt;/code&gt;&lt;/strong&gt; — a coarse category. Lightning headphones land on &lt;code&gt;.headphones&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;portName&lt;/code&gt;&lt;/strong&gt; — a human-readable name. iOS reports &lt;code&gt;&amp;quot;Headphones&amp;quot;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;uid&lt;/code&gt;&lt;/strong&gt; — a unique identifier you’d hope was model-specific. iOS reports &lt;code&gt;&amp;quot;Wired Headphones&amp;quot;&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are the strings for the Cipher cable, &lt;em&gt;and&lt;/em&gt; for Apple’s own EarPods Lightning, &lt;em&gt;and&lt;/em&gt; for any random no-name Lightning headset. On iPhone 15 / 16 with USB-C, plug in a $9 USB-C-to-3.5mm dongle or a $1500 portable USB DAC and &lt;code&gt;AVAudioSession&lt;/code&gt; reports the same thin set of generic strings for both. Apple’s audio API treats them as equivalent — identical text labels for hardware that ranges from worthless to high-end.&lt;/p&gt;
&lt;p&gt;There’s a reason for this. Apple’s design philosophy with &lt;code&gt;AVAudioSession&lt;/code&gt; is &lt;em&gt;abstraction&lt;/em&gt;: audio apps shouldn’t have to write custom drivers for every dongle on the market. From the OS’s point of view, all of those devices accept PCM audio at compatible rates over the same connector. The differences (max sample rate, DSP capability, headphone-driver pairing) are hidden because exposing them would push complexity onto every audio-app developer.&lt;/p&gt;
&lt;p&gt;For 99% of audio apps, that abstraction is exactly right. For an app whose entire identity is bit-exact playback to whatever DAC the user has invested in, it’s painful.&lt;/p&gt;
&lt;h2 id=&quot;what-i-tried-before-giving-up&quot;&gt;What I tried before giving up&lt;/h2&gt;
&lt;p&gt;The instinct of a stubborn engineer is to look elsewhere. &lt;em&gt;There must be some way to learn the model.&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;USB descriptors.&lt;/strong&gt; Class-compliant USB DACs identify themselves with a Vendor ID and Product ID — a manufacturer-specific pair of numbers that uniquely names them. On Linux, macOS, or Windows, an app can read these directly. On iOS, IOKit (the framework that exposes them) is unavailable. CoreMIDI doesn’t surface them either. Apple has deliberately left no public API for an app to read VID/PID for an audio device.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;AVAudioSession&lt;/code&gt;’s port descriptions.&lt;/strong&gt; As above — the strings are generic and intentionally so.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bluetooth or AirPlay metadata.&lt;/strong&gt; Doesn’t apply — Cipher is wired, and the same problem exists for wired USB-C DACs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So an iOS app cannot, by any supported route, learn that the connected output is anything more specific than its broad port category.&lt;/p&gt;
&lt;h2 id=&quot;the-mfi-back-channel&quot;&gt;The MFi back-channel&lt;/h2&gt;
&lt;p&gt;There &lt;em&gt;is&lt;/em&gt; a way to see Lightning hardware in detail. It’s just not available to most apps.&lt;/p&gt;
&lt;p&gt;Apple’s MFi program (Made for iPhone) certifies accessories through an authentication chip embedded in the cable. When an MFi accessory is plugged in, iOS reads the chip and learns what the accessory says about itself: manufacturer, model, firmware version, capabilities. An app can read all of this through the External Accessory framework — but only if the app declares the manufacturer’s specific protocol string (something like &lt;code&gt;com.audeze.cipher.audio&lt;/code&gt;) in its &lt;code&gt;Info.plist&lt;/code&gt; under &lt;code&gt;UISupportedExternalAccessoryProtocols&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The catch: the manufacturer has to whitelist the app’s bundle ID before the app can use the protocol string. This isn’t a public API anyone can opt into — it’s a per-vendor, per-app handshake. Audeze’s own &lt;em&gt;Audeze HQ&lt;/em&gt; app has the Audeze protocol string and Audeze’s blessing, so when you plug in the Cipher cable it can read the model, adjust the 10-band EQ stored in the cable’s flash, and update the cable’s firmware. My app, or any third-party music player, can’t see any of that — by design.&lt;/p&gt;
&lt;p&gt;The result is a quiet asymmetry that audiophile customers don’t usually notice: the manufacturer’s app gets the rich hardware view; everyone else gets &lt;code&gt;&amp;quot;Headphones&amp;quot;&lt;/code&gt;. There’s no public API I can write that bridges this gap. I’d need a partnership with Audeze — and even then, only Audeze hardware would benefit. The same pattern exists for USB-C DACs, with a similar set of vendor-specific protocols (Apple has not opened the framework up; they’ve only widened the connector).&lt;/p&gt;
&lt;h2 id=&quot;the-fix&quot;&gt;The fix&lt;/h2&gt;
&lt;p&gt;Once you accept that you cannot identify the DAC, the fix is just: be honest about what you don’t know.&lt;/p&gt;
&lt;p&gt;The original UI had a green &lt;strong&gt;Verified DAC&lt;/strong&gt; card that appears in the now-playing signal-path sheet when the app’s known-DAC list matches by name. For Chord, iFi, FiiO, Topping, AudioQuest’s DragonFly Cobalt, and a handful of others, that match works because their USB DACs report a model-specific name in &lt;code&gt;portName&lt;/code&gt;. For anything iOS labels generically — Cipher among them — no match, no card. The user reads the missing card as “the app doesn’t see my DAC.”&lt;/p&gt;
&lt;p&gt;The fix:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For external outputs without a profile match, render a neutral grey card that says &lt;strong&gt;“External output detected · Standard audio path”&lt;/strong&gt; alongside whatever name iOS gave us (often just “Headphones”). It looks nothing like the green Verified card, deliberately.&lt;/li&gt;
&lt;li&gt;It does &lt;strong&gt;not&lt;/strong&gt; say “bit-exact path active.” That would be a lie. iOS may apply sample-rate conversion silently if the stream exceeds the hardware’s ceiling — and the same UI shows an orange resampling indicator when that happens. A “bit-exact” claim alongside an “iOS is resampling” indicator would be a direct contradiction.&lt;/li&gt;
&lt;li&gt;For DSD playback errors on unprofiled hardware, the error message now leads with the most likely cause: &lt;em&gt;“The connected output likely doesn’t support DSD (some Lightning headphones and USB-C dongles are PCM-only).”&lt;/em&gt; Less mystery, more useful.&lt;/li&gt;
&lt;li&gt;For accessibility, the card combines into a single VoiceOver element with an explicit label — &lt;em&gt;“External output detected: Headphones. Capabilities aren’t in our profile list. DSD may not negotiate.”&lt;/em&gt; — instead of forcing a screen reader user to swipe through icon, title, subtitle, info-glyph one element at a time.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That’s the whole fix. No new API surface, no new heuristic, no fragile guesses. The only thing it really does is replace the &lt;em&gt;absence&lt;/em&gt; of a green card with the &lt;em&gt;presence&lt;/em&gt; of a neutral one.&lt;/p&gt;
&lt;h2 id=&quot;what-i-learned&quot;&gt;What I learned&lt;/h2&gt;
&lt;p&gt;Two things stick.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;First: absence of information is different from absence of the thing.&lt;/strong&gt; The original UI was technically correct — there was no “Verified DAC” card because no profile matched — but the user read it as “no DAC.” The presence of an explicit “External output detected” card uses no new information, and yet completely removes the perception. The most common UX bug in audiophile software, in my experience, is the same as the most common UX bug elsewhere: silently saying nothing when you should say “I’m not sure.”&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Second: Apple’s privacy and security philosophy has audiophile collateral, and the lesson outlives the hardware.&lt;/strong&gt; The MFi walled garden is mostly a good thing — it stops malicious accessories from impersonating each other and stops apps from fingerprinting users by their dongle pile. But it also means that the most basic question an audiophile asks (“is my expensive cable being recognized?”) cannot be answered honestly by any app the manufacturer hasn’t blessed.&lt;/p&gt;
&lt;p&gt;The Cipher cable that started this story is discontinued. The DAC chip in your USB-C dongle, whoever made it, is not. If you’ve ever wondered why a music player says “Output: Headphones” while a piece of expensive hardware blinks proudly on your desk, this is why. The DAC is there. The app just isn’t allowed to know its name.&lt;/p&gt;</content:encoded><media:content url="https://www.ars.md/og/when-no-dac-isnt-no-dac.png" medium="image" type="image/png"/><category>1-bit</category><category>iOS</category><category>audio</category><category>DAC</category><category>MFi</category><category>AVAudioSession</category><category>audiophile</category></item><item><title>1-bit is out</title><link>https://www.ars.md/blog/1-bit-is-out/</link><guid isPermaLink="true">https://www.ars.md/blog/1-bit-is-out/</guid><description>An iOS music player for people who care how their music sounds — bit-exact PCM and DSD over DoP, straight from your NAS to your DAC. Now on the App Store.</description><pubDate>Tue, 21 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Today I shipped &lt;a href=&quot;https://www.ars.md/1-bit&quot;&gt;1-bit&lt;/a&gt;, an iOS music player for people who care how their music sounds.&lt;/p&gt;
&lt;p&gt;Why “1-bit”? Because that’s the number that matters. DSD — Direct Stream Digital, the format behind SACD — encodes audio as a single-bit stream sampled millions of times a second, instead of the 16 or 24 bits per sample you get from PCM (think CDs, FLACs, most of Apple Music). It’s a different way of thinking about digital audio, and it’s what 1-bit is built around.&lt;/p&gt;
&lt;p&gt;The name is also a promise. The app doesn’t resample, doesn’t remix, doesn’t touch your audio on the way to your DAC. What goes in comes out — bit for bit.&lt;/p&gt;
&lt;h2 id=&quot;what-it-does&quot;&gt;What it does&lt;/h2&gt;
&lt;p&gt;Point it at an SMB share on your home network — a Synology, a QNAP, a Mac running File Sharing, whatever you already have — and 1-bit scans your library, reads the tags, pulls the artwork, and builds a browseable catalog of your albums and artists. Plug in a USB DAC and hit play.&lt;/p&gt;
&lt;p&gt;If you don’t run a NAS, drop files into the app’s Documents folder via Files or Finder over USB and it works the same way.&lt;/p&gt;
&lt;p&gt;No accounts. No cloud. No streaming service. Your music, your network, your DAC.&lt;/p&gt;
&lt;h2 id=&quot;bit-exact-and-what-that-actually-means&quot;&gt;Bit-exact, and what that actually means&lt;/h2&gt;
&lt;p&gt;For PCM, 1-bit sends samples straight from the file to the audio engine to your DAC, bypassing the iOS system mixer entirely. For DSD, it reads DSF files and packs the 1-bit stream into DoP (DSD over PCM) frames for the DAC to decode natively.&lt;/p&gt;
&lt;p&gt;This matters most if you own something like a Chord Hugo 2, a Mojo 2, or one of the many USB DACs that can lock to a native DSD carrier. Those DACs sound their best when software gets out of the way. 1-bit gets out of the way.&lt;/p&gt;
&lt;p&gt;It also handles the fiddly parts that trip up most iOS music apps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sample-rate renegotiation when you jump from a 44.1 kHz FLAC to a DSD128 album&lt;/li&gt;
&lt;li&gt;PLL settling time on FPGA-based DACs (the Chord family needs roughly 700 ms to re-lock cleanly — Hugo 2 will go silent without it)&lt;/li&gt;
&lt;li&gt;Gapless playback when consecutive tracks share a format&lt;/li&gt;
&lt;li&gt;DoP marker-phase continuity across track transitions, so your DAC never flickers mid-album&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;who-its-for&quot;&gt;Who it’s for&lt;/h2&gt;
&lt;p&gt;If you have a NAS full of FLACs and DSFs, a good DAC, and a pair of headphones or speakers you actually listen to — this is for you. It’s not trying to replace your streaming service. It’s trying to be the nicest possible way to listen to the music you already own.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://apps.apple.com/us/app/1-bit/id6762529497&quot;&gt;1-bit is on the App Store now&lt;/a&gt;, for iPhone and iPad running iOS 18.6 or later.&lt;/p&gt;
&lt;p&gt;Just press play.&lt;/p&gt;</content:encoded><media:content url="https://www.ars.md/og/1-bit-is-out.png" medium="image" type="image/png"/><category>1-bit</category><category>iOS</category><category>DSD</category><category>audio</category><category>launch</category></item><item><title>What&apos;s new in Sonogram — 11 April 2026 update</title><link>https://www.ars.md/blog/sonogram-april-2026-update/</link><guid isPermaLink="true">https://www.ars.md/blog/sonogram-april-2026-update/</guid><description>A photorealistic turntable, a snowy Alps landscape, an About page, ~130 commits — and a deep rewrite for frame-rate independence.</description><pubDate>Sat, 11 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;It’s been a busy week on Sonogram. Since April 2nd I’ve shipped almost &lt;strong&gt;130 commits&lt;/strong&gt; covering new visuals, a brand new About page, a raft of stability fixes, and some serious under-the-hood work on performance and frame-rate independence. Here’s a tour of the highlights.&lt;/p&gt;
&lt;h2 id=&quot;new-things-to-look-at&quot;&gt;New things to look at&lt;/h2&gt;
&lt;div style=&quot;display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1rem; margin-bottom: 2rem;&quot;&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;A photorealistic turntable visual&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;The biggest visual addition: a PBR-rendered turntable built on Three.js. It started life as a standalone Vite project (&lt;code&gt;turntable-visual/&lt;/code&gt;) and is now integrated directly into the main app — complete with depth-of-field, proper resource cleanup, and real GPU memory management.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;A reimagined Landscape scene&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;The landscape visual was looking a bit gloomy, so we brightened the terrain, raised the sun, tamed the fluid effects, and ultimately transformed it into a snowy Alps-at-dawn scene. The sun glow and sky atmosphere now stay in sync with the sun color parameter.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;Nebula gets a music-reactive sunburst&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;A new lighting effect that pulses with the music, layered on top of the existing volumetric clouds.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;Spectrogram feels alive&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;We added a particle system, orbital camera drift, and a dynamic background — the visual now breathes with the track instead of sitting still.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;An About page&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;Sonogram finally has a proper About page describing the project and crediting contributors, including Domenico Brizi for the volumetric cloud work.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;Mobile hamburger menu&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;Top navigation now collapses cleanly on mobile, with the expand/collapse behavior finally working the way you’d expect.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;things-we-removed&quot;&gt;Things we removed&lt;/h2&gt;
&lt;div style=&quot;border: 1px solid var(--color-brand-border); padding: 1.5rem 1.75rem; margin-bottom: 2rem;&quot;&gt;&lt;p style=&quot;margin-bottom: 1rem;&quot;&gt;Part of polishing the app meant trimming visuals that weren’t pulling their weight. These have all been retired:&lt;/p&gt;&lt;div style=&quot;display: flex; gap: 0.5rem; flex-wrap: wrap;&quot;&gt;&lt;span style=&quot;background: var(--color-brand-text); color: white; font-family: var(--font-sans); font-size: 0.8rem; font-weight: 600; padding: 0.35rem 0.85rem;&quot;&gt;Fire Mandala&lt;/span&gt;&lt;span style=&quot;background: var(--color-brand-text); color: white; font-family: var(--font-sans); font-size: 0.8rem; font-weight: 600; padding: 0.35rem 0.85rem;&quot;&gt;Light Bulb&lt;/span&gt;&lt;span style=&quot;background: var(--color-brand-text); color: white; font-family: var(--font-sans); font-size: 0.8rem; font-weight: 600; padding: 0.35rem 0.85rem;&quot;&gt;Vinyl (superseded by Turntable)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;under-the-hood&quot;&gt;Under the hood&lt;/h2&gt;
&lt;p&gt;This is where a lot of the work went, and it’s the kind of thing you feel more than see.&lt;/p&gt;
&lt;div style=&quot;display: flex; flex-direction: column; gap: 1.25rem; margin-bottom: 2rem;&quot;&gt;&lt;div style=&quot;border: 1px solid var(--color-brand-border); padding: 1.5rem 1.75rem;&quot;&gt;&lt;div style=&quot;font-family: var(--font-sans); font-weight: 700; font-size: 1.1rem; letter-spacing: -0.02em; margin-bottom: 0.75rem; padding-bottom: 0.5rem; border-bottom: 2px solid var(--color-brand-yellow);&quot;&gt;Frame-rate independent smoothing, everywhere&lt;/div&gt;&lt;p style=&quot;margin: 0;&quot;&gt;We hunted down every hardcoded smoothing alpha — frequency averages, per-bin &lt;code&gt;ampTex&lt;/code&gt; writes, beat smoothing, decay helpers — and rewrote them so the look stays the same whether you’re at 60fps or 144fps. The &lt;code&gt;alphaFreq&lt;/code&gt; formula is now self-documenting with explicit target alpha and fps.&lt;/p&gt;&lt;/div&gt;&lt;div style=&quot;border: 1px solid var(--color-brand-border); padding: 1.5rem 1.75rem;&quot;&gt;&lt;div style=&quot;font-family: var(--font-sans); font-weight: 700; font-size: 1.1rem; letter-spacing: -0.02em; margin-bottom: 0.75rem; padding-bottom: 0.5rem; border-bottom: 2px solid var(--color-brand-yellow);&quot;&gt;Audio processing is a single pass&lt;/div&gt;&lt;p style=&quot;margin: 0;&quot;&gt;We collapsed multiple loops into one, deduplicated RMS calculations, and hoisted loop-invariants out of the raymarching and spectrogram animation loops.&lt;/p&gt;&lt;/div&gt;&lt;div style=&quot;border: 1px solid var(--color-brand-border); padding: 1.5rem 1.75rem;&quot;&gt;&lt;div style=&quot;font-family: var(--font-sans); font-weight: 700; font-size: 1.1rem; letter-spacing: -0.02em; margin-bottom: 0.75rem; padding-bottom: 0.5rem; border-bottom: 2px solid var(--color-brand-yellow);&quot;&gt;Memory and resource cleanup&lt;/div&gt;&lt;p style=&quot;margin: 0;&quot;&gt;Mic streams now stop on cleanup, GPU resources are disposed properly, and init races are guarded so you can’t double-initialize a visualizer mid-load. DOF focus calculation was also fixed.&lt;/p&gt;&lt;/div&gt;&lt;div style=&quot;border: 1px solid var(--color-brand-border); padding: 1.5rem 1.75rem;&quot;&gt;&lt;div style=&quot;font-family: var(--font-sans); font-weight: 700; font-size: 1.1rem; letter-spacing: -0.02em; margin-bottom: 0.75rem; padding-bottom: 0.5rem; border-bottom: 2px solid var(--color-brand-yellow);&quot;&gt;THREE.Clock → THREE.Timer&lt;/div&gt;&lt;p style=&quot;margin: 0;&quot;&gt;The landscape and turntable visualizers have been migrated off the deprecated API.&lt;/p&gt;&lt;/div&gt;&lt;div style=&quot;border: 1px solid var(--color-brand-border); padding: 1.5rem 1.75rem;&quot;&gt;&lt;div style=&quot;font-family: var(--font-sans); font-weight: 700; font-size: 1.1rem; letter-spacing: -0.02em; margin-bottom: 0.75rem; padding-bottom: 0.5rem; border-bottom: 2px solid var(--color-brand-yellow);&quot;&gt;Load-time optimizations&lt;/div&gt;&lt;p style=&quot;margin: 0;&quot;&gt;Asset cache-busting now works correctly, &lt;code&gt;esm.sh&lt;/code&gt; is preconnected, and the Vinyl visualization’s quadratic audio gain scaling has been fixed.&lt;/p&gt;&lt;/div&gt;&lt;div style=&quot;border: 1px solid var(--color-brand-border); padding: 1.5rem 1.75rem;&quot;&gt;&lt;div style=&quot;font-family: var(--font-sans); font-weight: 700; font-size: 1.1rem; letter-spacing: -0.02em; margin-bottom: 0.75rem; padding-bottom: 0.5rem; border-bottom: 2px solid var(--color-brand-yellow);&quot;&gt;Refactor: one visualization, one file&lt;/div&gt;&lt;p style=&quot;margin: 0;&quot;&gt;Render and shader code for each visualization has been extracted into its own file, which should make future contributions considerably less painful.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded><media:content url="https://www.ars.md/og/sonogram-april-2026-update.png" medium="image" type="image/png"/><category>Sonogram</category><category>Three.js</category><category>WebGL</category><category>changelog</category></item><item><title>Sonogram: Transforming Sound into Visual Fingerprints</title><link>https://www.ars.md/blog/sonogram/</link><guid isPermaLink="true">https://www.ars.md/blog/sonogram/</guid><description>A deep dive into Sonogram, a full-stack web app that creates interactive 3D visualizations from audio mathematical properties.</description><pubDate>Wed, 01 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Sonogram&lt;/strong&gt; is a full-stack web app that transforms audio files into unique generative visual artworks — &lt;strong&gt;“sonic fingerprints.”&lt;/strong&gt; Upload a song, and it analyzes the audio’s mathematical properties to produce an interactive 3D visualization.&lt;/p&gt;
&lt;h2 id=&quot;what-it-does&quot;&gt;What it does&lt;/h2&gt;
&lt;div style=&quot;display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1rem; margin-bottom: 2rem;&quot;&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;Studio&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;Upload or record audio (MP3, FLAC, WAV, etc.). The app runs DSP analysis (spectral features, MFCCs, chroma, tempo, key detection via &lt;code&gt;librosa&lt;/code&gt;) and renders a real-time 3D visualization using Three.js.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;Gallery&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;Browse, search, and filter saved public visualizations by title, artist, genre, or BPM. Each piece has a shareable page with server-rendered OG tags for social previews.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;Compare Mode&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;Place two tracks side-by-side in a “Mirror Matrix” visualization.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;Admin Dashboard&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;Manage records, toggle visibility, and bulk-export artwork as ZIP files.&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); border-left: 4px solid var(--color-brand-yellow); padding: 1.25rem 1.5rem;&quot;&gt;&lt;strong style=&quot;font-family: var(--font-sans); font-size: 1.05rem; display: block; margin-bottom: 0.5rem;&quot;&gt;Embeddable&lt;/strong&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;Each visualization has an iframe-friendly &lt;code&gt;/embed/&lt;/code&gt; route for external use.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2 id=&quot;tech-stack&quot;&gt;Tech Stack&lt;/h2&gt;





























&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align:left&quot;&gt;Layer&lt;/th&gt;&lt;th style=&quot;text-align:left&quot;&gt;Technologies&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;strong&gt;Backend&lt;/strong&gt;&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;FastAPI, SQLAlchemy (SQLite), Uvicorn&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;strong&gt;Deployment&lt;/strong&gt;&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;Fly.io, Docker&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;strong&gt;Audio Analysis&lt;/strong&gt;&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;librosa, scipy, numpy (Single-pass STFT)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;strong&gt;Frontend&lt;/strong&gt;&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;Vanilla JS (~9K lines), Three.js (WebGL), PWA&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td style=&quot;text-align:left&quot;&gt;&lt;strong&gt;Imaging&lt;/strong&gt;&lt;/td&gt;&lt;td style=&quot;text-align:left&quot;&gt;Pillow (for PNG generation)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;interesting-facts&quot;&gt;Interesting Facts&lt;/h2&gt;
&lt;div style=&quot;display: flex; flex-direction: column; gap: 1.5rem; margin-bottom: 2rem;&quot;&gt;&lt;div style=&quot;border: 1px solid var(--color-brand-border); padding: 1.5rem 1.75rem;&quot;&gt;&lt;div style=&quot;font-family: var(--font-sans); font-weight: 700; font-size: 1.15rem; letter-spacing: -0.02em; margin-bottom: 0.75rem; padding-bottom: 0.5rem; border-bottom: 2px solid var(--color-brand-yellow);&quot;&gt;Steganography&lt;/div&gt;&lt;p style=&quot;margin-bottom: 0.75rem;&quot;&gt;UUIDs are invisibly embedded in artwork PNGs using &lt;strong&gt;LSB (Least Significant Bit) encoding&lt;/strong&gt; in the blue channel.&lt;/p&gt;&lt;div style=&quot;display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 0.75rem;&quot;&gt;&lt;div style=&quot;background: var(--color-brand-surface); padding: 0.75rem 1rem;&quot;&gt;&lt;span style=&quot;font-family: var(--font-sans); font-weight: 700; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--color-brand-muted); display: block; margin-bottom: 0.25rem;&quot;&gt;Magic Bytes&lt;/span&gt;&lt;span style=&quot;font-family: var(--font-mono); font-size: 0.95rem;&quot;&gt;”SONO”&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); padding: 0.75rem 1rem;&quot;&gt;&lt;span style=&quot;font-family: var(--font-sans); font-weight: 700; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--color-brand-muted); display: block; margin-bottom: 0.25rem;&quot;&gt;Location&lt;/span&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;First 320 pixels&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-surface); padding: 0.75rem 1rem;&quot;&gt;&lt;span style=&quot;font-family: var(--font-sans); font-weight: 700; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--color-brand-muted); display: block; margin-bottom: 0.25rem;&quot;&gt;Benefit&lt;/span&gt;&lt;span style=&quot;font-size: 0.95rem;&quot;&gt;Re-upload a PNG to recover metadata — no server lookup needed&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;border: 1px solid var(--color-brand-border); padding: 1.5rem 1.75rem;&quot;&gt;&lt;div style=&quot;font-family: var(--font-sans); font-weight: 700; font-size: 1.15rem; letter-spacing: -0.02em; margin-bottom: 0.75rem; padding-bottom: 0.5rem; border-bottom: 2px solid var(--color-brand-yellow);&quot;&gt;Hardware-aware Rendering&lt;/div&gt;&lt;p style=&quot;margin-bottom: 0.75rem;&quot;&gt;The frontend auto-detects GPU, CPU cores, RAM, and display size. It then classifies the device into &lt;strong&gt;LOW / MEDIUM / HIGH&lt;/strong&gt; tiers to dynamically adjust:&lt;/p&gt;&lt;div style=&quot;display: flex; gap: 0.5rem; flex-wrap: wrap;&quot;&gt;&lt;span style=&quot;background: var(--color-brand-text); color: white; font-family: var(--font-sans); font-size: 0.8rem; font-weight: 600; padding: 0.35rem 0.85rem;&quot;&gt;Resolution scaling&lt;/span&gt;&lt;span style=&quot;background: var(--color-brand-text); color: white; font-family: var(--font-sans); font-size: 0.8rem; font-weight: 600; padding: 0.35rem 0.85rem;&quot;&gt;Bloom strength&lt;/span&gt;&lt;span style=&quot;background: var(--color-brand-text); color: white; font-family: var(--font-sans); font-size: 0.8rem; font-weight: 600; padding: 0.35rem 0.85rem;&quot;&gt;Particle density&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;border: 1px solid var(--color-brand-border); padding: 1.5rem 1.75rem;&quot;&gt;&lt;div style=&quot;font-family: var(--font-sans); font-weight: 700; font-size: 1.15rem; letter-spacing: -0.02em; margin-bottom: 0.75rem; padding-bottom: 0.5rem; border-bottom: 2px solid var(--color-brand-yellow);&quot;&gt;Deterministic Visuals&lt;/div&gt;&lt;p&gt;A &lt;strong&gt;SHA-256 hash&lt;/strong&gt; of the raw audio bytes serves as the seed. This ensures that the same file always produces the exact same visualization, maintaining a 1:1 relationship between sound and art.&lt;/p&gt;&lt;/div&gt;&lt;div style=&quot;border: 1px solid var(--color-brand-border); padding: 1.5rem 1.75rem;&quot;&gt;&lt;div style=&quot;font-family: var(--font-sans); font-weight: 700; font-size: 1.15rem; letter-spacing: -0.02em; margin-bottom: 0.75rem; padding-bottom: 0.5rem; border-bottom: 2px solid var(--color-brand-yellow);&quot;&gt;Three Visualization Modes&lt;/div&gt;&lt;p style=&quot;margin-bottom: 0.75rem;&quot;&gt;Users can switch between three distinct modes in fullscreen:&lt;/p&gt;&lt;div style=&quot;display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 0.75rem;&quot;&gt;&lt;div style=&quot;background: var(--color-brand-text); color: white; padding: 1rem 1.25rem; text-align: center;&quot;&gt;&lt;span style=&quot;font-family: var(--font-sans); font-weight: 700; font-size: 1rem; display: block; margin-bottom: 0.25rem;&quot;&gt;Spectrogram&lt;/span&gt;&lt;span style=&quot;font-size: 0.85rem; color: #a0a0a0;&quot;&gt;Time-frequency heatmap&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-text); color: white; padding: 1rem 1.25rem; text-align: center;&quot;&gt;&lt;span style=&quot;font-family: var(--font-sans); font-weight: 700; font-size: 1rem; display: block; margin-bottom: 0.25rem;&quot;&gt;Crystal&lt;/span&gt;&lt;span style=&quot;font-size: 0.85rem; color: #a0a0a0;&quot;&gt;Abstract 3D geometry&lt;/span&gt;&lt;/div&gt;&lt;div style=&quot;background: var(--color-brand-text); color: white; padding: 1rem 1.25rem; text-align: center;&quot;&gt;&lt;span style=&quot;font-family: var(--font-sans); font-weight: 700; font-size: 1rem; display: block; margin-bottom: 0.25rem;&quot;&gt;Landscape&lt;/span&gt;&lt;span style=&quot;font-size: 0.85rem; color: #a0a0a0;&quot;&gt;Terrain-like surface&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style=&quot;border: 1px solid var(--color-brand-border); padding: 1.5rem 1.75rem;&quot;&gt;&lt;div style=&quot;font-family: var(--font-sans); font-weight: 700; font-size: 1.15rem; letter-spacing: -0.02em; margin-bottom: 0.75rem; padding-bottom: 0.5rem; border-bottom: 2px solid var(--color-brand-yellow);&quot;&gt;Key Detection&lt;/div&gt;&lt;p&gt;The app uses the &lt;strong&gt;Krumhansl-Schmuckler algorithm&lt;/strong&gt;. It correlates chroma vectors against major/minor pitch-class profiles to estimate the musical key with high accuracy.&lt;/p&gt;&lt;/div&gt;&lt;div style=&quot;border: 1px solid var(--color-brand-border); padding: 1.5rem 1.75rem;&quot;&gt;&lt;div style=&quot;font-family: var(--font-sans); font-weight: 700; font-size: 1.15rem; letter-spacing: -0.02em; margin-bottom: 0.75rem; padding-bottom: 0.5rem; border-bottom: 2px solid var(--color-brand-yellow);&quot;&gt;Theme System for AI Art&lt;/div&gt;&lt;p&gt;Includes theme definitions (Bauhaus, Neon Cyberpunk, Aerial Objects) with &lt;strong&gt;ControlNet scale parameters&lt;/strong&gt;, hinting at a future generative AI triptych feature.&lt;/p&gt;&lt;/div&gt;&lt;div style=&quot;border: 1px solid var(--color-brand-border); padding: 1.5rem 1.75rem;&quot;&gt;&lt;div style=&quot;font-family: var(--font-sans); font-weight: 700; font-size: 1.15rem; letter-spacing: -0.02em; margin-bottom: 0.75rem; padding-bottom: 0.5rem; border-bottom: 2px solid var(--color-brand-yellow);&quot;&gt;Hybrid Architecture&lt;/div&gt;&lt;p&gt;The Gallery and Studio operate as a &lt;strong&gt;SPA-style vanilla JS&lt;/strong&gt; application for speed, while individual sonogram pages (&lt;code&gt;/s/{id}&lt;/code&gt;) are &lt;strong&gt;server-rendered&lt;/strong&gt; to ensure rich social sharing metadata and SEO optimization.&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded><media:content url="https://www.ars.md/og/sonogram.png" medium="image" type="image/png"/><category>FastAPI</category><category>Three.js</category><category>DSP</category><category>Generative Art</category><category>Sonogram</category></item><item><title>The Qobuz that Claude Code Built</title><link>https://www.ars.md/blog/qobuz-claude-code/</link><guid isPermaLink="true">https://www.ars.md/blog/qobuz-claude-code/</guid><description>A self-hosted hi-res music manager — 6,980 lines of Python, 408 KB of vanilla JS, zero frameworks, and one very patient AI.</description><pubDate>Tue, 24 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Qobuz Hi-Res Music Manager is a self-hosted web application built for audiophiles who want full control over their high-resolution music collection. It connects to the Qobuz streaming service to search, organize, and play lossless audio — up to 24-bit/192kHz FLAC — all from a sleek, dark-themed interface.&lt;/p&gt;
&lt;h2 id=&quot;what-it-does&quot;&gt;What It Does&lt;/h2&gt;
&lt;p&gt;At its core, the app is an all-in-one music management hub: search Qobuz’s catalog and automatically organize local media files into a local library with proper metadata, cover art, and folder structure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI-Powered Discovery&lt;/strong&gt; — An optional Google Gemini integration lets users search for music using natural language (“dreamy 80s synth-pop with female vocals”) and receive personalized album recommendations based on their favorites.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Multi-Room Casting&lt;/strong&gt; — Stream to AirPlay 2 speakers, Volumio devices, or manually paired endpoints across your home network, all controlled from the browser.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Real-Time Visualizers&lt;/strong&gt; — Two canvas-based audio visualizers: a frequency-mapped blob analyzer and a 3D aurora terrain that morphs with the bass. Both are built with the Web Audio API.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.ars.md/_astro/artist-page.CEi0IgCo_ZIOOod.webp&quot; alt=&quot;The main UI — dark theme, gold accents, artist and album browsing all in one view&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1489&quot; height=&quot;1020&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.ars.md/_astro/ai-about.BJ1iMnGS_2szLgw.webp&quot; alt=&quot;The AI blob visualizer on the home page — a frequency-mapped canvas animation that reacts to the music&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1486&quot; height=&quot;1002&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.ars.md/_astro/visualizer.Cig12ZYj_TjMtT.webp&quot; alt=&quot;The 3D aurora terrain visualizer, built with the Web Audio API — it morphs with the bass in real time&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1491&quot; height=&quot;1018&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;by-the-numbers&quot;&gt;By the Numbers&lt;/h2&gt;





































&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Stat&lt;/th&gt;&lt;th&gt;Value&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;API endpoints&lt;/td&gt;&lt;td&gt;74&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Backend (&lt;code&gt;app.py&lt;/code&gt;)&lt;/td&gt;&lt;td&gt;6,980 lines in a single file&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Frontend (&lt;code&gt;index.html&lt;/code&gt;)&lt;/td&gt;&lt;td&gt;408 KB — the entire SPA in one file, no build step&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;External frameworks&lt;/td&gt;&lt;td&gt;Zero (vanilla JS + CSS, no React/Vue/Angular)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Database&lt;/td&gt;&lt;td&gt;SQLite with 2 tables&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;In-memory caches&lt;/td&gt;&lt;td&gt;600+ entries across albums, artists, and AI results&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Supported cast protocols&lt;/td&gt;&lt;td&gt;AirPlay 2, Volumio, manual HTTP&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2 id=&quot;tech-stack&quot;&gt;Tech Stack&lt;/h2&gt;
&lt;p&gt;Python 3.13 + Flask on the backend, with vanilla HTML/CSS/JS on the frontend — no bundler, no framework, no build step. Just run &lt;code&gt;./run.sh&lt;/code&gt; and the browser opens. The entire app is two files: one Python file and one HTML file.&lt;/p&gt;
&lt;p&gt;Notable dependencies include &lt;code&gt;pyatv&lt;/code&gt; for AirPlay 2 streaming, &lt;code&gt;zeroconf&lt;/code&gt; for network device discovery, &lt;code&gt;mutagen&lt;/code&gt; for audio tag management, and &lt;code&gt;google-genai&lt;/code&gt; for AI features.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.ars.md/_astro/album-detail.CEEqRnjW_1mPxfX.webp&quot; alt=&quot;Album detail view — track listing pulled from both local library and Qobuz, with lossless quality badges&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1492&quot; height=&quot;1020&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.ars.md/_astro/library-browser.VGIPanWH_2k5xyD.webp&quot; alt=&quot;The local library browser — artists, albums, tracks, and now-playing panel side by side&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1489&quot; height=&quot;1012&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;what-makes-it-interesting&quot;&gt;What Makes It Interesting&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Zero-framework frontend&lt;/strong&gt; — 408 KB of hand-written HTML/CSS/JS powers a fully responsive SPA with mobile bottom-nav, real-time progress tracking, and 3D canvas visualizations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Monolithic by design&lt;/strong&gt; — The entire backend is a single 6,980-line Python file — no microservices, no ORM, no async framework — yet it handles background scheduling, device discovery, and multi-client playback coordination.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;AI circuit breaker&lt;/strong&gt; — Gemini errors trigger a 5-minute backoff to prevent log spam — a production-grade resilience pattern in a personal project.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Smart deduplication&lt;/strong&gt; — Case-insensitive artist matching merges variants like “K.D. lang” and “k.d. lang” automatically.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fully offline-capable&lt;/strong&gt; — No CDN dependencies; the entire UI works without internet once loaded.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://www.ars.md/_astro/ai-search.CzjmX4oy_ZUuGRp.webp&quot; alt=&quot;Natural language AI search — type a mood or description and Gemini surfaces matching albums from Qobuz&apos;s catalog&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1492&quot; height=&quot;1026&quot;&gt;&lt;/p&gt;</content:encoded><media:content url="https://www.ars.md/og/qobuz-claude-code.png" medium="image" type="image/png"/><category>projects</category><category>claude-code</category><category>music</category><category>python</category></item><item><title>Hello, World</title><link>https://www.ars.md/blog/hello-world/</link><guid isPermaLink="true">https://www.ars.md/blog/hello-world/</guid><description>The first post on ars.md — what this blog is about and why I&apos;m writing.</description><pubDate>Mon, 23 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Every blog starts somewhere. This is mine.&lt;/p&gt;
&lt;h2 id=&quot;why-write&quot;&gt;Why write?&lt;/h2&gt;
&lt;p&gt;Writing forces clarity. When I try to explain something I half-understand, I quickly discover the edges of my understanding. A blog is a place to build that clarity in public — slowly, post by post.&lt;/p&gt;
&lt;p&gt;I’m not trying to be comprehensive or authoritative. I’m trying to think better, and writing is how I do that.&lt;/p&gt;
&lt;h2 id=&quot;what-this-blog-is&quot;&gt;What this blog is&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;ars.md&lt;/strong&gt; is a place for notes on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Technology I’m building or using&lt;/li&gt;
&lt;li&gt;Ideas worth examining&lt;/li&gt;
&lt;li&gt;Observations that won’t fit anywhere else&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Posts will vary in length. Some will be quick notes, others longer essays. I’m not committing to a schedule — just to writing when I have something worth saying.&lt;/p&gt;
&lt;h2 id=&quot;comments&quot;&gt;Comments&lt;/h2&gt;
&lt;p&gt;At the bottom of each post, you’ll find a comments section powered by &lt;a href=&quot;https://giscus.app&quot;&gt;Giscus&lt;/a&gt; — which means comments are actually GitHub Discussions. You’ll need a GitHub account to comment, which keeps things civil without needing moderation infrastructure.&lt;/p&gt;
&lt;p&gt;I’ll try to reply to every comment.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;That’s it for the introduction. More soon.&lt;/p&gt;</content:encoded><media:content url="https://www.ars.md/og/hello-world.png" medium="image" type="image/png"/><category>meta</category><category>writing</category></item><item><title>On Minimalism in Software</title><link>https://www.ars.md/blog/on-minimalism-in-software/</link><guid isPermaLink="true">https://www.ars.md/blog/on-minimalism-in-software/</guid><description>Why I keep coming back to simpler tools, smaller APIs, and less code.</description><pubDate>Fri, 20 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;There is a certain pleasure in software that does one thing well.&lt;/p&gt;
&lt;p&gt;Not because complexity is inherently bad — some problems are genuinely complex and deserve complex solutions. But most problems aren’t. And most software accretes complexity not because the problem requires it, but because it’s easier to add than to remove.&lt;/p&gt;
&lt;h2 id=&quot;the-cost-of-features&quot;&gt;The cost of features&lt;/h2&gt;
&lt;p&gt;Every feature a piece of software has is a feature that must be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Designed&lt;/li&gt;
&lt;li&gt;Implemented&lt;/li&gt;
&lt;li&gt;Tested&lt;/li&gt;
&lt;li&gt;Documented&lt;/li&gt;
&lt;li&gt;Maintained&lt;/li&gt;
&lt;li&gt;Understood by the user&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The last item is the one we most often forget. A feature that confuses users is worse than no feature at all, because it occupies mental space without providing value.&lt;/p&gt;
&lt;h2 id=&quot;what-minimalism-actually-means&quot;&gt;What minimalism actually means&lt;/h2&gt;
&lt;p&gt;Minimalism in software isn’t about having fewer lines of code, or a smaller bundle size, or fewer dependencies (though those are often useful side effects). It’s about having fewer &lt;em&gt;concepts&lt;/em&gt; — fewer things the user has to understand to accomplish their goal.&lt;/p&gt;
&lt;p&gt;A minimal interface is one where the user’s mental model and the software’s model are the same thing. When they diverge, friction appears.&lt;/p&gt;
&lt;h2 id=&quot;the-refactoring-that-never-happens&quot;&gt;The refactoring that never happens&lt;/h2&gt;
&lt;p&gt;The problem with complexity is that it compounds. Adding a feature to a simple system is easy. Adding a feature to a complex system requires understanding the existing complexity first, which is expensive, which makes each new feature more likely to add yet more complexity rather than integrate cleanly.&lt;/p&gt;
&lt;p&gt;The refactoring that would restore simplicity keeps getting deferred — it’s never the most urgent thing, until suddenly the system is incomprehensible to everyone including the people who built it.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;I don’t have a prescription for how to avoid this. I just find myself increasingly drawn to things that are simple. And increasingly suspicious of things that aren’t.&lt;/p&gt;</content:encoded><media:content url="https://www.ars.md/og/on-minimalism-in-software.png" medium="image" type="image/png"/><category>software</category><category>design</category></item></channel></rss>