@@ -2480,21 +2480,31 @@ class Singer {
24802480 tur . singer . embeddedGraphics [ blk ] = [ ] ;
24812481
24822482 // Ensure note value block unhighlights after note plays.
2483- // Cancel any previously pending unhighlight timer for this block to
2484- // prevent unbounded timer accumulation in tight infinite loops, which
2485- // would otherwise saturate the JS timer queue and stall the main thread.
2483+ // -- Merged fix: our timer-dedup + master's min-duration floor & stage.update --
2484+ // 1. Cancel any previously pending unhighlight timer for this block to
2485+ // prevent unbounded timer accumulation in tight infinite loops, which
2486+ // would otherwise saturate the JS timer queue and stall the main thread.
2487+ // 2. Enforce a minimum visible highlight duration (80 ms) so fast notes
2488+ // still produce a visible glow rather than instantly clearing.
2489+ // 3. Call activity.stage.update() after unhighlight so the canvas
2490+ // repaints immediately (matches master's behaviour).
2491+ const MIN_HIGHLIGHT_DURATION_MS = 80 ;
24862492 if ( ! tur . singer . _unhighlightTimers ) {
24872493 tur . singer . _unhighlightTimers = { } ;
24882494 }
24892495 if ( tur . singer . _unhighlightTimers [ blk ] ) {
24902496 clearTimeout ( tur . singer . _unhighlightTimers [ blk ] ) ;
24912497 }
2498+ const highlightDurationMs = Math . max ( beatValue * 1000 , MIN_HIGHLIGHT_DURATION_MS ) ;
24922499 tur . singer . _unhighlightTimers [ blk ] = setTimeout ( ( ) => {
24932500 if ( activity . blocks . visible && blk in activity . blocks . blockList ) {
24942501 activity . blocks . unhighlight ( blk ) ;
2502+ if ( activity . stage ) {
2503+ activity . stage . update ( ) ;
2504+ }
24952505 }
24962506 delete tur . singer . _unhighlightTimers [ blk ] ;
2497- } , beatValue * 1000 ) ;
2507+ } , highlightDurationMs ) ;
24982508 } ;
24992509
25002510 if ( last ( tur . singer . inNoteBlock ) !== null || noteInNote ) {
0 commit comments