Chorus: gathering the web apps you never close into one window that stays quiet.
A native Mac app that keeps mail, chat, and documents in one place, and keeps them from shouting for your attention.
The tools you never close are the ones that never stop asking for you.
Mail and team chat, a doc or two, a calendar you keep half an eye on. They sit open all day, and any one of them can interrupt whenever it likes.
A few applications never get closed. Mail, team chat, a document or two, whatever the day is running on. They live in a scatter of browser tabs and menu-bar apps, and any one of them can reach for your attention at any moment. No single interruption is the problem. The tax is a dozen open things at once, each of them holding a claim on you.
Apps that gather your services into one window already exist. Rambox and Franz do it, and so does a browser with enough tabs open. Most of them tidy the services and leave the noise, or add to it, so the unread counts simply pile up in one place instead of several. Chorus started from the other end. Collecting the services was the easy part. Keeping the collection calm took the rest of the work.
I built Chorus to quiet my own desktop, then designed it into a product.
It started in April 2026 as a rough shell that could show a few services in one window. The design and the product it grew into are mine.
Chorus began in April 2026 as a rough prototype: a shell that could show a few web services in one window, with the first sketches of spaces, badges, and keyboard shortcuts. I set it down for a few weeks, came back to it in June, and built it out from there. All of it is mine, the design decisions and the code alike.
From that shell I designed and built the parts that make the app worth keeping open. I worked out how notifications are governed, designed the switcher you move through it with, and added per-service dark mode, zoom, and session isolation. I wrote the interaction design, built it in Swift and SwiftUI, and set up the release pipeline that signs and notarizes each build before it ships. I have maintained it in public across 11 releases.
Spaces group your services the way your day already splits them.
Work in one space, personal in another. One is active at a time, and unread counts add up per space instead of into a single undifferentiated pile.
A space is a named group of services: a work space with team chat, mail, and a document tool; a personal space with the messaging apps friends actually use. One space is active at a time, so the window holds the six things this part of the day needs rather than the 20 the whole day might. Each space keeps its own aggregate unread count, which means a personal message never lights up the work space, and the reverse holds too.
A wall of services must not become a wall of noise.
Every service arrives assuming it may badge you and notify you at will. The design gives you the last word over both.
Left alone, each service assumes it may show a badge and post a notification whenever it likes. Chorus turns that assumption around. Every service has two independent switches, one for its unread badge and one for macOS notifications, with mute as a master override that silences both at once. A message still arrives. Whether it reaches you is a decision you have already made.
Quiet hours make that decision on a schedule. You set a start and an end, and the app holds notifications between them, wrapping correctly past midnight so a window like 10 p.m. to 7 a.m. behaves the way you mean it. Services you are not looking at hibernate: after a spell idle, a service drops out of memory and stops polling, so 20 open things do not cost 20 things' worth of memory, battery, or interruption.
The fastest path between two services is the keyboard.
Press ⌘K, type a few letters of a service or a space, and Enter takes you there. In a tool you live in all day, the keyboard is the main way through.
Moving between services is a keystroke. The switcher opens over the window, and a few letters of a service name or a space name narrow the list; the arrow keys move the highlight and Enter jumps you there. For an app you keep in front of you all day, reaching for the mouse to change context is friction you feel a hundred times. The switcher makes the quick path the obvious one.
Two accounts of the same service should never see each other.
A second Gmail gets its own sealed session, so cookies and unread counts never bleed from one account into the other.
People run two of the same thing: a work Gmail and a personal one, two Slack workspaces. Inside a single browser those share cookies and sign-in state, so a second account becomes a chore of constant logouts, or a leak waiting to happen. In Chorus each service gets its own isolated session, sealed off from the rest. Two Gmail accounts sit side by side, each signed in, and neither one's cookies or unread counts reach the other. It is the quiet kind of trust: you notice it only when you realize you never had to think about it.
The clearest design decisions were about what to leave out.
Twice, the obvious feature was the wrong one. Cutting them kept the app light and honest about what it is.
Two features were cut on purpose. The first was heavy ad and tracker blocking. It demonstrates well, but a roster of messaging apps has little to block, and doing it properly means fetching and compiling large blocklists inside a tight memory budget. The cost was steep and the benefit thin, so it stayed out until a lighter, domain-level version could earn its place later, which it did.
The second was an accessibility shortcut: shrinking the base font size to fit more on screen. That breaks fixed-size images and per-service layouts, and small-by-default is the opposite of accessible. A uniform zoom that scales a whole service, larger or smaller, does the honest version of the same job. Choosing the system's own web engine over bundling a second browser followed the same instinct. Take the lighter path, and respect the machine it runs on.
It ships, and it keeps shipping.
11 public releases across the first 11 days, notarized and self-updating, with fixes driven by reports from people I have never met.
The first public version shipped on 2 July 2026. It was signed and notarized, and a Sparkle update feed lets it patch itself without a trip to a download page. Ten more releases followed over the next 11 days, up to 1.5.2 on the 13th. Most of that run was repair and refinement: reader mode, per-service and automatic dark mode, moving a service between spaces, and a steady list of smaller fixes.
Some of those fixes came from strangers. A user on Reddit reported a crash on launch that struck after deleting a space, which traced back to a stale reference left in saved data. Version 1.5.1 stopped new deletions from causing it, and 1.5.2 repaired stores already broken by the earlier bug, backing up the data file first. Shipping in public means strangers hit the bugs, and answering them is part of the deal.
-
v1.0.0 · 2 July 2026
First public release. Signed, notarized, and self-updating from the first day.
-
v1.4.0 · 9 July 2026
Reader mode, automatic dark mode, and a light domain-level ad and tracker blocker, the restrained version of the feature cut earlier.
-
v1.5.0 · 12 July 2026
Move a service from one space to another with a right-click.
-
v1.5.2 · 13 July 2026
Fixed a launch crash a stranger reported, and stopped badge counts leaking between two accounts of the same service.
The bug I regret is the one that broke the app's main promise.
Badge counts leaked between two accounts of the same service. That is exactly the boundary the product sells, and I caught it late.
For a while, two accounts of the same service could show each other's unread counts. Isolation was the whole promise, and here was the one place it leaked. It shipped because my testing ran mostly on a single account per service, so the multi-account case, the exact case the feature exists for, went unexercised until someone hit it. I fixed it in 1.5.2, but the lesson is about order. When a product's core claim is a boundary, the first test should try to cross it. I now start from the case that would embarrass me, then work back toward the happy path.