Skip to main content

Reacting to beat and note clicks

Click-to-seek in a lesson viewer, hover a note to pop a tooltip, tap a note to audition it in a chord trainer โ€” every one of these is a couple of lines against the six mouse events on the API. alphaTab hit-tests every mouse press against the rendered score and fires typed events with the Beat or Note under the cursor. Click a beat in the demo below and watch the cursor snap to it โ€” that is the built-in seek behavior wired through the same events you subscribe to. The log panel on the right shows every beat and note event as it fires.

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

The six eventsโ€‹

Beats always resolve. Notes resolve only when core.includeNoteBounds is enabled, since alphaTab skips per-note bounds capture by default for performance.

  • beatMouseDown โ€” mouse pressed on a beat. Fires with the Beat.
  • beatMouseMove โ€” cursor dragged over a new beat after a press. Fires with the Beat.
  • beatMouseUp โ€” mouse released after a beat press. Fires with the Beat or null if the release landed outside a beat.
  • noteMouseDown โ€” mouse pressed on a note head or tab number. Fires with the Note.
  • noteMouseMove โ€” cursor dragged over a new note after a press. Fires with the Note.
  • noteMouseUp โ€” mouse released after a note press. Fires with the Note or null.

The underlying IMouseEventArgs reaches your platform bridge on the on-<event> DOM event and exposes preventDefault(), isLeftMouseButton, and relative getX(container) / getY(container) โ€” call preventDefault() if you handle the click yourself and want to suppress the built-in seek and selection behavior.

Click to seekโ€‹

Set tickPosition to the beat's absolutePlaybackStart and playback jumps to that beat. This is the pattern alphaTab uses internally when player.enableSeekToClick is on; wire it yourself when the flag is off.

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

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

Per-note click hookโ€‹

Turn on core.includeNoteBounds first, then subscribe to noteMouseDown โ€” the event stays quiet without it. Use it to audition a single note, pop a fingering hint, or route the note into an editor selection.

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

const container = getAlphaTabContainer();
const tooltip = document.getElementById('note-tooltip');

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

api.noteMouseDown.on((note) => {
// Audition the note through the built-in synth.
api.playNote(note);
});

api.noteMouseMove.on((note) => {
// Show a fingering / pitch hint tooltip next to the note.
tooltip.textContent = `string ${note.string} ยท fret ${note.fret}`;
tooltip.hidden = false;
});

api.noteMouseUp.on(() => {
tooltip.hidden = true;
});

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