Skip to content

Repository files navigation

murmur

murmur

A cafe in your menu bar

Build macOS 14+ MIT


Background noise that sounds like somewhere, not like something. Chatter you can't quite make out, an espresso machine behind it, and two controls: which cafe, and where you're sitting in it — because in a real room, distance means darker and wetter, not just quieter.

No dock icon, no window, no content. Click the cup, pick a cafe and a seat, get on with it. Play and Stop sit at the top of the menu.

murmur's menu: currently playing, cafe, seating, volume

Which cafe

Cafe What you hear
Isle of Wight Indistinct talk, rain outside
São Paulo Busy chain cafe, steady hum
Indianapolis Dense and close, binaural

Three real field recordings, all public domain — sources in CREDITS.md. Each is downloaded the first time you pick it, verified against its SHA-256, and cached from then on. Switching cafes keeps the current one playing until the next is on disk, so you get a swap rather than a gap.

Where you're sitting

Each cafe, three rooms. The seat isn't a different file — it's the same recording heard from somewhere else, shaped in real time.

Seat What you hear
Counter Espresso hiss, chatter up close
Corner table Voices blur together
By the window Muffled, with traffic outside

Moving from the counter to the window drops the low-pass corner from 12 kHz to 2 kHz, roughly triples the reverb mix, trims the level, and fades in a quiet bed of generated street noise. Measured across the span: 22 dB of high-frequency energy gone, about 6 dB of overall level.

Build & run

./run.sh

Builds with xcodebuild (ad-hoc signed) into build/ and launches the menu bar app. Requires macOS 14+.

Or in Xcode:

open murmur.xcodeproj      # ⌘R

How it works

  • A real NSMenu on a real NSStatusItem — so it behaves like every other menu bar item, including holding the menu bar open inside another app's fullscreen. Native checkmarks, native ⌘, and ⌘Q. The volume slider lives in an NSMenuItem's custom view, which is something NSMenu allows and SwiftUI's menu bridge does not.
  • The checkmark is the play state, not a stored selection — the tick sits next to whatever is actually sounding, and a dash means it's still downloading. So picking a cafe starts it, and picking the one already playing stops it. There's an explicit Play/Stop at the top of the menu too, since that gesture is something you'd have to know rather than discover.
  • Settings (⌘,) holds launch-at-login, auto-play, update checks and the audio credits — so the dropdown stays a thing you use rather than a thing you configure. It's a hand-built NSWindow hosting SwiftUI, because SwiftUI's own Settings scene can't reliably be opened from an app with no menus.
  • Scenes are downloaded on first play and cached in the app's sandbox container, pinned by SHA-256 — which is re-checked on every play, so a truncated file repairs itself. The app itself ships with no audio.
  • Gapless looping straight off disk. scheduleFile is queued twice and re-armed on each .dataConsumed callback, so memory stays flat instead of holding a whole recording as PCM.
  • The loop seam is baked into the file, not crossfaded at runtime: tools/prep-scene.sh lays a recording's head over its own tail, so wrapping to frame 0 is already smooth.
  • The room is the node graph — AVAudioUnitEQ → AVAudioUnitReverb, with a pink-noise AVAudioSourceNode for the street. Seat changes are parameter writes, so they take effect instantly and without a click.
  • One PlaybackState enum is the single source of truth for the UI, bridged into AppKit with withObservationTracking.
  • Native macOS. No dependencies, no Electron.

Making a scene

tools/screen-candidate.sh raw-recording.flac --window 300
tools/prep-scene.sh raw-recording.flac cafe.m4a --start 35 --duration 300

The screener reports whether a recording is a usable bed — how much of its energy is chatter, how steady it is, how often something jumps out — and finds the calmest stretch, printing the prep-scene.sh line for it. The prep step trims, high-passes away handling noise, normalises to −23 LUFS so it sits under whatever else you're listening to, crossfades the loop seam, encodes AAC, and prints the SHA-256 to paste into Scene.swift. Sources and licences go in CREDITS.md.

Verifying it

The room DSP can be measured rather than guessed at:

swiftc -O murmur/RoomDSP.swift tools/render-seats.swift -o /tmp/render-seats
/tmp/render-seats cafe.m4a /tmp/out

renders eight seconds per seat offline through the app's real graph. See AGENT.md for what the numbers should look like.

Licence

MIT for the code. The cafe recordings are public domain and credited separately in CREDITS.md.

About

A cafe in your menu bar — ambient cafe sound for macOS, with a seat control that places you in the room

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages