Summary
(As of v2.6.2.) The next-tick interval derives from the next word's data-m (hyperaudio-lite.js:928-933):
let interval = 0;
if (this.wordArr[index]) {
interval = this.wordArr[index].n.getAttribute('data-m') - this.currentTime * 1000;
}
this.timer = setTimeout(() => this.checkPlayHead(), interval + 1);
Once currentTime passes the final word, index === wordArr.length, wordArr[index] is undefined, so interval stays 0 and the loop re-arms every 1 ms (browser-clamped to ~4 ms after nesting depth 5 → ~250 wakeups/s, each an async getTime() promise plus an O(log n) binary search).
Failure scenario
Transcript ends before the audio (trailing music, untranscribed Q&A): ten minutes of outro ≈ 150k timer wakeups — sustained CPU/battery drain, silent. Also, getAttribute('data-m') returning null (attribute removed by an edit) coerces the subtraction to a negative interval → the same tight loop mid-transcript.
Fix
When wordArr[index] is missing, or the computed interval is ≤ 0 / NaN, fall back to a sane poll interval (e.g. 250 ms).
Found reviewing the copy vendored in hyperaudio-lite-editor / GliderMac (2026-07-11).
Summary
(As of v2.6.2.) The next-tick interval derives from the next word's
data-m(hyperaudio-lite.js:928-933):Once
currentTimepasses the final word,index === wordArr.length,wordArr[index]is undefined, sointervalstays 0 and the loop re-arms every 1 ms (browser-clamped to ~4 ms after nesting depth 5 → ~250 wakeups/s, each an asyncgetTime()promise plus an O(log n) binary search).Failure scenario
Transcript ends before the audio (trailing music, untranscribed Q&A): ten minutes of outro ≈ 150k timer wakeups — sustained CPU/battery drain, silent. Also,
getAttribute('data-m')returningnull(attribute removed by an edit) coerces the subtraction to a negative interval → the same tight loop mid-transcript.Fix
When
wordArr[index]is missing, or the computed interval is ≤ 0 / NaN, fall back to a sane poll interval (e.g. 250 ms).Found reviewing the copy vendored in hyperaudio-lite-editor / GliderMac (2026-07-11).