javascript 58 lines · 9 steps

Building a synced lyrics highlighter

A class that highlights the current lyric line as audio plays, using a binary search on timestamps.

Explained by highlit
1class LyricsSync {
2 constructor(audio, container, lines) {
3 this.audio = audio;
4 this.container = container;
5 this.lines = [...lines].sort((a, b) => a.time - b.time);
6 this.activeIndex = -1;
7 this.elements = this.render();
8 this.audio.addEventListener('timeupdate', this.onTimeUpdate);
9 }
10 
11 render() {
12 return this.lines.map((line) => {
13 const el = document.createElement('p');
14 el.className = 'lyric-line';
15 el.textContent = line.text;
16 el.addEventListener('click', () => {
17 this.audio.currentTime = line.time;
18 this.audio.play();
19 });
20 this.container.appendChild(el);
21 return el;
22 });
23 }
24 
25 findIndexAt(time) {
26 let lo = 0;
27 let hi = this.lines.length - 1;
28 let result = -1;
29 while (lo <= hi) {
30 const mid = (lo + hi) >> 1;
31 if (this.lines[mid].time <= time) {
32 result = mid;
33 lo = mid + 1;
34 } else {
35 hi = mid - 1;
36 }
37 }
38 return result;
39 }
40 
41 onTimeUpdate = () => {
42 const index = this.findIndexAt(this.audio.currentTime);
43 if (index === this.activeIndex) return;
44 
45 this.elements[this.activeIndex]?.classList.remove('active');
46 this.activeIndex = index;
47 
48 const current = this.elements[index];
49 if (current) {
50 current.classList.add('active');
51 current.scrollIntoView({ behavior: 'smooth', block: 'center' });
52 }
53 };
54 
55 destroy() {
56 this.audio.removeEventListener('timeupdate', this.onTimeUpdate);
57 }
58}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Sorting timestamps once up front lets every lookup use fast binary search instead of scanning.
  2. 2Diffing against the last active index avoids redundant DOM writes on every timeupdate tick.
  3. 3Storing the listener as an arrow class field keeps `this` bound and makes clean removal possible.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building a synced lyrics highlighter — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code