1010< body >
1111
1212< header >
13- < a href ="#panel-minimal " class ="skip-link "> Skip to demo </ a >
13+ < a href ="#card-grid " class ="skip-link "> Skip to demos </ a >
1414 < h1 > @adasp/latency-test · Demo</ h1 >
1515 < p > Validates the built IIFE bundle — load component via < code > <script></ code > tag</ p >
1616 < span id ="activity-indicator " class ="activity-idle "> </ span >
@@ -22,20 +22,59 @@ <h1>@adasp/latency-test · Demo</h1>
2222</ div >
2323
2424< div id ="demo-section " hidden >
25- < div class ="tablist " role ="tablist " aria-label ="Demo patterns ">
26- < button role ="tab " id ="tab-minimal " aria-selected ="true " aria-controls ="panel-minimal " tabindex ="0 "> Minimal</ button >
27- < button role ="tab " id ="tab-multi " aria-selected ="false " aria-controls ="panel-multi " tabindex ="-1 "> Multi-Run</ button >
28- < button role ="tab " id ="tab-context " aria-selected ="false " aria-controls ="panel-context " tabindex ="-1 "> Context Share</ button >
29- < button role ="tab " id ="tab-mode " aria-selected ="false " aria-controls ="panel-mode " tabindex ="-1 "> Mode Toggle</ button >
30- < button role ="tab " id ="tab-lifecycle " aria-selected ="false " aria-controls ="panel-lifecycle " tabindex ="-1 "> Lifecycle</ button >
31- < button role ="tab " id ="tab-debug " aria-selected ="false " aria-controls ="panel-debug " tabindex ="-1 "> Debug Mode</ button >
32- < button role ="tab " id ="tab-gain " aria-selected ="false " aria-controls ="panel-gain " tabindex ="-1 "> Host Gain</ button >
25+
26+ < details id ="audio-info " hidden >
27+ < summary > Audio Session Info</ summary >
28+ < table >
29+ < thead class ="sr-only ">
30+ < tr >
31+ < th scope ="col "> Property</ th >
32+ < th scope ="col "> Value</ th >
33+ </ tr >
34+ </ thead >
35+ < tbody > </ tbody >
36+ </ table >
37+ </ details >
38+
39+ < div id ="card-grid " class ="card-grid ">
40+ < button class ="demo-card " data-panel ="panel-minimal " aria-controls ="panel-minimal " aria-expanded ="false ">
41+ < span class ="card-title "> Minimal</ span >
42+ < span class ="card-desc "> Single test run, simplest integration</ span >
43+ </ button >
44+ < button class ="demo-card " data-panel ="panel-multi " aria-controls ="panel-multi " aria-expanded ="false ">
45+ < span class ="card-title "> Multi-Run</ span >
46+ < span class ="card-desc "> Multiple runs with mean, std, min, max aggregates</ span >
47+ </ button >
48+ < button class ="demo-card " data-panel ="panel-context " aria-controls ="panel-context " aria-expanded ="false ">
49+ < span class ="card-title "> Context Share</ span >
50+ < span class ="card-desc "> Host-managed AudioContext and MediaStream</ span >
51+ </ button >
52+ < button class ="demo-card " data-panel ="panel-mode " aria-controls ="panel-mode " aria-expanded ="false ">
53+ < span class ="card-title "> Mode Toggle</ span >
54+ < span class ="card-desc "> MediaRecorder vs AudioWorklet A/B comparison</ span >
55+ </ button >
56+ < button class ="demo-card " data-panel ="panel-audioworklet " aria-controls ="panel-audioworklet " aria-expanded ="false ">
57+ < span class ="card-title "> AudioWorklet</ span >
58+ < span class ="card-desc "> Raw Float32 PCM capture — v2 default backend</ span >
59+ </ button >
60+ < button class ="demo-card " data-panel ="panel-lifecycle " aria-controls ="panel-lifecycle " aria-expanded ="false ">
61+ < span class ="card-title "> Lifecycle</ span >
62+ < span class ="card-desc "> All six lifecycle events logged in real-time</ span >
63+ </ button >
64+ < button class ="demo-card " data-panel ="panel-debug " aria-controls ="panel-debug " aria-expanded ="false ">
65+ < span class ="card-title "> Debug Mode</ span >
66+ < span class ="card-desc "> Internal checkpoint logging for development</ span >
67+ </ button >
68+ < button class ="demo-card " data-panel ="panel-gain " aria-controls ="panel-gain " aria-expanded ="false ">
69+ < span class ="card-title "> Host Gain</ span >
70+ < span class ="card-desc "> GainNode chain for low mic levels (e.g. Safari)</ span >
71+ </ button >
3372</ div >
3473
35- < main >
74+ < div id =" demo-panel-area " >
3675
37- <!-- ─── Tab : Minimal ─── -->
38- < div id ="panel-minimal " class ="tabpanel " role =" tabpanel " aria-labelledby =" tab-minimal " aria- hidden=" false " >
76+ <!-- ─── Panel : Minimal ─── -->
77+ < div id ="panel-minimal " class ="demo-panel " hidden >
3978 < h2 > Minimal Integration</ h2 >
4079 < p class ="desc "> Single test run using the < code > mediarecorder</ code > backend. The simplest way to validate the component works.</ p >
4180 < div class ="controls ">
@@ -66,8 +105,8 @@ <h2>Minimal Integration</h2>
66105 </ details >
67106</ div >
68107
69- <!-- ─── Tab : Multi-Run ─── -->
70- < div id ="panel-multi " class ="tabpanel " role =" tabpanel " aria-labelledby =" tab-multi " aria- hidden=" true " >
108+ <!-- ─── Panel : Multi-Run ─── -->
109+ < div id ="panel-multi " class ="demo-panel " hidden >
71110 < h2 > Multi-Run with Aggregates</ h2 >
72111 < p class ="desc "> Configure the number of test runs. Results are accumulated and displayed with mean, standard deviation, min, and max.</ p >
73112 < div class ="controls ">
@@ -118,8 +157,8 @@ <h2>Multi-Run with Aggregates</h2>
118157 </ details >
119158</ div >
120159
121- <!-- ─── Tab : Context Share ─── -->
122- < div id ="panel-context " class ="tabpanel " role =" tabpanel " aria-labelledby =" tab-context " aria- hidden=" true " >
160+ <!-- ─── Panel : Context Share ─── -->
161+ < div id ="panel-context " class ="demo-panel " hidden >
123162 < h2 > Host-Managed AudioContext & Stream</ h2 >
124163 < p class ="desc "> The host creates its own < code > AudioContext</ code > and < code > MediaStream</ code > , then passes them via properties. The component never closes or stops what it didn't create.</ p >
125164 < div class ="controls ">
@@ -155,8 +194,8 @@ <h2>Host-Managed AudioContext & Stream</h2>
155194 </ details >
156195</ div >
157196
158- <!-- ─── Tab : Mode Toggle ─── -->
159- < div id ="panel-mode " class ="tabpanel " role =" tabpanel " aria-labelledby =" tab-mode " aria- hidden=" true " >
197+ <!-- ─── Panel : Mode Toggle ─── -->
198+ < div id ="panel-mode " class ="demo-panel " hidden >
160199 < h2 > Capture Backend Comparison</ h2 >
161200 < p class ="desc "> Run < code > MediaRecorder</ code > and < code > AudioWorklet</ code > back-to-back on the same source stream for direct A/B comparison.</ p >
162201 < div class ="controls ">
@@ -208,8 +247,50 @@ <h3>AudioWorklet</h3>
208247 </ details >
209248</ div >
210249
211- <!-- ─── Tab: Lifecycle ─── -->
212- < div id ="panel-lifecycle " class ="tabpanel " role ="tabpanel " aria-labelledby ="tab-lifecycle " aria-hidden ="true ">
250+ <!-- ─── Panel: AudioWorklet ─── -->
251+ < div id ="panel-audioworklet " class ="demo-panel " hidden >
252+ < h2 > AudioWorklet Backend</ h2 >
253+ < p class ="desc "> Captures raw Float32 PCM directly in an < code > AudioWorkletProcessor</ code > — no codec round-trip, sample-accurate timing. This is the v2 default backend. Use when you need the most accurate round-trip measurement.</ p >
254+ < div class ="controls ">
255+ < label > Runs:
256+ < input type ="number " id ="aw-count " value ="5 " min ="1 " max ="50 ">
257+ </ label >
258+ < button class ="btn btn-primary " id ="aw-start "> Start</ button >
259+ < button class ="btn btn-secondary " id ="aw-stop " disabled > Stop</ button >
260+ </ div >
261+ < div id ="aw-current " class ="result-box " style ="display:none ">
262+ < div class ="latency " id ="aw-current-latency "> —</ div >
263+ < div class ="detail " id ="aw-current-detail "> </ div >
264+ </ div >
265+ < div id ="aw-aggregate " class ="result-box aggregate " style ="display:none ">
266+ < div class ="stat " id ="aw-mean "> Mean: —</ div >
267+ < div class ="stat " id ="aw-stats "> SD (Standard Deviation): — · Min: — · Max: —</ div >
268+ </ div >
269+ < latency-test id ="aw-tester " recording-mode ="audioworklet "> </ latency-test >
270+ < details class ="config-snippet ">
271+ < summary > Configuration</ summary >
272+ < pre > < code > <latency-test id="aw-tester" recording-mode="audioworklet"></latency-test>
273+
274+ <script>
275+ const tester = document.querySelector('#aw-tester')
276+ tester.inputStream = hostStream // host-managed MediaStream
277+ tester.audioContext = hostAudioContext // host-managed AudioContext
278+ tester.numberOfTests = 5
279+
280+ tester.addEventListener('latency-result', e => {
281+ console.log(`${e.detail.latency.toFixed(2)} ms · ${e.detail.reliable ? 'reliable' : 'unreliable'}`)
282+ })
283+ tester.addEventListener('latency-complete', e => {
284+ console.log(`AW mean: ${e.detail.mean.toFixed(2)} ms`)
285+ })
286+
287+ tester.start()
288+ </script></ code > </ pre >
289+ </ details >
290+ </ div >
291+
292+ <!-- ─── Panel: Lifecycle ─── -->
293+ < div id ="panel-lifecycle " class ="demo-panel " hidden >
213294 < h2 > Lifecycle Event Log</ h2 >
214295 < p class ="desc "> All six lifecycle events are logged in real-time with timestamps.</ p >
215296 < div class ="controls ">
@@ -243,8 +324,8 @@ <h2>Lifecycle Event Log</h2>
243324 </ details >
244325</ div >
245326
246- <!-- ─── Tab : Debug Mode ─── -->
247- < div id ="panel-debug " class ="tabpanel " role =" tabpanel " aria-labelledby =" tab-debug " aria- hidden=" true " >
327+ <!-- ─── Panel : Debug Mode ─── -->
328+ < div id ="panel-debug " class ="demo-panel " hidden >
248329 < h2 > Debug Mode</ h2 >
249330 < div class ="callout-warn ">
250331 < strong > Development only.</ strong > Debug mode enables < code > console.debug('[latency-test]', …)</ code > logging at internal checkpoints. Having debug on — or DevTools open — can perturb browser scheduling and affect latency accuracy. Do not use for measurements you intend to record.
@@ -296,12 +377,12 @@ <h2>Debug Mode</h2>
296377 </ details >
297378</ div >
298379
299- <!-- ─── Tab : Host Gain ─── -->
300- < div id ="panel-gain " class ="tabpanel " role =" tabpanel " aria-labelledby =" tab-gain " aria- hidden=" true " >
380+ <!-- ─── Panel : Host Gain ─── -->
381+ < div id ="panel-gain " class ="demo-panel " hidden >
301382 < h2 > Host-Controlled Input Gain</ h2 >
302383 < p class ="desc "> The host builds a < code > GainNode</ code > chain and passes the processed stream to the component. Use this when your recording setup has low mic levels — common on Safari ≥ 16 with < code > echoCancellation: false</ code > (empirical value: 50×). A < code > ChannelSplitterNode</ code > isolates the left channel, which also handles wired earpods on Safari that force a stereo stream with signal only on the left.</ p >
303384 < div class ="callout-warn ">
304- This tab measures a different pipeline than direct-stream mode (an extra browser-managed Web Audio / MediaStream bridge). Results are self-consistent but should not be compared directly to other tabs .
385+ This panel measures a different pipeline than direct-stream mode (an extra browser-managed Web Audio / MediaStream bridge). Results are self-consistent but should not be compared directly to other panels .
305386 </ div >
306387 < div class ="controls ">
307388 < label > < input type ="checkbox " id ="gain-chain-toggle " checked > Use gain chain</ label >
@@ -346,14 +427,16 @@ <h2>Host-Controlled Input Gain</h2>
346427 </ details >
347428</ div >
348429
349- </ main >
430+ </ div > <!-- #demo-panel-area -->
431+
350432</ div > <!-- #demo-section -->
351433
352434< script src ="js/common.js "> </ script >
353435< script src ="js/minimal.js "> </ script >
354436< script src ="js/multi-run.js "> </ script >
355437< script src ="js/context-share.js "> </ script >
356438< script src ="js/mode-toggle.js "> </ script >
439+ < script src ="js/audioworklet.js "> </ script >
357440< script src ="js/lifecycle.js "> </ script >
358441< script src ="js/debug.js "> </ script >
359442< script src ="js/host-gain.js "> </ script >
0 commit comments