Skip to main content

Range selection

Drag-select a phrase to loop it. Extend the range with keyboard arrows. Wire a "loop this section" button in a lesson toolbar. Three methods drive the highlight overlay โ€” the translucent bar you see behind the selected range โ€” from your own gesture code, and one convenience method turns whatever is highlighted into a real playback loop. Grab the handles in the demo below to reshape the range; release, and playback loops the newly selected span.

The three methodsโ€‹

  • api.highlightPlaybackRange(startBeat, endBeat) โ€” paints the highlight overlay between two beats. Pure visual: it does not change what plays.
  • api.applyPlaybackRangeFromHighlight() โ€” reads the currently highlighted range, sets api.playbackRange to loop that span, and moves the tick position to the start.
  • api.clearPlaybackRangeHighlight() โ€” clears the overlay. Also pure visual; it does not touch playbackRange.

Split like this so a selection UI stays responsive during the drag (highlightPlaybackRange on every move), commits the loop only when the user releases (applyPlaybackRangeFromHighlight on drag-end), and clears cleanly (clearPlaybackRangeHighlight on escape or click-outside).

The built-in drag-selectโ€‹

With player.enablePlaybackRangeSelection on, alphaTab drives the same overlay from mouse drags. A drag becomes a range once it reaches a second beat; a press and release on one beat stays a click. By default that click clears the range. Set player.resetPlaybackRangeOnClick to false to keep the range and seek inside it instead. The two-beat threshold and resetPlaybackRangeOnClick are available since 1.9.0. Enabling and disabling interaction covers the details.

The editor patternโ€‹

Wire your drag gesture to the three methods. During the drag, keep painting the range. On release, commit it to a playback loop.

import * as alphaTab from '@coderline/alphatab';

const container = getAlphaTabContainer();
const api = new alphaTab.AlphaTabApi(container, {
core: { file: '/scores/song.gp' },
player: {
playerMode: alphaTab.PlayerMode.EnabledAutomatic,
enableSeekToClick: false,
enablePlaybackRangeSelection: false,
},
});

let dragStart = null;

api.beatMouseDown.on((beat) => {
dragStart = beat;
api.highlightPlaybackRange(beat, beat);
});

api.beatMouseMove.on((beat) => {
if (!dragStart) {
return;
}
api.highlightPlaybackRange(dragStart, beat);
});

api.beatMouseUp.on((beat) => {
if (!dragStart || !beat || beat === dragStart) {
api.clearPlaybackRangeHighlight();
dragStart = null;
return;
}
api.applyPlaybackRangeFromHighlight();
api.isLooping = true;
dragStart = null;
});

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โ€‹