diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx
index 78c953e99..479bc7424 100644
--- a/source/funkin/play/PlayState.hx
+++ b/source/funkin/play/PlayState.hx
@@ -684,7 +684,7 @@ class PlayState extends MusicBeatSubState
       vocals.playerVolume = 1;
       vocals.opponentVolume = 1;
 
-      currentStage.resetStage();
+      if (currentStage != null) currentStage.resetStage();
 
       playerStrumline.clean();
       opponentStrumline.clean();
@@ -871,6 +871,13 @@ class PlayState extends MusicBeatSubState
         trace('Found ${songEventsToActivate.length} event(s) to activate.');
         for (event in songEventsToActivate)
         {
+          // If an event is trying to play, but it's over 5 seconds old, skip it.
+          if (event.time - Conductor.songPosition < -5000)
+          {
+            event.activated = true;
+            continue;
+          };
+
           var eventEvent:SongEventScriptEvent = new SongEventScriptEvent(event);
           dispatchEvent(eventEvent);
           // Calling event.cancelEvent() skips the event. Neat!
@@ -1828,6 +1835,7 @@ class PlayState extends MusicBeatSubState
 
         // Judge the miss.
         // NOTE: This is what handles the scoring.
+        trace('Missed note! ${note.noteData}');
         onNoteMiss(note);
 
         note.handledMiss = true;
@@ -1861,8 +1869,7 @@ class PlayState extends MusicBeatSubState
   {
     for (note in playerStrumline.notes.members)
     {
-      var hitWindowStart = note.strumTime - Constants.HIT_WINDOW_MS;
-      var hitWindowCenter = note.strumTime;
+      if (note == null || note.hasBeenHit) continue;
       var hitWindowEnd = note.strumTime + Constants.HIT_WINDOW_MS;
 
       if (Conductor.songPosition > hitWindowEnd)
@@ -1872,6 +1879,9 @@ class PlayState extends MusicBeatSubState
         note.handledMiss = true;
       }
     }
+
+    playerStrumline.handleSkippedNotes();
+    opponentStrumline.handleSkippedNotes();
   }
 
   /**
@@ -1935,6 +1945,7 @@ class PlayState extends MusicBeatSubState
         if (targetNote == null) continue;
 
         // Judge and hit the note.
+        trace('Hit note! ${targetNote.noteData}');
         goodNoteHit(targetNote, input);
 
         targetNote.visible = false;
@@ -2262,12 +2273,12 @@ class PlayState extends MusicBeatSubState
     if (FlxG.keys.justPressed.NINE) iconP1.toggleOldIcon();
 
     #if debug
-    // PAGEUP: Skip forward one section.
-    // SHIFT+PAGEUP: Skip forward ten sections.
-    if (FlxG.keys.justPressed.PAGEUP) changeSection(FlxG.keys.pressed.SHIFT ? 10 : 1);
-    // PAGEDOWN: Skip backward one section. Doesn't replace notes.
-    // SHIFT+PAGEDOWN: Skip backward ten sections.
-    if (FlxG.keys.justPressed.PAGEDOWN) changeSection(FlxG.keys.pressed.SHIFT ? -10 : -1);
+    // PAGEUP: Skip forward two sections.
+    // SHIFT+PAGEUP: Skip forward twenty sections.
+    if (FlxG.keys.justPressed.PAGEUP) changeSection(FlxG.keys.pressed.SHIFT ? 20 : 2);
+    // PAGEDOWN: Skip backward two section. Doesn't replace notes.
+    // SHIFT+PAGEDOWN: Skip backward twenty sections.
+    if (FlxG.keys.justPressed.PAGEDOWN) changeSection(FlxG.keys.pressed.SHIFT ? -20 : -2);
     #end
 
     if (FlxG.keys.justPressed.B) trace(inputSpitter.join('\n'));
@@ -2550,6 +2561,7 @@ class PlayState extends MusicBeatSubState
 
   public override function close():Void
   {
+    criticalFailure = true; // Stop game updates.
     performCleanup();
     super.close();
   }
@@ -2564,6 +2576,10 @@ class PlayState extends MusicBeatSubState
       // TODO: Uncache the song.
     }
 
+    // Stop the music.
+    FlxG.sound.music.pause();
+    vocals.stop();
+
     // Remove reference to stage and remove sprites from it to save memory.
     if (currentStage != null)
     {
diff --git a/source/funkin/play/notes/Strumline.hx b/source/funkin/play/notes/Strumline.hx
index 0407d8ffc..15069f9b7 100644
--- a/source/funkin/play/notes/Strumline.hx
+++ b/source/funkin/play/notes/Strumline.hx
@@ -55,7 +55,12 @@ class Strumline extends FlxSpriteGroup
 
   final noteStyle:NoteStyle;
 
+  /**
+   * The note data for the song. Should NOT be altered after the song starts,
+   * so we can easily rewind.
+   */
   var noteData:Array<SongNoteData> = [];
+
   var nextNoteIndex:Int = -1;
 
   var heldKeys:Array<Bool> = [];
@@ -221,15 +226,21 @@ class Strumline extends FlxSpriteGroup
     if (noteData.length == 0) return;
 
     var songStart:Float = PlayState.instance.startTimestamp ?? 0.0;
+    var hitWindowStart:Float = Conductor.songPosition - Constants.HIT_WINDOW_MS;
     var renderWindowStart:Float = Conductor.songPosition + RENDER_DISTANCE_MS;
 
     for (noteIndex in nextNoteIndex...noteData.length)
     {
       var note:Null<SongNoteData> = noteData[noteIndex];
 
-      if (note == null) continue;
-      if (note.time < songStart) continue;
-      if (note.time > renderWindowStart) break;
+      if (note == null) continue; // Note is blank
+      if (note.time < songStart || note.time < hitWindowStart)
+      {
+        // Note is in the past, skip it.
+        nextNoteIndex = noteIndex + 1;
+        continue;
+      }
+      if (note.time > renderWindowStart) break; // Note is too far ahead to render
 
       var noteSprite = buildNoteSprite(note);
 
@@ -238,7 +249,7 @@ class Strumline extends FlxSpriteGroup
         noteSprite.holdNoteSprite = buildHoldNoteSprite(note);
       }
 
-      nextNoteIndex++; // Increment the nextNoteIndex rather than splicing the array, because splicing is slow.
+      nextNoteIndex = noteIndex + 1; // Increment the nextNoteIndex rather than splicing the array, because splicing is slow.
     }
 
     // Update rendering of notes.
@@ -376,6 +387,17 @@ class Strumline extends FlxSpriteGroup
     }
   }
 
+  /**
+   * Called when the PlayState skips a large amount of time forward or backward.
+   */
+  public function handleSkippedNotes():Void
+  {
+    // By calling clean(), we remove all existing notes so they can be re-added.
+    clean();
+    // By setting noteIndex to 0, the next update will skip past all the notes that are in the past.
+    nextNoteIndex = 0;
+  }
+
   public function onBeatHit():Void
   {
     if (notes.members.length > 1) notes.members.insertionSort(compareNoteSprites.bind(FlxSort.ASCENDING));
diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx
index 8cf496637..c57835ca7 100644
--- a/source/funkin/ui/debug/charting/ChartEditorState.hx
+++ b/source/funkin/ui/debug/charting/ChartEditorState.hx
@@ -3063,6 +3063,7 @@ class ChartEditorState extends HaxeUIState
     var targetSong:Song = Song.buildRaw(currentSongId, songMetadata.values(), availableVariations, songChartData, false);
 
     subStateClosed.add(fixCamera);
+    subStateClosed.add(updateConductor);
 
     openSubState(new PlayState(
       {
@@ -3080,10 +3081,17 @@ class ChartEditorState extends HaxeUIState
   {
     FlxG.cameras.reset(new FlxCamera());
     FlxG.camera.focusOn(new FlxPoint(FlxG.width / 2, FlxG.height / 2));
+    FlxG.camera.zoom = 1.0;
 
     add(this.component);
   }
 
+  function updateConductor(_:FlxSubState = null):Void
+  {
+    var targetPos = scrollPositionInMs;
+    Conductor.update(targetPos);
+  }
+
   /**
    * Loads an instrumental from an absolute file path, replacing the current instrumental.
    *