diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx
index 8b7dee87e..f6bb463e7 100644
--- a/source/funkin/play/PlayState.hx
+++ b/source/funkin/play/PlayState.hx
@@ -97,7 +97,7 @@ typedef PlayStateParams =
   ?targetDifficulty:String,
   /**
    * The variation to play on.
-   * @default `Constants.DEFAULT_VARIATION` .
+   * @default `Constants.DEFAULT_VARIATION`
    */
   ?targetVariation:String,
   /**
@@ -118,8 +118,14 @@ typedef PlayStateParams =
   ?minimalMode:Bool,
   /**
    * If specified, the game will jump to the specified timestamp after the countdown ends.
+   * @default `0.0`
    */
   ?startTimestamp:Float,
+  /**
+   * If specified, the game will play the song with the given speed.
+   * @default `1.0` for 100% speed.
+   */
+  ?playbackRate:Float,
   /**
    * If specified, the game will not load the instrumental or vocal tracks,
    * and must be loaded externally.
@@ -210,6 +216,12 @@ class PlayState extends MusicBeatSubState
    */
   public var startTimestamp:Float = 0.0;
 
+  /**
+   * Play back the song at this speed.
+   * @default `1.0` for normal speed.
+   */
+  public var playbackRate:Float = 1.0;
+
   /**
    * An empty FlxObject contained in the scene.
    * The current gameplay camera will always follow this object. Tween its position to move the camera smoothly.
@@ -550,6 +562,7 @@ class PlayState extends MusicBeatSubState
     isPracticeMode = params.practiceMode ?? false;
     isMinimalMode = params.minimalMode ?? false;
     startTimestamp = params.startTimestamp ?? 0.0;
+    playbackRate = params.playbackRate ?? 1.0;
     overrideMusic = params.overrideMusic ?? false;
     previousCameraFollowPoint = params.cameraFollowPoint;
 
@@ -777,6 +790,7 @@ class PlayState extends MusicBeatSubState
 
       // Reset music properly.
       FlxG.sound.music.time = startTimestamp - Conductor.instance.instrumentalOffset;
+      FlxG.sound.music.pitch = playbackRate;
       FlxG.sound.music.pause();
 
       if (!overrideMusic)
@@ -1783,14 +1797,16 @@ class PlayState extends MusicBeatSubState
     // A negative instrumental offset means the song skips the first few milliseconds of the track.
     // This just gets added into the startTimestamp behavior so we don't need to do anything extra.
     FlxG.sound.music.play(true, startTimestamp - Conductor.instance.instrumentalOffset);
+    FlxG.sound.music.pitch = playbackRate;
 
     // I am going insane.
     FlxG.sound.music.volume = 1.0;
-    FlxG.sound.music.fadeTween.cancel();
+    if (FlxG.sound.music.fadeTween != null) FlxG.sound.music.fadeTween.cancel();
 
     trace('Playing vocals...');
     add(vocals);
     vocals.play();
+    vocals.pitch = playbackRate;
     resyncVocals();
 
     #if discord_rpc
diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx
index e46779483..2f1764ecd 100644
--- a/source/funkin/ui/debug/charting/ChartEditorState.hx
+++ b/source/funkin/ui/debug/charting/ChartEditorState.hx
@@ -5308,6 +5308,10 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
     var startTimestamp:Float = 0;
     if (playtestStartTime) startTimestamp = scrollPositionInMs + playheadPositionInMs;
 
+    var playbackRate:Float = (menubarItemPlaybackSpeed.value.toFloat() * 2.0) / 100.0;
+    playbackRate = Math.floor(playbackRate / 0.05) * 0.05; // Round to nearest 5%
+    playbackRate = Math.max(0.05, Math.min(2.0, playbackRate)); // Clamp to 5% to 200%
+
     var targetSong:Song;
     try
     {
@@ -5357,6 +5361,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
         practiceMode: playtestPracticeMode,
         minimalMode: minimal,
         startTimestamp: startTimestamp,
+        playbackRate: playbackRate,
         overrideMusic: true,
       });
 
diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx
index e4a6b96d8..a1799481e 100644
--- a/source/funkin/ui/freeplay/FreeplayState.hx
+++ b/source/funkin/ui/freeplay/FreeplayState.hx
@@ -1143,6 +1143,12 @@ class FreeplayState extends MusicBeatSubState
           targetSong: targetSong,
           targetDifficulty: targetDifficulty,
           targetVariation: targetVariation,
+          // TODO: Make this an option!
+          // startTimestamp: 0.0,
+          // TODO: Make this an option!
+          // playbackRate: 0.5,
+          practiceMode: false,
+          minimalMode: false,
         }, true);
     });
   }