A custom cursor handler
A bouncing pill above the current beat, a lyric bubble that hops from word to word, a companion animation that rides the notation โ anything that has to move in lockstep with the cursor but is not the cursor. Assign an ICursorHandler to api.customCursorHandler and alphaTab hands you the cursor elements plus every placement callback. Toggle the demo below to swap between the default cursor and a handler that rides a bouncing pill adorner above the beat.
When to reach for itโ
The CSS-level knobs cover most restyling. Reach for a custom handler when placement itself needs to change:
- Extra DOM adornments that ride along with the cursor (a fingering pointer, a metronome ticker).
- Animation curves the CSS transition cannot express (physics-driven follow, easing per cursor mode).
- Coupling cursor motion to another surface (a scrubber on an external video track).
The handler implements ICursorHandler: onAttach, onDetach, placeBarCursor, placeBeatCursor, and transitionBeatCursor. alphaTab still creates the cursor IContainer elements โ your handler decides where they sit and how they move.
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 },
});
api.customCursorHandler = {
_adorner: undefined,
onAttach(cursors) {
this._adorner = document.createElement('div');
this._adorner.className = 'my-cursor-adorner';
cursors.cursorWrapper.element.appendChild(this._adorner);
},
onDetach(cursors) {
this._adorner?.remove();
this._adorner = undefined;
},
placeBarCursor(barCursor, beatBounds) {
const bar = beatBounds.barBounds.masterBarBounds.visualBounds;
barCursor.setBounds(bar.x, bar.y, bar.w, bar.h);
},
placeBeatCursor(beatCursor, beatBounds, startBeatX) {
const bar = beatBounds.barBounds.masterBarBounds.visualBounds;
beatCursor.transitionToX(0, startBeatX);
beatCursor.setBounds(startBeatX, bar.y, 1, bar.h);
this._adorner.style.transform = `translate(${startBeatX}px, ${bar.y - 12}px)`;
},
transitionBeatCursor(beatCursor, beatBounds, startBeatX, nextBeatX, duration, cursorMode) {
beatCursor.transitionToX(duration, nextBeatX);
this._adorner.style.transition = `transform ${duration}ms linear`;
this._adorner.style.transform = `translate(${nextBeatX}px, ${beatBounds.barBounds.masterBarBounds.visualBounds.y - 12}px)`;
},
};
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โ
- Cursor and element highlighting โ the built-in cursor behavior you are replacing.
- Customizing cursor colors and animation โ restyle without replacing the handler.
customCursorHandlerin the API reference โ signature of theICursorHandlerinterface.