Skip to content

Latest commit

 

History

History
76 lines (61 loc) · 3.62 KB

File metadata and controls

76 lines (61 loc) · 3.62 KB

Audio ground truth

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.

Method

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.

Result (3 clients, 150s, 228 paired clicks)

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.

Scope and limits, stated

  • 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.

A measurement bug worth recording

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.

Reproduce

cd sync-harness
./scripts/make-clicktrack.sh          # regenerate the asset (ffmpeg)
npx tsx src/audio-truth-run.ts        # 3 browsers, 150s