Chorus: gathering the web apps you never close into one window that stays quiet.
Twenty services in one window, and most of the design spent on the harder half: unread counts that stay inside their space, a switch over every badge and notification, and two accounts of the same service sealed from each other.
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.
Those applications live in a scatter of browser tabs and menu-bar apps. 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, Franz, or a browser with enough tabs open. Most tidy the services and leave the noise, so the unread counts 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. I designed and built everything it became.
That shell had the first sketches of spaces, badges, and keyboard shortcuts in it. I set it down for a few weeks, came back in June, and built the rest, down to the release pipeline that signs and notarizes each build before it ships. I have maintained it in public since, folding in fixes as people put it to use.
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. Only the active one is on screen, so the window holds the six things this part of the day needs rather than the 20 the whole day might. 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.
Chorus gives you a switch for each. A message still arrives. Whether it reaches you is a decision you have already made.
Two switches rather than one looks like indecision until you follow where the two things come from. An unread count is something Chorus goes and reads off a page on a timer. A notification is something the page hands over the moment it happens. They arrive by different routes, only one of them is ever written down, and a single control over both would have to be wrong about one of them.
Quiet hours make the decision on a schedule, 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, so 20 open things do not cost 20 things' worth of memory, battery, or interruption.
The keyboard beats the mouse a hundred times a day.
Press ⌘K, type a few letters, and Enter takes you there.
The list narrows on a space name as readily as a service name, so the same few keystrokes reach either. 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.
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 the two sit side by side, each signed in, and neither has to be logged out to reach the other.
Three times, I cut the feature that demos best.
Cutting them kept the app light, and honest about what it is.
The reasoning was the same each time. An app you keep open all day is judged on what it costs you while you are not thinking about it, so the lighter path wins even when the heavier one reads better in a feature list.
| what I cut | why the obvious version was wrong | what shipped |
|---|---|---|
| heavy ad and tracker blocking | A roster of messaging apps has little to block, and doing it properly means fetching and compiling large blocklists inside a tight memory budget. Steep cost, thin benefit. | Nothing, at first. A lighter domain-level version earned its way in later. |
| shrinking the base font to fit more on screen | It breaks fixed-size images and per-service layouts, and small-by-default is the opposite of accessible. It was filed as an accessibility feature and would have cost accessibility. | A uniform zoom that scales a whole service, larger or smaller. |
| bundling a browser engine | Shipping a second engine buys control over rendering and pays for it in download size and memory, plus a security surface I would then be on the hook to patch. | The system’s own web engine, patched by the operating system. |
Shipped 2 July 2026, signed and notarized from day one.
Then improved steadily as real users, people I had never met, found what I had missed.
A Sparkle update feed went in with that first version, so it patches itself without a trip to a download page. The releases since have been mostly repair and refinement.
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 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.
It shipped because my testing ran mostly on a single account per service, so 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.