@@ -18,6 +18,7 @@ export function useAudio() {
1818 const isLoadedRef = useRef ( false ) ;
1919 const perfTickRef = useRef ( 0 ) ;
2020 const wakeLockRef = useRef ( null ) ;
21+ const playheadVersionRef = useRef ( 0 ) ;
2122
2223 const loadKit = useCallback ( async ( kitId ) => {
2324 const kit = KITS [ kitId ] ;
@@ -67,6 +68,9 @@ export function useAudio() {
6768 }
6869
6970 if ( Tone . getTransport ( ) . state === "started" ) {
71+ // Invalidate any already queued UI updates from the previous
72+ // transport timeline before stopping.
73+ playheadVersionRef . current += 1 ;
7074 // Pause playback but preserve the current playhead position so resume
7175 // continues from the same step. `handleReset` is responsible for
7276 // explicitly resetting to step 0.
@@ -164,7 +168,11 @@ export function useAudio() {
164168 } ) ;
165169
166170 // Schedule UI update
171+ const scheduledPlayheadVersion = playheadVersionRef . current ;
167172 Tone . getDraw ( ) . schedule ( ( ) => {
173+ // Ignore stale scheduled updates after a manual seek/stop.
174+ if ( scheduledPlayheadVersion !== playheadVersionRef . current ) return ;
175+
168176 if ( perfEnabled ) {
169177 performance . mark ( uiMarkName ) ;
170178 performance . measure ( measureName , audioMarkName , uiMarkName ) ;
@@ -192,6 +200,8 @@ export function useAudio() {
192200 } , [ ] ) ;
193201
194202 const setStep = useCallback ( ( step ) => {
203+ // Invalidate queued draw updates from the previous timeline position.
204+ playheadVersionRef . current += 1 ;
195205 stepRef . current = step ;
196206 setCurrentStep ( step ) ;
197207 } , [ ] ) ;
0 commit comments