Skip to main content

Enabling and disabling interaction

A read-only thumbnail preview does not want random clicks moving its playhead. A practice UI with a fixed loop should not let a stray drag repaint the range. A looping exercise should let the reader jump around inside the loop without losing it. Three flags cover these scenarios: player.enableSeekToClick gates the click-to-seek behavior, player.enablePlaybackRangeSelection gates the drag-select, and player.resetPlaybackRangeOnClick decides whether a plain click clears the current range. All three default to true when the player is active. Toggle them in the demo below: the events keep firing while the corresponding built-in behavior stops.

Event log
Click a beat or note in the score...

What the flags gateโ€‹

With both enableSeekToClick and enablePlaybackRangeSelection on (the default), alphaTab calls preventDefault() on left-button mouse events over the score, paints the selection highlight on beat-move, and seeks or commits the selection on beat-up.

Turn enableSeekToClick off and the transport stays put on click while every beatMouseDown / Move / Up and noteMouseDown / Move / Up event still fires. Turn enablePlaybackRangeSelection off and drag stops painting a range. In both cases your own handlers become the source of behavior.

A drag only becomes a selection once the pointer reaches a beat other than the one it was pressed on, so a range always spans at least two beats. A press and release on the same beat counts as a click, even if the mouse moved a little in between. Releasing a drag sets the playback range and moves the playhead to its start.

All three flags are available since 1.9.0.

Keep the range on click (new in 1.9)โ€‹

player.resetPlaybackRangeOnClick controls what a plain click does to an active playback range. It only applies while enablePlaybackRangeSelection is on.

  • true (default): a click clears the playback range and its highlight. With enableSeekToClick on, the playhead then moves to the clicked beat.
  • false: a click keeps the range. With enableSeekToClick on, the playhead moves to the clicked beat, clamped to the range: a click before the range seeks to its start, a click after it seeks to its end. A new drag still replaces the range.

The same clamping applies whenever a click seeks while a range stays active, for example a range set through api.playbackRange with enablePlaybackRangeSelection off.

Migrating from enableUserInteractionโ€‹

The three flags replace the player.enableUserInteraction master toggle, which is deprecated since 1.9.0 and still works. Setting it, in code or in JSON settings, writes the same value to enableSeekToClick, enablePlaybackRangeSelection, and resetPlaybackRangeOnClick. Reading it returns true when seek-to-click or range selection is on.

When to turn it offโ€‹

  • Read-only viewers. A score preview that never seeks: leave the events unwired and disable enableSeekToClick so clicks fall through to whatever surrounds the score.
  • Host editors with their own routing. An editor that treats clicks as note selection, drag as reordering, right-click as a menu. Take the raw events, route them yourself, keep the built-in seek out of the way.
  • Custom transport UX. A tutor UI where the transport advances beat-by-beat under teacher control. Bind beatMouseDown to your own state machine instead of jumping the tick position.
  • Fixed loops. A practice UI that owns the playback range: turn off enablePlaybackRangeSelection so the user cannot accidentally repaint it by dragging.
  • Seeking inside a loop. A practice UI where the reader drags a loop once and then clicks around inside it: turn off resetPlaybackRangeOnClick.

Show it in codeโ€‹

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,
},
});

api.beatMouseDown.on((beat) => {
routeBeatClickToEditor(beat);
});

To keep drag-select and seek-to-click but stop a click from clearing the selected 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: true,
enablePlaybackRangeSelection: true,
resetPlaybackRangeOnClick: false,
},
});

api.playbackRangeChanged.on((e) => {
api.isLooping = e.playbackRange !== 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โ€‹