CompanyCompany · Products, experiments, and the work behind them.

PartyParty

Public beta · started June 2026

Play audio from your Mac. Guests scan a QR code and listen on their own headphones.

Native audio capture and LL-HLS streaming connect a Mac to iPhone browsers, over venue Wi-Fi or a cloud relay. No guest app or account. Public beta; mixed-path timing is still being resolved.

Visit product · View source · Join the TestFlight beta

Scope: Product strategy, interaction and visual design, native macOS app, Go streaming and relay architecture, guest web experience, Cloudflare infrastructure, testing, distribution, and operations

PartyParty turns one Mac into the sound system for a room. The host plays music or DJs as usual. Guests scan a QR code and listen through their own iPhones and headphones, at their own volume, with no app or account. Native iPhone playback keeps the music going when Safari moves to the background or the phone goes into a pocket.

I built the Mac app, streaming services and guest experience with AI agents. Native audio capture, low-latency HLS and direct or relayed delivery meet in a deceptively simple interaction: join and listen. It is a free public TestFlight beta for Apple silicon Macs on macOS 26 or later. Mixed-path timing remains unresolved: the latest recorded real-player measurements put direct and relayed guests about 2.2 seconds apart. This is not yet a synchronized-playback promise.

A room that explains how to connect

The Mac picks one path for the whole room rather than letting each phone decide. checking while the network evidence is still unsettled, direct when guests reach the Mac over venue Wi-Fi and the internet is up, local when they reach it and the internet is not, relay when a guest proves the Wi-Fi isolates devices from one another and the host has enabled the cloud leg, and no_path when guests cannot reach the Mac and there is no internet to fall back on. no_path is a real product state with a stable reason code and a sentence explaining what to change, not a spinner that never resolves. The decision is a pure function of five observations plus the host's own preference, which is why it can be covered exhaustively by a 21-case table rather than tested by anecdote.

Evaluating it without a Mac

Hosting requires a compatible Mac; the guest experience runs on an iPhone. Without that setup, the illustrated walkthrough explains the host, scan and listen steps. It uses an inert demo QR and an animated playback screen, not a recording of a native session or a measured delay. The product site explains the beta. The source repository is public under a source-visible, all-rights-reserved notice, not an open-source reuse license. The artifacts below make the engineering inspectable, including the dated playback logs with the failing run kept alongside the passing one.

Choosing the iPhone playback engine

The requirement is that audio survives a locked screen. The first approach was hls.js with ManagedMediaSource on iPhone, and it produced repeated audible seek skips on real phones. Browser-level tests could not hear them, which is the recurring shape of this project's failures: the thing that broke was audible and the thing that was green was computed.

The decision is one engine per platform, fixed in code rather than configurable. Apple devices use native HLS and AVPlayer because it plays smoothly and keeps running while the phone is locked. hls.js stays only as the fallback for browsers with no native HLS, and it is never fetched on the Apple join path, so its 404 KB never enters a guest's first load. Chrome, Firefox and Edge on iOS route to native as well, since they are WebKit shells whose Managed Media Source support synced noticeably worse in the field. No URL parameter or remote setting can move an Apple device onto ManagedMediaSource.

What settled the shape of the intervention was a live-set measurement. Healthy native playback is left passive; the page never seeks or rate-steers it. The single correction is bounded: a visible phone that stays at least 750 ms past the room target for three consecutive measurements gets a fresh native attachment, never while Safari is hidden or the phone is locked. That correction used to carry a two-per-session lifetime cap. After two ordinary stalls the aligner went permanently dead and a phone was observed 16.7 seconds behind at a live test on 2026-08-04. The cap was removed and the correction now stays available for the whole set, with spacing that is only the mechanical time an attachment needs to land and be re-measured.

Authoring the three-second cushion

Every guest, direct or relayed, has to target the same instant. Each chunk of audio carries the wall-clock time it was captured, the room publishes one delay D, and a chunk stamped T is played at T plus D. Direct and relayed listeners then agree by construction instead of by coincidence.

Two ways of authoring that cushion failed, and both passed the test suite first. Stretching PART-HOLD-BACK to 2.9 seconds pointed outside the region where parts exist, and AVPlayer responded by snapping a listener to the oldest edge of the window. Putting EXT-X-START in the media playlist without PRECISE measured 25.00 seconds from the live edge, because AVPlayer applied the offset from the wrong end. Both were green-tested geometry builds and both failed on real phones within minutes on 2026-08-05.

The shipping geometry was recovered by reading the code from the era that had actually run live for weeks rather than paraphrasing it, and it is now fixed:

Setting Value
Codec AAC-LC
Bitrate 320 kbps stereo
Segment duration 500 ms
Part duration 150 ms
Retained segments 48
PART-HOLD-BACK floor 0.9 seconds
Room target 3 seconds

The attachment pin is EXT-X-START:TIME-OFFSET=-3.000,PRECISE=YES in the multivariant playlist, and the modest hold-back stays in the media playlists. One declared target for every phone on every path, never adaptive, never per-phone, never tracking listener conditions, because a room-wide value that tracks room health moves healthy players whenever a struggling peer changes it. Stability over immediacy was my call: the cushion is sized so ordinary Wi-Fi stalls pass silently instead of reaching ears. The rule that followed is that any change to the delay, the hold-back, the attachment position or the segment and part shape has to pass a real-AVPlayer soak before the build is uploaded anywhere. An early soak reported 3.11 seconds, but the proxy behind that measurement was subsequently rejected as attachment evidence. The real guest-path measurements below are the relevant result.

What counts as a measurement

To soak a candidate manifest without shipping it, a bench proxy serves the live stream through a rewritten playlist. On 2026-08-11 it was run three times: with the attachment pin in the multivariant playlist, with the pin in the media playlist, and with no pin anywhere. The three runs measured 3.12, 3.20 and 3.16 seconds, and all three logged SOAK PASS. A measurement that returns the same answer when the mechanism under test is removed entirely is measuring the harness, not the manifest; the synchronous proxy itself makes the player fall behind.

So the proxy was struck as evidence for attachment position, and the requirement became measurement through the real guest path. Run that way the same day, the relay path logged SOAK PASS at a 3.33 second median and the direct path logged SOAK FAIL at 1.17 seconds against the declared 3.00 second target. Both logs are kept in the repository. I did not lower the declared target to match the accidental direct number, because the cushion is what absorbs venue Wi-Fi stalls, and I did not delete the failing log to make the receipts directory look uniform.

Where an agent was wrong

This project is agent-built, and its two worst defects were not in the product. They were in the things that were supposed to check the product, and both printed the word PASS.

The Cloudflare Worker smoke suite registered every test into an array and then never called one. It printed "PASS 14 worker smoke tests" against any worker.js at all, including a build with a route deleted that a test asserted on, and it was believed for months. It now actually runs them, and the comment recording the failure sits directly above the loop that fixes it.

The first version of the playback soak reported SOAK PASS for a run where the AVPlayer never connected: status FAILED and position 0.00 on every sample. PASS is now an assertion rather than the absence of a complaint. A run passes only if it proves it attached, that at least four fifths of its samples were ready to play, that position advanced, that a live-edge measurement existed and drifted by less than 1.5 seconds, that the median sat within 0.75 seconds of the declared target, and that nothing ever moved backward. That harness is what produced the FAIL log above instead of a fourth agreeable PASS.

The pattern in both is a counter wearing the word PASS. Agents produce those readily and confidently, and the only defense that has worked here is to make the passing condition an explicit assertion about physical behavior, then run it against a stream I can hear.

My contribution

I conceived and built PartyParty end to end and hold the decisions the evidence forced: the fixed room target and stability over immediacy, native AVPlayer as the only Apple engine, no_path as an honest state instead of a retry loop, the removal of the correction cap, the rejection of the bench proxy as evidence, the refusal to lower the declared target to match an accidental measurement, and the refusal to restore synchronized-playback language before a supervised mixed-path field test exists. I also hold the boundaries agents do not cross here: no build reaches TestFlight or the App Store without my explicit ask, and no timer, hook, or watcher may publish one.

Reproducible artifacts

Every claim above has a file. In github.com/tetrisgm/partyparty:

  • docs/relay-architecture.md: the shipped network design, the mode table, and the boundary where the Cloudflare Worker stops. It handles bootstrap, anonymous certificate coordination and relay registration, and never receives playlists, media parts, guest photos, or listener requests.
  • internal/relay/mode.go: the five room modes, the evidence struct, and decide written as a pure function so the whole state machine is table-testable. internal/relay/mode_test.go holds the 21-case decision table and a separate test asserting that no_path is produced only for genuinely unserviceable networks.
  • internal/schedule/schedule.go: the single implementation of the room delay that both the direct and relay paths import, with PartHoldBack = 0.9 and Delay = 3.0 as constants. It lives in its own package because when only the direct path applied the rewrite, relayed guests silently ran on the muxer's raw hold-back.
  • docs/low-latency-setup.md: the fixed profile above, and the standing statement that unit, contract, WebKit and proxy tests are necessary but cannot prove audible native playback.
  • scripts/soak-playback.swift and scripts/soak-playback.sh: the muted headless AVPlayer soak, one sample every five seconds, with the verdict function that has to be satisfied before the word PASS is printed.
  • docs/receipts/: the dated logs. soak-20260811-relay-PASS-3.33s.log, soak-20260811-direct-FAIL-1.17s.log, and the three bench-proxy runs soak-20260811-benchproxy-pin-master-3.12s.log, soak-20260811-benchproxy-pin-media-3.20s.log and soak-20260811-benchproxy-pin-none-3.16s.log that showed the proxy could not tell the pin positions apart.
  • cloudflare/worker.js: the anonymous broker. Each install gets a memorable hostname under party.partyparty.party, an unproxied 60-second A record pointing it at the Mac's current LAN address which the Worker verifies by reading back, a per-install relay publish credential compared in constant time, and the shared wildcard certificate for *.party.partyparty.party. scripts/issue-wildcard.sh obtains and renews that certificate through a Cloudflare DNS-01 challenge and publishes it for installs to fetch. This is what lets an iPhone open a private LAN address over HTTPS with no profile to install and no certificate warning to accept.
  • web/listener.html: the guest page, including the engine selection and the outlier correction with its thresholds.
  • cloudflare/test/smoke.mjs: the 25 Worker smoke tests, and the comment about the years they did not run.
  • docs/HANDOFF.md and AGENTS.md: the operating contract, including which physical checks are still owed.

Operating limits

Stated the way the product states them, not the way a launch page would prefer.

  • Delay. Direct and relayed listeners can currently land at different delays, so the product says low-latency and not synchronized or real time. The 2026-08-11 measurement puts the direct path near 1.17 seconds and the relay near 3.33 seconds against a declared three-second target, which is roughly 2.2 seconds apart for a mixed room, and the current per-phone bootstrap permits both paths in one Wi-Fi-plus-cloud room. It is reproduced and not fixed. Synchronized-playback language stays out of public copy until a candidate passes the pre-upload real-AVPlayer soak and a supervised mixed-path field test, and that condition is a line item on the release checklist rather than a memory.
  • Internet. The claim is "works without internet after setup", never an unconditional "no internet". A cached valid certificate on its own is not sufficient: the venue's resolver still has to map the secure hostname to the Mac's current LAN address, and the app observes that resolver directly rather than assuming it. A travel router the host controls can supply that record alongside a DHCP reservation. When there is no internet relay and secure local resolution is proven impossible, the app reports no_path and may offer an explicitly degraded HTTP link to the exact current LAN IP, on which locked-screen playback is not promised. The measurements above were taken on a development network with a real AVPlayer; the physical checks still owed are named in docs/HANDOFF.md, and they include joining through the wildcard certificate on a real iPhone and running a relayed party with many phones on venue Wi-Fi.
  • Scale. The Mac uploads each part once whether five or five hundred guests listen, so its work is essentially independent of attendance, but origin egress and connection capacity are the real scaling dimensions and large-venue scale is not claimed until it is physically demonstrated.
  • Relay is music-first by design. Media has strict priority, guest photos take a capped and throttled secondary path, and videos never enter the relay at all. Direct and local modes keep the full local media experience. No relay component creates a durable party, recording, replay, or public event page, and a quiet room's rolling state disappears.
  • Track recognition. A development-signed build matched real tracks from system output including catalog artwork, so live catalog recognition works in a properly provisioned development build. Recognition in the fielded TestFlight or App Store signing lane is not verified, and the product labels it that way.
  • Status. A free public TestFlight beta with no permanent pricing promise, for Apple silicon Macs on macOS 26 or later with recent-iPhone guests.