A tiny, native system-tray player for internet radio (MP3/AAC) streams. Point it at any http(s) MP3 or AAC stream, then one click to play or pause, hover to see the current track, with proper OS media-control and media-key integration and automatic reconnect — written in Rust, packaged for every major desktop.
▶️ / ⏸️ One-button play / pause from the tray icon or its menu.- 🔗 Bring your own stream — plays any http(s) MP3 or AAC stream you
configure (AAC-LC; HE-AAC/aacPlus is not supported).
.plsplaylist URLs work too: the entries are tried in order until one connects. - 🎵 Now-playing track shown on hover (tray tooltip) and in the menu, parsed live from the stream's ICY metadata.
- 🎛️ System media controls — MPRIS on Linux, System Media Transport Controls on Windows, and the Now Playing center on macOS.
- ⌨️ Media-key support (Play/Pause/Stop keys) on all three platforms.
- 🔁 Automatic reconnect with exponential backoff when the stream drops.
- 🚀 Start on login — opt-in toggle right in the tray menu.
- 🪶 Lightweight & self-contained — a pure-Rust audio pipeline (no GStreamer, no VLC), so installation is a single small binary.
- 🖥️ Cross-platform — Linux (GNOME, KDE, and other StatusNotifier trays), macOS, and Windows.
| OS | Architectures | Packages |
|---|---|---|
| Linux | x86_64, aarch64 |
.deb, .rpm, Homebrew, Arch PKGBUILD, Nix flake |
| macOS | Apple Silicon + Intel | universal .dmg (.app), Homebrew cask + formula |
| Windows | x86_64 |
NSIS installer .exe + portable .exe |
Download the assets for your platform from the latest release.
sudo apt install ./whirr_*_linux_amd64.deb # or _arm64.debRuntime dependencies (libgtk-3, libayatana-appindicator3, libasound2) are
pulled in automatically.
sudo dnf install ./whirr-*.x86_64.rpm # or .aarch64.rpmbrew install samuelb/tap/whirrInstalls the plain whirr binary from the latest release. On macOS the
--cask variant below gives you a proper .app in Applications instead.
# download PKGBUILD from the latest release assets, then:
makepkg -si(An AUR package whirr can be published from the same PKGBUILD.)
# run without installing
nix run github:samuelb/whirr
# or add to a flake and install packages.default
nix profile install github:samuelb/whirrHomebrew (recommended):
brew install --cask samuelb/tap/whirrbrew upgrade --cask whirr then tracks new releases. The tap lives at
samuelb/homebrew-tap and is
refreshed automatically on every release.
Manual:
- Download
whirr-macos.dmg, open it, and drag Whirr to Applications. - On first launch, right-click the app and choose Open (the build is ad-hoc signed; a Developer ID signature can be added in CI).
The app runs as a menu-bar item with no Dock icon.
- Installer: run
whirr-setup-<version>.exe(adds a Start-menu shortcut and an uninstaller). - Portable: just run
whirr-windows-x64.exe.
cargo install --path .
# or
cargo build --release # binary at target/release/whirrLinux build dependencies:
sudo apt-get install libgtk-3-dev libayatana-appindicator3-dev libasound2-dev pkg-configLaunch Whirr (from your app launcher or the command line). A tray icon appears.
First run: no stream is configured yet, so the tray shows No stream URL
configured. Choose Set stream URL… from the tray menu, enter the URL of
the MP3 or AAC stream (or a .pls playlist pointing at one) you want to play,
and playback starts right away. To also start playing automatically on later
launches, enable Autoplay on startup in the menu.
- Left-click the icon (Windows/macOS) to toggle play/pause.
- Right-click (or left-click on Linux) opens the menu:
- the current track (or Not playing),
- Play / Pause,
- Set stream URL… (opens the configuration dialog),
- Start on login (toggle),
- Autoplay on startup (toggle),
- About,
- Quit.
- Media keys and your desktop's media widget / lock-screen controls work too.
whirr --selftest # connect, decode silently for ~10s, print status
RUST_LOG=debug whirr # verbose loggingThe stream URL is set from the tray menu (Set stream URL…) and takes effect immediately — no restart needed. The dialog is native on every platform: a GTK dialog on Linux, an AppleScript dialog on macOS, and an input box on Windows.
All settings (including the URL) are also stored as TOML; the file is created on first launch and watched while the app runs — edits are applied automatically within a couple of seconds, no restart needed. Location:
| OS | Path |
|---|---|
| Linux | ~/.config/whirr/config.toml |
| macOS | ~/Library/Application Support/whirr/config.toml |
| Windows | %APPDATA%\whirr\config\config.toml |
# The http(s) MP3/AAC stream (or .pls playlist) to play. There is no default;
# set it here or via the "Set stream URL…" tray menu item.
stream_url = "https://example.com/stream.mp3"
# Output volume, 0.0–1.0
volume = 1.0
# Start playing automatically on launch (off by default)
autoplay = false
# Kept in sync with the OS "start on login" state
autostart = false HTTP (reqwest) ──► ICY de-mux ──► bounded channel ──► Symphonia decode ──► Rodio/CPAL ──► speakers
│ │
│ └─► StreamTitle ──► tray tooltip + MPRIS/SMTC metadata
└─► auto-reconnect with backoff
reqweststreams the radio feed withIcy-MetaData: 1.icysplits interleaved metadata from audio and reports theStreamTitle.symphoniadetects and decodes MP3 or AAC (ADTS) frames;rodio/cpalplay them.souvlakiprovides MPRIS / SMTC / MediaRemote and media keys.tray-icon+taoprovide the tray and event loop;auto-launchhandles start-on-login.
Playback state uses a generation counter so pausing is instant and a single
worker ever feeds the audio device; the worker owns the reconnect loop, so the
stream self-heals without UI involvement. See src/player.rs for details.
cargo test # unit tests (offline)
cargo fmt --all # format
cargo clippy --all-targets
cargo run # run locally- CI (
.github/workflows/ci.yml): fmt, Clippy (warnings-as-errors) and tests on Linux, macOS and Windows for every push/PR. - Release (
.github/workflows/release.yml): triggered manually (workflow_dispatch); the version is derived from the conventional commits since the last release (override with the major/minor/patchbumpinput). It bumps the version metadata, builds and publishes.deb+.rpm(amd64 & arm64, via nfpm), Linux binary tarballs, a universal macOS.dmg+ binary tarball, a Windows installer + portable exe, a source tarball with a pinned ArchPKGBUILD, and a consolidatedchecksums.txt. It then refreshes the Homebrew tap — cask and formula — via the tap's reusable publish workflow (requires aHOMEBREW_TAP_TOKENrepo secret with write access to the tap).
Licensed under the MIT license (LICENSE-MIT).
Unless you explicitly state otherwise, any contribution you intentionally submit for inclusion shall be licensed as above, without any additional terms or conditions.