Beat visibility and note bounds
Only redraw overlays for beats the reader can actually see. Turn a note-picker UI on and off without paying the memory cost the rest of the time. Two orthogonal knobs sit next to boundsLookup and cover both jobs. api.isVisible(beat) reports whether a beat is inside the current viewport โ cheap to call from a played-beat handler. core.includeNoteBounds opts alphaTab into recording per-note rectangles, unlocking note-level hit-testing at the cost of a slightly larger bounds table. The readout below reports isVisible per beat; the overlay swaps between beat rectangles and per-note-head rectangles as includeNoteBounds flips on and off.
isVisible(beat)โ
api.isVisible(beat) returns true when the beat's bounds fall inside the currently rendered viewport. It reads from boundsLookup.findBeat(beat) under the hood โ a beat that never made it into the current render (partial render, off-screen bar, hidden track) returns false.
Wire it into a played-beat handler to gate expensive UI work:
- Skip drawing a highlight overlay when the beat is not on-screen.
- Suppress a camera-follow scroll pulse if the beat is already inside the visible area.
- Tag a chord readout as "off-screen" when the user scrolled away from the cursor.
core.includeNoteBoundsโ
Off by default. Turn it on and alphaTab captures the rectangle of every note head or tab number during render, populating beatBounds.notes and enabling boundsLookup.getNoteAtPos(...).
The trade-off:
- Off โ beat-level hit-testing only. Smaller bounds table, faster render. Note-mouse events never fire.
- On โ full note-level precision. The
noteMouseDown/Move/Upevents start firing. Slight memory and render-time cost proportional to note density.
Turn it on when your UI needs to click a specific note (audition, edit, popup a note-level annotation) โ not when a beat-level click is enough.
Show it in codeโ
import * as alphaTab from '@coderline/alphatab';
const container = getAlphaTabContainer();
const chordReadout = document.getElementById('chord-readout');
const api = new alphaTab.AlphaTabApi(container, {
core: {
file: '/scores/song.gp',
includeNoteBounds: true,
},
player: { playerMode: alphaTab.PlayerMode.EnabledAutomatic },
});
api.playedBeatChanged.on((beat) => {
if (!api.isVisible(beat)) {
chordReadout.dataset.offscreen = 'true';
return;
}
chordReadout.dataset.offscreen = 'false';
chordReadout.textContent = beat.chord?.name ?? '';
});
api.noteMouseDown.on((note) => {
api.playNote(note);
});
getAlphaTabContainer() is a placeholder for the platform-specific step of obtaining the render target. Every other line runs unchanged on all three runtimes.
Where to look nextโ
- Bounds lookup โ the full geometry tree behind both flags.
- Reacting to beat and note clicks โ the note events unlocked by
includeNoteBounds. includeNoteBoundsin the settings reference.NoteBoundsandBeatBoundsin the types reference.