The Problem
If you’ve ever tried to capture system audio in an Electron app on macOS, you know how frustrating it can be. The typical approach looks something like this:
- Fork Existential Audio’s BlackHole (assuming licensing allows it)
- Customize and codesign the driver
- Package an installer or setup script
- Handle automatic input switching or create an aggregate device
- Ship all this alongside your app
From there, you can capture system audio using the WebRTC getUserMedia
API by requesting it as a microphone input.
It works — barely. But it’s a fragile, ugly solution that’s painful for both developers and users:
- Native drivers are hard to ship and maintain
- Codesigning and customization are non-trivial, especially for newcomers
- Automatically changing a user’s audio devices is guaranteed to annoy them
- With Apple’s recent changes to audio routing in macOS, you now have to write extra code just to make your app work across all platforms
A “Better” Solution
Some clever developers have sidestepped this mess by writing native Swift apps that capture system audio, then stream it to their Electron app or save it to disk.
It’s smarter — but it’s still a hassle:
- It’s another binary to build, sign, and maintain
- It doesn’t integrate cleanly with WebRTC or the Web Audio API
- You’re stuck passing audio through the main process rather than piping it directly into the renderer
If you’re going down this route in 2025, I highly recommend checking out Nick Payne’s excellent audiotee. If it had existed seven years ago, I might’ve cried tears of joy. It’s that good — clean API, easy setup, just works.
But I still wasn’t satisfied.
My Ah-Ha Moment
I wanted something cleaner. No drivers. No native apps. Just Electron, shipping with zero extra binaries, working out of the box.
So I did what any desperate developer would do: I started digging through the Chromium source code.
And it paid off.
Turns out, the Chromium team has already built robust system audio loopback support into the browser across platforms. They’ve done amazing work. The only catch? A lingering macOS screen capture bug has kept this feature out of reach for Electron apps — if you’re capturing video.
But if you only need audio, the fix is surprisingly simple. Chromium hides the functionality behind a few internal flags.
The Hidden Chromium Flags
Here’s the magic:
MacLoopbackAudioForScreenShare
MacCatapSystemAudioLoopbackCapture
(for Core Audio)MacSckSystemAudioLoopbackOverride
(for ScreenCaptureKit)PulseaudioLoopbackForScreenShare
(for Linux + PulseAudio)
On macOS 13+, enable both MacLoopbackAudioForScreenShare
and MacSckSystemAudioLoopbackOverride
. Then, in your setDisplayMediaRequestHandler
callback, pass "loopback"
as the audio
param.
Here’s the code that made it work.
The first time I saw it actually capturing system audio — no driver, no hacky input switching, just a clean WebRTC stream — my Apple Watch said my heart rate hit 118 BPM.
I was that excited.
This breakthrough changes the game for Electron developers building audio tools. It’s native, it’s simple, and it’s finally possible to ship truly seamless audio capture on macOS without all the legacy baggage.
Curious to try it yourself? Check out the open-source repo at https://github.com/alectrocute/electron-audio-loopback and install the package with npm i electron-audio-loopback
.
Discuss this post on Hacker News and Reddit.