During YouTube Video playback, experiencing a drift sync is fighting with AlphaTab #2419
Replies: 2 comments
-
|
I'm missing a bit information here to judge where the problem on such a drift is. Can you provide the youtube video and test file you are using and seeing a drift? Generally the feature is supposed to work like this: The external media/youtube video defines the main playback time-axis but it sounds like you do things vice versa adjusting youtube to fit the music notation. alphaTab has a feature called sync points which needs to be used to synchronize the music sheet with any external media. Having those sync points defined, alphaTab dynamically adjusts the playback position to match the position given by the youtube video keeping things in sync: https://www.alphatab.net/docs/guides/audio-video-sync |
Beta Was this translation helpful? Give feedback.
-
|
Hey Daniel,
I am working on a Github repo that will include a few different test files
for you... I will have those tomorrow at the latest. I normally use an
src/app/page.tsx for the live testing on localhost3000. Is that okay for
you as well along with the sample files? Or do you prefer anything
different?
Here is a YouTube video link: https://youtu.be/20H6ZjcIp7c
We have confirmed as per your email:
Key Missing Pieces:
1. *We're doing it backwards!* Daniel says:
- ✅ Correct: YouTube defines time → AlphaTab follows
- ❌ What we did: AlphaTab defines time → YouTube follows
2. *We haven't implemented Sync Points* - This is likely THE issue
- The drift sync code we added is fighting against how it should work
- We need to use AlphaTab's built-in sync point system However,
if we are using guitar pro files is this still necessary? or is there an
alternative option?) Example: Adjusting the Start Time (Pickup
Bar/Anacrusis) and Tempo?
I am struggling on this one... as I am not sure which way is correct. We
know that AlphaTab is supposed to follow YouTube. And as for sync points I
am using Gp files and to AlphaTab may be completely different, than loading
GP tabs on Songsterr.com or Ultimate-Guitar.com as end users don't use sync
points as the system follows a different process that makes it very
user friendly. So anyone can upload a guitar pro file and then upload any
related YouTube video and somehow (perhaps the synth player is muted
underneath) but it is controlling the timing. They don't have
metadata.jsons and Songsterr doesn't keep a metadata.json library for sync
points. I did attach an editor for Songsterr and as you will see, sync
points is not an option when I load gp files or create my tab manually.
Now I realized they may have found a simple way for the public (not sure)
as I am trying to replicate somehow on my end.... This is an example (see
screenshot for songsterr editor):
*******Start of Songsterr Editor Comparison for YouTube video sync********
You are correct that you won't find a labeled "sync points" or "align
audio" button in the Songsterr Editor. The synchronization is handled
indirectly through standard music notation features and, for uploaded
videos, a reporting process for moderators.
How to Manually Sync in the Editor
The manual synchronization you are looking for involves manipulating the
music notation itself to match the audio, as outlined in the documentation
text you previously mentioned:
- Adjusting the Start Time (Pickup Bar/Anacrusis): To offset the start
of the music to line up with the audio, you can insert rests at the
beginning of the first measure (anacrusis). The editor has note duration
options (visible in your screenshot, the orange section with note icons)
that allow you to insert various rests. By inserting the appropriate
duration of rests at the start, you push the actual music to the correct
temporal position.
- Modifying Tempo Changes: The Songsterr editor allows you to add tempo
changes throughout the score. If a song's tempo is not constant, you can
add multiple tempo markers (J=120, etc.) at specific points in the music
to correct the playback speed and keep it aligned with the original audio.
The features for manually dragging specific sync points are often found in
dedicated, professional music notation such as a guitar pro file or digital
audio workstation software, not directly in the Songsterr editor's user
interface.
Yes, if a Guitar Pro (GP) file already has the correct tempo changes and
anacrusis (pickup bar) information embedded, these details should be
preserved when you import it into Songsterr. The core process for you would
then be linking the YouTube video.
How to Add a YouTube Video to an Existing Tab
When you upload a GP file to Songsterr and publish it, or when you are
editing a draft, you have the option to link a YouTube video.
- The Songsterr system then attempts to automatically synchronize the
tab with the audio from that specific YouTube video.
- It is at this point that the existing tempo and anacrusis data from
your GP file are used as the foundation for the sync.
What to Do If the Sync Is Off
Even with a well-prepared GP file, slight variations in performance between
different audio sources can cause a drift.
- If the video sync is slightly off, the manual corrections (inserting
rests, adding or modifying tempo markers) would still be necessary within
the Songsterr Editor to fine-tune the alignment with the specific video
you've linked.
- If the song has a very "human" feel or a lot of micro-tempo changes,
the automatic sync might not be perfect, requiring you to make adjustments
section by section.
So while the GP file gives you a great head start, the video link is where
the specific synchronization for that particular performance is established.
Would you like to know the exact steps to upload a GP file and link the
YouTube video within the Songsterr interface?
***** End of Songsterr Editor Comparision******
So if it is correct for AlphaTab as well, that the gp files would "NOT
NEED" sync points?
Then here is where we are struggling to find answers: I have used both
Claude.ai and Gemini now to help me but each see's something slightly
different:
They both confirmed the logs:
*🎉 BREAKTHROUGH!* The logs reveal EVERYTHING!
The Smoking Gun:
🔍 Output type: AlphaSynthScriptProcessorOutput
🔍 Has updatePosition? false
*updatePosition() DOES NOT EXIST in AlphaSynthScriptProcessorOutput!*
The AlphaTab docs show it on IExternalMediaSynthOutput, but you're getting
AlphaSynthScriptProcessorOutput(the SYNTH output, not the external media
output).
Why It's Working Now:
When you press play on the YouTube player directly, the cursor syncs *because
AlphaTab is following its OWN internal timing* (from when you called
api.play()), NOT from your 50ms loop!
The 50ms loop with updatePosition() *never worked* - the sync is happening
because:
1. You call api.play() when YouTube starts
2. AlphaTab's internal player starts its own timing
3. They happen to be somewhat aligned (but will drift apart over time)
The Real Problem:
*Your playerMode is STILL set to 'synthesizer' instead of 'external'!*
Check your AlphaTabRenderer settings initialization. I bet you have:
// ❌ WRONG - Always synthesizer
settings.player.playerMode = alphaTab.PlayerMode.EnabledSynthesizer;
// OR even worse, the dynamic switch isn't working:
settings.player.playerMode = audioSource === 'synth'
? alphaTab.PlayerMode.EnabledSynthesizer
: alphaTab.PlayerMode.EnabledExternalMedia; // ← This isn't being applied
*The Fix:*
You need to *VERIFY* the playerMode is actually changing when you switch to
"original" mode
console.log('🔍 Current playerMode:',
api.settings.player.playerMode);console.log('🔍 Expected for
original:', alphaTab.PlayerMode.EnabledExternalMedia);
*If playerMode is still showing as synthesizer when in "original" mode,
that's your problem!*
The dynamic playerMode switch isn't working, so AlphaTab never creates the
external media output type that HAS updatePosition().
*NOW GEMINI IS SHOWING ME THIS:*
This log file is the definitive proof we needed. It confirms exactly why
the "Pure External Mode" (V97.0/V97.6) is failing and why the "Hybrid Mode"
(using api.timePosition) worked.
Here is the breakdown of the "Smoking Gun" and the immediate solution.
🔫 The Smoking Gun
The logs prove that *AlphaTab never actually switched its internal engine
to External Mode.*
-
*Log:* 🔍 Output type: AlphaSynthScriptProcessorOutput
-
This object is the *Synthesizer's* audio driver.
-
*Log:* 🔍 Has updatePosition? false
-
This confirms you are trying to call a method that *does not exist*
on the Synthesizer.
Even though your AlphaTabRenderer logs said "Updating player mode to:
external," AlphaTab's internal engine *did not swap the audio output object*.
It stayed attached to the Synthesizer. This is why updatePosition fails—you
are asking the Synthesizer to update a manual position, which it doesn't
support via that specific method.
🚀 The Solution: Revert to the "Hybrid" Fix
You had it working perfectly before Claude tried to force the "Proper
External Mode." The "Proper" mode requires a hard reset of the AlphaTab
player that isn't happening dynamically.
*Don't fight the library.* If AlphaTab wants to stay in Synthesizer mode,
let it! We can still silence it and drive the cursor manually.
(This does not sound right???)
*Go back to the configuration that worked:*
1.
*Keep playerMode as "synthesizer"* (or ignore the fact that it fails to
switch).
2.
*Use api.timePosition* to drive the cursor (instead of
output.updatePosition).
3.
*Mute the Synth* (which you already do via api.masterVolume = 0).
Action Plan (Reverting V97.6)
In your page.tsx (Original Mode Sync Loop), replace the complex casting and
updatePosition logic with the robust public setter.
*Change this (The V97.6 Failure):*
TypeScript
// ❌ DELETE THIS COMPLEX BLOCK// const output = api.player.output as
alphaTab.synth.IExternalMediaSynthOutput;// if (output && typeof
output.updatePosition === 'function') { ... }
*To This (The Working Fix):*
TypeScript
// ✅ RESTORE THIS SIMPLE FIX// This works on ANY output type (Synth OR External)
api.timePosition = timeMs;
Why this is the "Master Blueprint" Fix
-
*Robustness:* api.timePosition works even if AlphaTab fails to switch
modes. It forces the visual cursor to move regardless of what the audio
engine is doing.
-
*Stability:* You avoid the race conditions of trying to destroy/recreate
the internal player object to get the "correct" IExternalMediaSynthOutput
.
-
*Result:* You get the exact behavior you want (YouTube plays, cursor
follows) without the "method not found" errors.
*Next Step:* Revert that sync loop code to use api.timePosition = ytTime.
That was your winning state.
So as you can see this is where the confusion is really coming into play.
I don't believe that the synth player is simply muted? But correct me if
I am wrong, as I thought it was paused or off entirely during the switch?
But it is muted but unpaused, while in the "original" mode that might be
how the others are doing it as well?
Sorry for the long list of details.....
Thank you for your patience and expertise on this all!
…On Sat, Dec 6, 2025 at 2:17 AM Daniel Kuschny ***@***.***> wrote:
I'm missing a bit information here to judge where the problem on such a
drift is.
Can you provide the youtube video and test file you are using and seeing a
drift?
Generally the feature is supposed to work like this: The external
media/youtube video defines the main playback time-axis but it sounds like
you do things vice versa adjusting youtube to fit the music notation.
alphaTab has a feature called sync points which needs to be used to
synchronize the music sheet with any external media. Having those sync
points defined, alphaTab dynamically adjusts the playback position to match
the position given by the youtube video keeping things in sync:
https://www.alphatab.net/docs/guides/audio-video-sync
https://www.alphatab.net/docs/guides/media-sync-editor
—
Reply to this email directly, view it on GitHub
<#2419 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/BNLESQDD75WJ7H4YN4AOX7L4AKUNBAVCNFSM6AAAAACOHIBJEOVHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTKMJXHEZDGMA>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
I am using Claude.ai to assist me on this issue: See additional notes below for the steps we used to create this using page.tsx and AlphaTabRenderer.tsx plus a YouTubePlayer.tsx
The drift sync is fighting with AlphaTab:🔁 V96.4: Drift sync (PLAYING, drift=1.0s) → 23.5s
🎯 V96.5: YouTube seeking: trueThis happens every ~1 second, causing the jerky playback! Here's what's happening:🔍 The Problem:You're running TWO independent players:
AlphaTab synthesizer (muted, playing at MIDI tempo)
YouTube video (playing at video tempo)
They drift apart because:
AlphaTab uses the exact MIDI tempo from the .gp file
YouTube video might be at a slightly different tempo
Small differences accumulate to >1s drift
Drift detection kicks in and rewinds YouTube
This causes the jerky cursor
Claude.ai is suggesting
🔧 Solution Options:
Option 1: Disable drift sync during playback (Quick fix)
Only sync on seek/pause, not during playback.
Option 2: Increase drift threshold (Temporary fix)
Change from 1.0s to 2.0s or 3.0s so it doesn't correct as often.
Option 3: Switch to proper External Media mode (Correct fix)
The AlphaTab docs show using playerMode: EnabledExternalMedia, not synthesizer. In External mode, we need to use updatePosition() to drive the cursor (which would exist in that mode).Let me implement Option 1 (disable drift sync during playback) as the quickest fix:
Note: Claude.ai chose option 1 at the moment. But I believe Option 3 is what we really need and need some guidance on best way to go about that now?
Here is a RECAP
We first noticed the notation color, auto-scroll all stopped working after the External playerMode conversion:
AlphaTab External Media documentation pattern. Here's what changed:
🔧 V96.5 Updates (Following AlphaTab Docs):
javascriptif (event.data === 1) { // Playing
setIsPlaying(true);
api.play(); // 🎯 NEW: Sync AlphaTab state
}
else if (event.data === 2) { // Paused
setIsPlaying(false);
api.pause(); // 🎯 NEW: Sync AlphaTab state
}
else if (event.data === 0) { // Ended
setIsPlaying(false);
api.stop(); // 🎯 NEW: Sync AlphaTab state
}
javascriptif (audioSource === 'original') {
if (isPlaying) {
output.handler.pause(); // Control YouTube
api.pause(); // 🎯 NEW: Sync AlphaTab
} else {
output.handler.play(); // Control YouTube
api.play(); // 🎯 NEW: Sync AlphaTab
}
}
Added warnings to see if updatePosition method exists.
📚 AlphaTab Docs Pattern:
According to the docs, for external media we need THREE things:
✅ Handler - Let AlphaTab control YouTube (play/pause/seek)
✅ State sync - Call api.play()/pause()/stop() when YouTube state changes
✅ Position updates - Call updatePosition() every 50ms during playback
**Console logs:
We started having this as the warnings:**
page.tsx:479 🎬 V96.5: YouTube state change: 1▶️ V96.5: YouTube playing -> setIsPlaying(true) + api.play()⚠️ V96.5: updatePosition method not found on output⚠️ V96.5: updatePosition method not found on output⚠️ V96.5: updatePosition method not found on output⚠️ V96.5: updatePosition method not found on output⚠️ V96.5: updatePosition method not found on output
page.tsx:487
DebugPanel.tsx:145 ✅ Score loaded - updating debug panel
installHook.js:1
overrideMethod @ installHook.js:1Understand this warning
DebugPanel.tsx:145 ✅ Score loaded - updating debug panel
installHook.js:1
overrideMethod @ installHook.js:1Understand this warning
DebugPanel.tsx:145 ✅ Score loaded - updating debug panel
2installHook.js:1
overrideMethod @ installHook.js:1Understand this warning
DebugPanel.tsx:145 ✅ Score loaded - updating debug panel
installHook.js:1
overrideMethod @ installHook.js:1Understand this warning
2DebugPanel.tsx:145 ✅ Score loaded - updating debug panel
installHook.js:1
Claude.ai notes:⚠️ V96.5: updatePosition method not found on output is the clue. Let me check something - since purple notation and auto-scroll ARE working, I suspect we might not even need that 50ms loop anymore!
The warning
Here's my theory: Since we're running in playerMode="synthesizer" (but with the synth muted via masterVolume = 0), when we call api.play() in the state change handler, AlphaTab's internal synthesizer is actually running and driving the cursor - it just can't be heard because it's muted!
Let me test this theory by removing the problematic 50ms loop entirely:
After removing the 50s
🔁 V96.4: Drift sync (PLAYING, drift=1.1s) → 37.6s▶️ V96.5: YouTube playing -> setIsPlaying(true) + api.play()▶️ V96.5: YouTube playing -> setIsPlaying(true) + api.play()▶️ V96.5: YouTube playing -> setIsPlaying(true) + api.play()
page.tsx:700 🎯 V96.5: YouTube seeking: true
YouTubePlayer.tsx:149 🎬 V96.4: YouTube state: 3
page.tsx:479 🎬 V96.5: YouTube state change: 3
DebugPanel.tsx:145 ✅ Score loaded - updating debug panel
YouTubePlayer.tsx:149 🎬 V96.4: YouTube state: 1
page.tsx:479 🎬 V96.5: YouTube state change: 1
page.tsx:487
DebugPanel.tsx:145 ✅ Score loaded - updating debug panel
page.tsx:700 🎯 V96.5: YouTube seeking: false
8DebugPanel.tsx:145 ✅ Score loaded - updating debug panel
YouTubePlayer.tsx:212 🔁 V96.4: Drift sync (PLAYING, drift=1.1s) → 38.6s
page.tsx:700 🎯 V96.5: YouTube seeking: true
YouTubePlayer.tsx:149 🎬 V96.4: YouTube state: 3
page.tsx:479 🎬 V96.5: YouTube state change: 3
DebugPanel.tsx:145 ✅ Score loaded - updating debug panel
page.tsx:700 🎯 V96.5: YouTube seeking: false
YouTubePlayer.tsx:149 🎬 V96.4: YouTube state: 1
page.tsx:479 🎬 V96.5: YouTube state change: 1
page.tsx:487
6DebugPanel.tsx:145 ✅ Score loaded - updating debug panel
DebugPanel.tsx:145 ✅ Score loaded - updating debug panel
DebugPanel.tsx:145 ✅ Score loaded - updating debug panel
YouTubePlayer.tsx:212 🔁 V96.4: Drift sync (PLAYING, drift=1.3s) → 39.5s
page.tsx:700 🎯 V96.5: YouTube seeking: true
page.tsx:289 🎮 V96.5: handlePlayPause - mode: original, isPlaying: true
page.tsx:296 ⏸️ V96.5: Pausing via handler + api.pause()
page.tsx:131 ⏸️ V96.5: Handler.pause() called
page.tsx:134 ✅ V96.5: YouTube pauseVideo() executed
YouTubePlayer.tsx:149 🎬 V96.4: YouTube state: 3
page.tsx:479 🎬 V96.5: YouTube state change: 3
DebugPanel.tsx:145 ✅ Score loaded - updating debug panel
page.tsx:700 🎯 V96.5: YouTube seeking: false
YouTubePlayer.tsx:149 🎬 V96.4: YouTube state: 1
page.tsx:479 🎬 V96.5: YouTube state change: 1
page.tsx:487
YouTubePlayer.tsx:149 🎬 V96.4: YouTube state: 2
page.tsx:479 🎬 V96.5: YouTube state change: 2
page.tsx:495 ⏸️ V96.5: YouTube paused -> setIsPlaying(false) + api.pause()
alphaTab.core.mjs:43732 [Deprecation] The ScriptProcessorNode is deprecated. Use AudioWorkletNode instead. (https://bit.ly/audio-worklet)
play @ alphaTab.core.mjs:43732
handleWorkerMessage @ alphaTab.core.mjs:44145Understand this warning
3DebugPanel.tsx:145 ✅ Score loaded - updating debug panel
Beta Was this translation helpful? Give feedback.
All reactions