The music player macOS deserves. No Electron. No Catalyst. No subscription. No cloud. Just your music, played beautifully.
Most Mac music players are either abandoned, Electron-wrapped, or stripped-down streaming clients that barely tolerate local files. Bòcan is the answer to all three: a native Swift 6 app built entirely around owning and enjoying your own library, the way iTunes used to before it became a content storefront.
- True gapless playback with nanosecond
AVAudioTimeanchoring. Classical transitions, live albums, and DJ mixes play as the artist intended, with zero silence and zero clicks. - 10-band graphic EQ, bass boost, stereo expander, binaural crossfeed, and a peak limiter, a full DSP chain between your files and your ears.
- ReplayGain applied at playback time; analyses missing tags in the background using EBU R128 loudness.
- Configurable crossfade (0–12 s), playback speed (0.5×–2.0×) with pitch correction, and a sleep timer that fades gracefully rather than cutting mid-note.
- Everything AVFoundation handles natively: FLAC, ALAC, AAC, MP3, WAV, AIFF, CAF, M4A.
- The awkward ones too, via an integrated FFmpeg bridge: Ogg Vorbis, Opus, APE (Monkey's Audio), WavPack, DSD. No plug-ins, no extra installs.
- Folder-based, non-destructive. Point it at your music directory and it indexes without touching a single file.
- Live FSEvents watcher picks up new or changed files automatically; mtime + fingerprint deduplication keeps your library clean.
- AcoustID fingerprinting against MusicBrainz. Identify any track, preview every proposed tag change side-by-side with what you have now, tick the fields you want to update, and apply. Pick the exact release you own (original pressing, reissue, territory variant) from everything MusicBrainz knows, and optionally write the deep identifiers too: ISRC, track totals, and Picard-compatible MusicBrainz IDs.
- In-app tag editor with multi-track batch editing, embedded cover-art drag-and-drop, and undo.
- Three-pane browser: Albums grid, Tracks list, Artists view, and Recently Added. The Artists, Genres, and Composers views each flip between a plain list and a grid of cover-art cards (a 2x2 mosaic of that collection's albums), and the choice is remembered per section and mirrored in the View menu. Open a genre or composer and you can browse it as songs or as a grid of its albums. Sort the Albums, Artists, Genres, Composers, and Podcasts lists the way you think (by name, count, year, unplayed episodes, and more), and your choice is remembered across launches. The Artists view can also filter down to album artists only, so one-song guest credits and "feat." appearances stop crowding the list; the funnel icon fills whenever the filter is active, and the choice sticks across launches. Double-click any album cover (Albums grid, a genre or composer's albums, an artist page) to play it on the spot; a single click still opens its track list. And just start typing over any view: the first letter jumps straight into the search box and the query builds from there (
⌘Ffocuses it too). Browse views also remember where you were: scroll halfway down a list, open something, and clicking back returns you to the same spot rather than the top. Compilations stay as one album: files flagged as a compilation group under a single "Various Artists" entry even when the Album Artist tag is empty. - Smart Playlists built from a rule editor, compiled to live SQL and updated automatically as your library changes. Sort by several keys with priorities (say artist, then track number, then title), and click a column header in the track list to re-sort on the fly.
- Live playlist sync: add or remove tracks from a manual playlist while it is playing and Up Next updates immediately. Sequential mode keeps your position and reorders around it; shuffle mode drops removed tracks and slots new ones in.
- Sortable playlists: click a column header to sort a playlist for browsing. Your hand-arranged order is kept underneath, and a "Playlist Order" button returns to it (drag-to-reorder pauses while a column sort is active, so the saved order is never scrambled).
- Import / export M3U, PLS, and XSPF playlists, with fuzzy track matching on import.
- Real-time visualisers: spectrum bars, oscilloscope, Halo (a breathing spectrum ring with beat ripples), Cascade (a scrolling spectrogram waterfall), Starfield (a frequency-coloured warp field that stretches into streaks on the beat), and Nebula (a luminous gas cloud whose churn, drifting wisps, and pressure waves all follow the music), dockable or full-screen with
⌘⇧F. All six are GPU-rendered with Metal, which moves the drawing off the CPU and onto per-pixel shaders: they stay smooth at full-screen and native Retina resolution, and can sustain effects a CPU renderer could not (Nebula's churning gas is a live shader simulation, not a looping texture). They ship with six colour palettes, including a music-steered Drift and a magnitude-to-heat Thermal ramp. Hover any visualizer to switch the mode or palette in place with the on-screen steppers. - Mini Player in four layouts (Strip, Compact, Square, Visualizer) with always-on-top mode.
- Mac-native feel: gentle trackpad haptics when you love, rate, seek, or release the volume slider, and a soft window cross-fade when swapping between the Mini Player and the main window. The system Now Playing controls (Control Center and the media-key overlay) show the current track with its album artwork. Respects Reduce Motion and the system trackpad haptics setting.
- Last.fm, ListenBrainz, and Rocksky scrobbling, offline-resilient with Keychain auth and a dead-letter queue.
- Subsonic / Navidrome / Airsonic servers as first-class sources alongside your local library. Federated search across every server, per-server status dots, offline banners with one-tap retry,
⌘⇧1–⌘⇧9to jump straight to a server, and drag a streamed song straight into Up Next. - Podcasts - subscribe by URL or search across Podcast Index and Apple iTunes; RSS and Atom feeds; per-episode resume; download episodes individually or in bulk, plus optional per-show auto-download of new episodes; show notes (with safe HTML rendering) reachable from the player bar; Podcasting 2.0 chapters (jump-to and read), transcripts, host/guest credits (
podcast:person), and recommended-show shelves (podcast:podroll); variable speed and skip intervals. - Internet radio - a first-class station catalog in the sidebar. Add stations by hand, paste a
.pls/.m3uplaylist URL into Add Station's "Stream or Playlist URL" field and every stream inside it is offered as a station, or import/drop a playlist file and its stream URLs become stations instead of missing tracks. While a station plays, the ICY now-playing title takes the player's title line (the station moves to the artist line, and the system Now Playing widget follows), and the player's info button opens the station sheet with the live stream facts: container, codec and profile (LC vs HE-AAC), sample rate, channels, claimed bitrate, and whether the station sends titles at all. Those facts, plus the station's own name, genre, and homepage from its ICY headers, are remembered per station so the info sheet works offline too. Live streams play through the FFmpeg decoder with automatic reconnect, the scrubber stays honest about live audio having no timeline, and station idents never pollute your scrobbles or play history. - Library Summary : open Tools -> Library Summary (
⌘⇧Y) for six tabs of answers about your collection, all computed locally from data the app already keeps:- Basic Info : the totals at a glance: songs, albums, artists, album artists, and the full playback duration in days, hours, minutes, and seconds.
- Library Hygiene : the problems worth fixing: track-number gaps (you have 1, 2, 3, 5, 6), albums exploded into one-track shards by inconsistent tags, implausible or contradictory years, albums missing artwork, year, or MusicBrainz IDs, and tracks whose files vanished from disk. Every offender is one click from its album with the guilty song selected.
- Audio Quality : codec, sample-rate, bit-depth, and bitrate distributions; the lossless-to-lossy split by song count and by gigabytes (they tell different stories); mixed-format albums; true-peak clipping; and Analyse Provenance, which decodes sample windows from every lossless file, looks for the hard spectral shelf a lossy encoder leaves behind, and files a confidence-weighted Suspected Transcodes report ("87% confident · shelf at 16 kHz"). Suspected, never accused: nothing is deleted or retagged, verdicts persist, and new or changed files re-queue automatically.
- Collection Shape : a release-year histogram; ownership versus listening per decade as two aligned strips, because the gap between what you own and what you play is the interesting bit; the one-track guest long tail against your ten-plus-album lifers; the longest and shortest songs and the longest album; and average album length by decade, rising when the CD arrives and falling after.
- Listening Behaviour : import your Last.fm export and years of scrobbles come home, matched to your library by MusicBrainz ID and normalised titles, kept patiently unmatched where the music is gone, and never inflating a local play count. On top of that history: utilisation and a play-count Gini coefficient (most people are north of 0.7 and horrified), skip-rate delete candidates with average bail-out points, dormant favourites, the "never got past the singles" abandoned-albums report, an hour-by-weekday listening heatmap on the thermal palette, a discovery-rate chart of new artists per month, and the artists who only exist for you in December.
- Podcasts : the unheard backlog in hours with a projection against your actual listening rate ("about 19 months", or the unflinching "at your current rate: never"); dead feeds with one-click unsubscribe; the downloaded-but-never-played hoard per show with its disk bill; completion rates with the average abandonment point ("62% finished, abandons around 14:03" tells you where the ad break is); episode length creep by year; time-to-listen medians separating news from comfort; and Reap Now to delete listened episodes still hoarding disk months later. Both actions are confirmed, and reaping never runs on its own.
- In-app Log Console : open Help -> Log Console (
⌘⇧L) to tail every log line since launch, filtered by level or category, with free-text search, live tailing, pause and resume, copy to clipboard, and export to a.logfile. Diagnose a problem without ever leaving the app.
- Phone Sync serves your library, read-only, to a paired Android phone over your local network. One way: the Mac is the source, the phone keeps a copy, and nothing is ever written back to your files or your database.
- Turn it on in Settings > Phone Sync. The Mac advertises itself on your LAN via Bonjour; there is no cloud relay, no account, and nothing leaves your network.
- Pairing takes seconds. The Mac shows a six-digit code, you type it on the phone, and the Mac asks for one final "Pair with this phone?" confirmation. The code verifies the connection; trust is only granted when you confirm on the Mac.
- Sync profiles decide what travels: everything, or just the playlists you choose, with downloaded podcast episodes optional (show cover art included). A live size estimate shows the total before the phone starts pulling.
- A "Ready to sync" line in the same pane counts up while the Mac prepares your library in the background (each track gets a fingerprint the phone uses to verify downloads). On a big library the first pass takes a little while; tracks appear on the phone as they become ready.
- Locked down by default: mutual TLS with certificates pinned in both directions. Revoking a phone in Settings blocks it at the TLS layer on its very next connection.
- The companion app is Bòcan for Android; both sides implement the same open sync protocol.
I've tried hard to ensure Bòcan is fully navigable without a mouse or a screen:
- VoiceOver-first track list : each row announces "Title, Artist, Album, Duration" as a single spoken sentence rather than reading every column individually.
- Live now-playing announcements : when the track changes, VoiceOver speaks the new track name automatically, without you having to navigate to the transport bar.
- Full keyboard navigation with logical focus sections and no focus traps.
- Dynamic Type throughout : every label, badge, and table cell scales with your macOS text size setting.
- Album cells, artist rows, and genre chips are grouped with
.combineso VoiceOver reads them as single logical elements. - Transport controls carry state via
accessibilityValue(e.g. "Shuffle, on, button") so you always know what you're toggling. - EQ band sliders report their gain in the format "80 Hz, +3.0 dB" rather than a raw number.
- Swift 6 strict concurrency :
@MainActorisolation,Sendableeverywhere it matters, zero data races by design. - Twelve clean SPM modules with no upward imports and their own test suites.
- 80% line-coverage gate in CI; the build fails if coverage drops.
- GRDB 7 with typed repositories, FTS5 full-text search, and
ValueObservation-based reactive streams. - XcodeGen project generation : no hand-edited
.pbxprojfiles in the repo. - CodeQL, SwiftLint, SwiftFormat, and Dependabot on every pull request.
Naming things is hard. I was up from midnight to almost 3 am trying to figure out a name that wasn’t already taken for another app. In the end, I couldn’t find one that was free, so I went with the name I’ve used on the internet since the internet existed.
Bocán (Old Irish) means a young male deer, the root of which is cognate with the Welsh "boc" and the Breton "bok", both meaning "buck".
Bòcan (Scottish Gaelic, roughly BAW-khan) is a hobgoblin or a household spirit.
Bòcan curates your music library while you sleep. The short version is that computers don't like ò, so the binary, bundle, and repository all use bocan:
| Property | Value |
|---|---|
| Display name | Bòcan |
| Binary / package name | bocan |
| Bundle ID | io.cloudcauldron.bocan |
| Log subsystem | io.cloudcauldron.bocan |
| Minimum macOS | 15.0 (Sequoia) |
- File management tools: move, rename, and organise files directly from the library.
- Remote control: iPhone and Android apps to control playback on the Mac; the Mac stays the source.
- Accessibility: ongoing work; VoiceOver coverage is good but there's more to do.
Contributions and ideas welcome. Open an issue or a pull request.
Download the DMG from bocan.app/download or the GitHub Releases page. Open it, drag Bòcan to Applications, done.
Homebrew: add the tap once, then install:
brew tap bocan/bocan
brew install bocanSparkle keeps the app current automatically. brew upgrade --greedy bocan does the same from the terminal if you prefer.
See DEVELOPMENT.md for prerequisites, environment setup, the build system, common make targets, FFmpeg and fpcalc notes, and contribution guidelines.
| Module | Description |
|---|---|
Observability |
Structured logging (AppLogger), in-process ring buffer (LogStore), log console support, telemetry, MetricKit |
Persistence |
GRDB schema + migrations, repositories, reactive ValueObservation |
AudioEngine |
AVFoundation + FFmpeg decoder graph, ring buffer, DSP chain, playback actor |
Metadata |
TagLib read/write, cover-art extraction, LRC lyric parser |
Acoustics |
Chromaprint fingerprinting, AcoustID + MusicBrainz lookup |
Subsonic |
Subsonic / Navidrome / Airsonic client, capability detection, Keychain credentials |
Library |
Folder scanner, FSEvents watcher, conflict resolver, cover-art cache |
Playback |
Queue, history, shuffle strategies, gapless + crossfade scheduler, MPNowPlaying, sleep timer |
Scrobble |
Last.fm / ListenBrainz / Rocksky providers, offline-resilient scrobble queue |
Podcasts |
Feed refresh, episode downloads, Podcast Index / iTunes search, Podcasting 2.0 extras |
SyncServer |
Phone Sync: pairing, trust store, mutual-TLS HTTP server, manifest + file serving |
UI |
SwiftUI views, view models, mini player, settings, theming, snapshot tests |
See LICENSE.
Much love to the giants whose shoulders I stand on:
- Christopher Snowhill, maintainer of of the excellent Cog. The patron saint of native macOS music players and Cog is the direct spiritual ancestor of what I've built here.
- Fabrice Bellard, creator of FFmpeg, and QEMU, and TinyCC, and a dozen other earth-shaking projects. The reason Bòcan can play Ogg, Opus, APE, WavPack, and DSD without making users install plugins.
- Christopher "Monty" Montgomery and the Xiph.Org Foundation: Vorbis, FLAC, Opus, Speex. The entire free codec stack on which lossless audio on the open web is built.
- Peter Pawlowski, maintainer of foobar2000, solo since 2002. Even though no foobar code touches Bòcan, foobar's design DNA (modular DSP, smart playlists, gapless, format breadth) is the spiritual blueprint of every audiophile-flavour music player that has followed.
- Lukáš Lalinský, creator of Chromaprint and AcoustID. The entire fingerprinting and auto-tagging pipeline in Bòcan exists because of his work. Also a long-time TagLib contributor. A one-person open-source music-metadata hero.
- Scott Wheeler and the TagLib contributors: without TagLib, I'd be reimplementing ID3v2.4 + Vorbis comments + APE tags + MP4 atoms by hand.
- Robert Kaye and the MetaBrainz Foundation: MusicBrainz, ListenBrainz, Cover Art Archive. Bòcan's auto-tagging, lookup, and open scrobbling all sit on this stack.
- Gwendal Roué, maintainer of GRDB.swift. Quietly one of the best-maintained Swift libraries in existence, solo for over a decade. My entire persistence layer is built on it.
- Justin Frankel, co-creator of Winamp (back in his Nullsoft days), later REAPER, NSIS, gnutella, and most of Cockos. Winamp basically invented the modern desktop music player. Anyone building one stands on this work whether they realise it or not.
- Jean-Marc Valin, primary author of Opus (and Speex before that), one of the great open codec achievements of the last twenty years.
- Yonas Kolb, creator of XcodeGen. Saved me (and everyone else) from hand-editing
.pbxprojand weeping in merge conflicts. - Nick Lockwood, author of SwiftFormat. Keeps my codebase from looking like it was written by seven different people on seven different keyboards.
- JP Simard, creator of SwiftLint, the linter that enforces my
.swiftlint.ymlon every commit. - Mathieu Dubart, builder of SwiftSonic and Cassette. Bòcan's entire Subsonic / Navidrome / Airsonic stack rides on SwiftSonic; without it, talking to OpenSubsonic servers would be a hand-rolled mess.
- Tsiry Sandratraina, builder of Rocksky. The third leg of Bòcan's scrobbling stool, alongside Last.fm and ListenBrainz.
- Nuno Dias, creator of FeedKit. Bòcan's entire podcast layer parses RSS, Atom, and the Podcasting 2.0 namespace through FeedKit; without it I'd be hand-rolling feed XML and weeping into my
podcast:persontags.