Seeking by tick and time
Two very different UIs move the same playhead: a chapter-marker list that jumps to bar 12 wants ticks (a score coordinate that stays put across tempo changes), and a video cursor overlay that drives the score wants milliseconds (a wall-clock coordinate that tracks the video's own time base). alphaTab exposes the playhead in both units โ pick the one that matches the UI you're wiring up.
Tick vs timeโ
tickPositionโ read or write the playhead in MIDI ticks. Ticks are model-accurate: bar 12 beat 2 sits at the same tick every time you load the score, regardless of tempo, count-in, orplaybackSpeed. Use ticks when the UI reasons about score structure โ jumping to a section, snapping to a beat, or persisting bookmarks.timePositionโ read or write the playhead in milliseconds of playback. Time is wall-clock: it advances with the audio and reflects any tempo changes and speed modifiers. Use time when the UI reasons about elapsed audio โ a progress bar, an mm:ss readout, or synchronization with an external clock.
endTick and endTime give the total length of the current MIDI in each unit. Divide the current value by the end to compute a normalized progress 0..1 for either kind of slider.
The position eventโ
playerPositionChanged fires on every internal tick update. The event args carry currentTick, endTick, currentTime, endTime, and an isSeek flag that distinguishes an ordinary time step from a jump you (or another handler) triggered.
Update the progress UI from the event; don't poll tickPosition on a timer.
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.EnabledSynthesizer },
});
const progress = document.getElementById('progress');
api.playerPositionChanged.on((args) => {
progress.value = args.currentTime / args.endTime;
});
progress.addEventListener('input', () => {
api.timePosition = progress.value * api.endTime;
});
getAlphaTabContainer() is a placeholder for the platform-specific step of obtaining the render target โ a <div> on Web, an AlphaTab control on .NET, or an AlphaTabView on Android. Every other line runs unchanged on all three runtimes.
Where to look nextโ
- Looping a section โ
playbackRangeuses the same tick coordinate system. - The MIDI event stream โ subscribe to per-event ticks for finer-grained wiring than
playerPositionChanged. - Playback cursor โ the visual counterpart to
playerPositionChanged. tickPosition,timePosition,endTick,endTime, andplayerPositionChangedreference entries.