Every drift figure in this repo ultimately comes from a player's own
currentTime. That is the instrument grading itself: if the readout were
biased, every client would agree on a wrong answer and the harness would
report perfect sync. This track measures the signal instead.
A same-origin click track (1kHz burst every 2s, generated by
sync-harness/scripts/make-clicktrack.sh) is played by three real Chrome
clients under the normal sync engine. Each page routes its media element
through a Web Audio graph where an AudioWorklet — running on the audio
thread, one RMS per 128-sample quantum (~2.7ms at 48kHz) — detects each
click and reports it. The main thread maps that instant onto the page clock
the harness shares across all browsers.
Two clients emitting the same physical click at the same instant produce the
same stamp, so the difference between their stamps is the real output
skew, computed without consulting any player clock. Click identity comes
from currentTime (clicks are 2s apart; ±100ms of player error cannot
mislabel one) — coarse identification, precise measurement.
This required an Html5Adapter, which is independently useful: the same
estimator and controller drive YouTube and a <video> element unchanged,
so the sync core is demonstrably player-agnostic rather than YouTube-shaped.
| measurement | P50 | P95 | max |
|---|---|---|---|
| audio truth (physical output skew) | 13.2ms | 64.0ms | 288ms |
| player-API skew at the same instants | 7.9ms | 19.0ms | — |
| calibration residual |audio − API| | 6.9ms | 58.9ms | — |
The player-API measurement understates true output skew — by ~5ms at P50
and ~45ms at P95 here. Sync is genuinely tight (13ms median between what two
speakers actually emit), but the API-derived numbers this project publishes
are optimistic, and now the size of that gap is measured rather than
assumed. The residual is what a listener experiences and a currentTime
diff cannot see: decode and output buffering between the reported position
and the moment sound leaves the device.
- Measured on the HTML5 path, not YouTube. A cross-origin iframe cannot be tapped by Web Audio, which is precisely why this needed a same-origin asset. The systematic gap is a property of browser media playback, so it is indicative for the YouTube path — but it is not a measurement of it.
- One machine, one audio device. Constant offsets (device output latency, worklet quantum) are identical per client and cancel in the pairwise difference. Across heterogeneous hardware they would not.
- Resolution floor ~2.7ms (worklet quantum), with a 400ms re-arm so one 30ms burst cannot double-trigger.
The first run reported a 1.75-second audio skew against a 6ms
player-clock agreement. That is physically impossible for players agreeing
that closely, so the instrument was wrong, not the engine: AudioContext. currentTime does not advance while the context is suspended (autoplay
policy suspends it until playback), and the clients launch 1.5s apart — the
error was exactly the stagger. The fix is getOutputTimestamp(), whose
(contextTime, performanceTime) pair already accounts for suspension and
output latency.
The lesson generalises: a result that is too large to be real is as informative as one that is too good. Both are the instrument talking.
cd sync-harness
./scripts/make-clicktrack.sh # regenerate the asset (ffmpeg)
npx tsx src/audio-truth-run.ts # 3 browsers, 150s