diff --git a/source/funkin/Conductor.hx b/source/funkin/Conductor.hx
index b8ded63da..f333b4072 100644
--- a/source/funkin/Conductor.hx
+++ b/source/funkin/Conductor.hx
@@ -45,6 +45,8 @@ class Conductor
    */
   public static var songPosition(default, null):Float = 0;
 
+  public static var songPositionNoOffset(default, null):Float = 0;
+
   /**
    * Beats per minute of the current song at the current time.
    */
@@ -144,13 +146,27 @@ class Conductor
    */
   public static var currentStepTime(default, null):Float;
 
+  public static var currentStepTimeNoOffset(default, null):Float;
+
   public static var beatHit(default, null):FlxSignal = new FlxSignal();
   public static var stepHit(default, null):FlxSignal = new FlxSignal();
 
   public static var lastSongPos:Float;
-  public static var visualOffset:Float = 0;
-  public static var audioOffset:Float = 0;
-  public static var offset:Float = 0;
+
+  /**
+   * An offset tied to the current chart file to compensate for a delay in the instrumental.
+   */
+  public static var instrumentalOffset:Float = 0;
+
+  /**
+   * An offset tied to the file format of the audio file being played.
+   */
+  public static var formatOffset:Float = 0;
+
+  /**
+   * An offset set by the user to compensate for input lag.
+   */
+  public static var inputOffset:Float = 0;
 
   public static var beatsPerMeasure(get, never):Float;
 
@@ -200,15 +216,24 @@ class Conductor
    * @param	songPosition The current position in the song in milliseconds.
    *        Leave blank to use the FlxG.sound.music position.
    */
-  public static function update(songPosition:Float = null)
+  public static function update(?songPosition:Float)
   {
-    if (songPosition == null) songPosition = (FlxG.sound.music != null) ? FlxG.sound.music.time + Conductor.offset : 0.0;
+    if (songPosition == null)
+    {
+      // Take into account instrumental and file format song offsets.
+      songPosition = (FlxG.sound.music != null) ? (FlxG.sound.music.time + instrumentalOffset + formatOffset) : 0.0;
+    }
 
     var oldBeat = currentBeat;
     var oldStep = currentStep;
 
+    // Set the song position we are at (for purposes of calculating note positions, etc).
     Conductor.songPosition = songPosition;
 
+    // Exclude the offsets we just included earlier.
+    // This is the "true" song position that the audio should be using.
+    Conductor.songPositionNoOffset = Conductor.songPosition - instrumentalOffset - formatOffset;
+
     currentTimeChange = timeChanges[0];
     for (i in 0...timeChanges.length)
     {
@@ -230,6 +255,9 @@ class Conductor
       currentStep = Math.floor(currentStepTime);
       currentBeat = Math.floor(currentBeatTime);
       currentMeasure = Math.floor(currentMeasureTime);
+
+      currentStepTimeNoOffset = FlxMath.roundDecimal((currentTimeChange.beatTime * 4)
+        + (songPositionNoOffset - currentTimeChange.timeStamp) / stepLengthMs, 6);
     }
     else
     {
@@ -240,6 +268,8 @@ class Conductor
       currentStep = Math.floor(currentStepTime);
       currentBeat = Math.floor(currentBeatTime);
       currentMeasure = Math.floor(currentMeasureTime);
+
+      currentStepTimeNoOffset = FlxMath.roundDecimal((songPositionNoOffset / stepLengthMs), 4);
     }
 
     // FlxSignals are really cool.
diff --git a/source/funkin/audio/VoicesGroup.hx b/source/funkin/audio/VoicesGroup.hx
index 6d61e6481..8c95eb3eb 100644
--- a/source/funkin/audio/VoicesGroup.hx
+++ b/source/funkin/audio/VoicesGroup.hx
@@ -11,12 +11,22 @@ class VoicesGroup extends SoundGroup
   /**
    * Control the volume of only the sounds in the player group.
    */
-  public var playerVolume(default, set):Float;
+  public var playerVolume(default, set):Float = 1.0;
 
   /**
    * Control the volume of only the sounds in the opponent group.
    */
-  public var opponentVolume(default, set):Float;
+  public var opponentVolume(default, set):Float = 1.0;
+
+  /**
+   * Set the time offset for the player's vocal track.
+   */
+  public var playerVoicesOffset(default, set):Float = 0.0;
+
+  /**
+   * Set the time offset for the opponent's vocal track.
+   */
+  public var opponentVoicesOffset(default, set):Float = 0.0;
 
   public function new()
   {
@@ -42,6 +52,57 @@ class VoicesGroup extends SoundGroup
     return playerVolume = volume;
   }
 
+  override function set_time(time:Float):Float
+  {
+    forEachAlive(function(snd) {
+      // account for different offsets per sound?
+      snd.time = time;
+    });
+
+    playerVoices.forEachAlive(function(voice:FlxSound) {
+      voice.time -= playerVoicesOffset;
+    });
+    opponentVoices.forEachAlive(function(voice:FlxSound) {
+      voice.time -= opponentVoicesOffset;
+    });
+
+    return time;
+  }
+
+  function set_playerVoicesOffset(offset:Float):Float
+  {
+    playerVoices.forEachAlive(function(voice:FlxSound) {
+      voice.time += playerVoicesOffset;
+      voice.time -= offset;
+    });
+    return playerVoicesOffset = offset;
+  }
+
+  function set_opponentVoicesOffset(offset:Float):Float
+  {
+    opponentVoices.forEachAlive(function(voice:FlxSound) {
+      voice.time += opponentVoicesOffset;
+      voice.time -= offset;
+    });
+    return opponentVoicesOffset = offset;
+  }
+
+  public override function update(elapsed:Float):Void
+  {
+    forEachAlive(function(snd) {
+      if (snd.time < 0)
+      {
+        // Sync the time without calling update().
+        // time gets reset if it's negative.
+        snd.time += elapsed * 1000;
+      }
+      else
+      {
+        snd.update(elapsed);
+      }
+    });
+  }
+
   /**
    * Add a voice to the opponent group.
    */
diff --git a/source/funkin/data/song/SongData.hx b/source/funkin/data/song/SongData.hx
index 663dcc9bd..c0fd96855 100644
--- a/source/funkin/data/song/SongData.hx
+++ b/source/funkin/data/song/SongData.hx
@@ -34,6 +34,12 @@ class SongMetadata
   @:default(false)
   public var looped:Bool;
 
+  /**
+   * Instrumental and vocal offsets. Optional, defaults to 0.
+   */
+  @:optional
+  public var offsets:SongOffsets;
+
   /**
    * Data relating to the song's gameplay.
    */
@@ -59,6 +65,7 @@ class SongMetadata
     this.artist = artist;
     this.timeFormat = 'ms';
     this.divisions = null;
+    this.offsets = new SongOffsets();
     this.timeChanges = [new SongTimeChange(0, 100)];
     this.looped = false;
     this.playData = new SongPlayData();
@@ -196,6 +203,90 @@ class SongTimeChange
   }
 }
 
+/**
+ * Offsets to apply to the song's instrumental and vocals, relative to the chart.
+ * These are intended to correct for issues with the chart, or with the song's audio (for example a 10ms delay before the song starts).
+ * This is independent of the offsets applied in the user's settings, which are applied after these offsets and intended to correct for the user's hardware.
+ */
+class SongOffsets
+{
+  /**
+   * The offset, in milliseconds, to apply to the song's instrumental relative to the chart.
+   * For example, setting this to `-10.0` will start the instrumental 10ms earlier than the chart.
+   *
+   * Setting this to `-5000.0` means the chart start 5 seconds into the song.
+   * Setting this to `5000.0` means there will be 5 seconds of silence before the song starts.
+   */
+  @:optional
+  @:default(0)
+  public var instrumental:Float;
+
+  /**
+   * Apply different offsets to different alternate instrumentals.
+   */
+  @:optional
+  @:default([])
+  public var altInstrumentals:Map<String, Float>;
+
+  /**
+   * The offset, in milliseconds, to apply to the song's vocals, relative to the chart.
+   * These are applied ON TOP OF the instrumental offset.
+   */
+  @:optional
+  @:default([])
+  public var vocals:Map<String, Float>;
+
+  public function new(instrumental:Float = 0.0, ?altInstrumentals:Map<String, Float>, ?vocals:Map<String, Float>)
+  {
+    this.instrumental = instrumental;
+    this.altInstrumentals = altInstrumentals == null ? new Map<String, Float>() : altInstrumentals;
+    this.vocals = vocals == null ? new Map<String, Float>() : vocals;
+  }
+
+  public function getInstrumentalOffset(?instrumental:String):Float
+  {
+    if (instrumental == null || instrumental == '') return this.instrumental;
+
+    if (!this.altInstrumentals.exists(instrumental)) return this.instrumental;
+
+    return this.altInstrumentals.get(instrumental);
+  }
+
+  public function setInstrumentalOffset(value:Float, ?instrumental:String):Float
+  {
+    if (instrumental == null || instrumental == '')
+    {
+      this.instrumental = value;
+    }
+    else
+    {
+      this.altInstrumentals.set(instrumental, value);
+    }
+    return value;
+  }
+
+  public function getVocalOffset(charId:String):Float
+  {
+    if (!this.vocals.exists(charId)) return 0.0;
+
+    return this.vocals.get(charId);
+  }
+
+  public function setVocalOffset(charId:String, value:Float):Float
+  {
+    this.vocals.set(charId, value);
+    return value;
+  }
+
+  /**
+   * Produces a string representation suitable for debugging.
+   */
+  public function toString():String
+  {
+    return 'SongOffsets(${this.instrumental}ms, ${this.altInstrumentals}, ${this.vocals})';
+  }
+}
+
 /**
  * Metadata for a song only used for the music.
  * For example, the menu music.
@@ -309,6 +400,7 @@ class SongPlayData
    * The difficulty ratings for this song as displayed in Freeplay.
    * Key is a difficulty ID or `default`.
    */
+  @:optional
   @:default(['default' => 1])
   public var ratings:Map<String, Int>;
 
diff --git a/source/funkin/data/song/SongRegistry.hx b/source/funkin/data/song/SongRegistry.hx
index 8e0f4577d..850654eb7 100644
--- a/source/funkin/data/song/SongRegistry.hx
+++ b/source/funkin/data/song/SongRegistry.hx
@@ -19,7 +19,7 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
    * Handle breaking changes by incrementing this value
    * and adding migration to the `migrateStageData()` function.
    */
-  public static final SONG_METADATA_VERSION:thx.semver.Version = "2.2.0";
+  public static final SONG_METADATA_VERSION:thx.semver.Version = "2.2.1";
 
   public static final SONG_METADATA_VERSION_RULE:thx.semver.VersionRule = "2.2.x";
 
diff --git a/source/funkin/data/song/migrator/SongData_v2_1_0.hx b/source/funkin/data/song/migrator/SongData_v2_1_0.hx
index 192c440e0..1a61184ed 100644
--- a/source/funkin/data/song/migrator/SongData_v2_1_0.hx
+++ b/source/funkin/data/song/migrator/SongData_v2_1_0.hx
@@ -16,6 +16,8 @@ class SongMetadata_v2_1_0
    */
   public var playData:SongPlayData_v2_1_0;
 
+  // In metadata `v2.2.1`, `SongOffsets` was added.
+  // var offsets:SongOffsets;
   // ==========
   // UNMODIFIED VALUES
   // ==========
diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx
index 7d20f4697..6136cf1b7 100644
--- a/source/funkin/play/PlayState.hx
+++ b/source/funkin/play/PlayState.hx
@@ -179,6 +179,13 @@ class PlayState extends MusicBeatSubState
    */
   public var songScore:Int = 0;
 
+  /**
+   * Start at this point in the song once the countdown is done.
+   * For example, if `startTimestamp` is `30000`, the song will start at the 30 second mark.
+   * Used for chart playtesting or practice.
+   */
+  public var startTimestamp:Float = 0.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.
@@ -254,10 +261,6 @@ class PlayState extends MusicBeatSubState
    */
   public var disableKeys:Bool = false;
 
-  public var startTimestamp:Float = 0.0;
-
-  var overrideMusic:Bool = false;
-
   public var isSubState(get, never):Bool;
 
   function get_isSubState():Bool
@@ -317,6 +320,18 @@ class PlayState extends MusicBeatSubState
    */
   var skipHeldTimer:Float = 0;
 
+  /**
+   * Whether the PlayState was started with instrumentals and vocals already provided.
+   * Used by the chart editor to prevent replacing the music.
+   */
+  var overrideMusic:Bool = false;
+
+  /**
+   * After the song starts, the song offset may dictate we wait before the instrumental starts.
+   * This variable represents that delay, and is subtracted from until it reaches 0, before calling `music.play()`
+   */
+  var songStartDelay:Float = 0.0;
+
   /**
    * Forcibly disables all update logic while the game moves back to the Menu state.
    * This is used only when a critical error occurs and the game absolutely cannot continue.
@@ -551,6 +566,7 @@ class PlayState extends MusicBeatSubState
 
     // Prepare the Conductor.
     Conductor.forceBPM(null);
+    Conductor.instrumentalOffset = currentChart.offsets.getInstrumentalOffset();
     Conductor.mapTimeChanges(currentChart.timeChanges);
     Conductor.update((Conductor.beatLengthMs * -5) + startTimestamp);
 
@@ -699,8 +715,8 @@ class PlayState extends MusicBeatSubState
 
       // Reset music properly.
 
+      FlxG.sound.music.time = Math.max(0, startTimestamp - Conductor.instrumentalOffset);
       FlxG.sound.music.pause();
-      FlxG.sound.music.time = (startTimestamp);
 
       if (!overrideMusic)
       {
@@ -751,17 +767,40 @@ class PlayState extends MusicBeatSubState
       if (isInCountdown)
       {
         Conductor.update(Conductor.songPosition + elapsed * 1000);
-        if (Conductor.songPosition >= startTimestamp) startSong();
+        if (Conductor.songPosition >= (startTimestamp)) startSong();
       }
     }
     else
     {
-      // DO NOT FORGET TO REMOVE THE HARDCODE! WHEN I MAKE BETTER OFFSET SYSTEM!
+      if (Constants.EXT_SOUND == 'mp3')
+      {
+        Conductor.formatOffset = Constants.MP3_DELAY_MS;
+      }
+      else
+      {
+        Conductor.formatOffset = 0.0;
+      }
 
-      // :nerd: um ackshually it's not 13 it's 11.97278911564
-      if (Constants.EXT_SOUND == 'mp3') Conductor.offset = Constants.MP3_DELAY_MS;
-
-      Conductor.update();
+      if (songStartDelay > 0)
+      {
+        // Code to handle the song not starting yet (positive instrumental offset in metadata).
+        // Wait for offset to elapse before actually hitting play on the instrumental.
+        songStartDelay -= elapsed * 1000;
+        if (songStartDelay <= 0)
+        {
+          FlxG.sound.music.play();
+          Conductor.update(); // Normal conductor update.
+        }
+        else
+        {
+          // Make beat events still happen.
+          Conductor.update(Conductor.songPosition + elapsed * 1000);
+        }
+      }
+      else
+      {
+        Conductor.update(); // Normal conductor update.
+      }
 
       if (!isGamePaused)
       {
@@ -1137,12 +1176,12 @@ class PlayState extends MusicBeatSubState
 
     if (!startingSong
       && FlxG.sound.music != null
-      && (Math.abs(FlxG.sound.music.time - (Conductor.songPosition - Conductor.offset)) > 200
-        || Math.abs(vocals.checkSyncError(Conductor.songPosition - Conductor.offset)) > 200))
+      && (Math.abs(FlxG.sound.music.time - (Conductor.songPositionNoOffset)) > 200
+        || Math.abs(vocals.checkSyncError(Conductor.songPositionNoOffset)) > 200))
     {
       trace("VOCALS NEED RESYNC");
-      if (vocals != null) trace(vocals.checkSyncError(Conductor.songPosition - Conductor.offset));
-      trace(FlxG.sound.music.time - (Conductor.songPosition - Conductor.offset));
+      if (vocals != null) trace(vocals.checkSyncError(Conductor.songPositionNoOffset));
+      trace(FlxG.sound.music.time - (Conductor.songPositionNoOffset));
       resyncVocals();
     }
 
@@ -1684,12 +1723,28 @@ class PlayState extends MusicBeatSubState
     }
 
     FlxG.sound.music.onComplete = endSong;
-    FlxG.sound.music.play();
-    FlxG.sound.music.time = startTimestamp;
+    // 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.time = startTimestamp - Conductor.instrumentalOffset;
+
     trace('Playing vocals...');
     add(vocals);
-    vocals.play();
-    resyncVocals();
+
+    if (FlxG.sound.music.time < 0)
+    {
+      // A positive instrumentalOffset means Conductor.songPosition will still be negative after the countdown elapses.
+      trace('POSITIVE OFFSET: Waiting to start song...');
+      songStartDelay = Math.abs(FlxG.sound.music.time);
+      FlxG.sound.music.time = 0;
+      FlxG.sound.music.pause();
+      vocals.pause();
+    }
+    else
+    {
+      FlxG.sound.music.play();
+      vocals.play();
+      resyncVocals();
+    }
 
     #if discord_rpc
     // Updating Discord Rich Presence (with Time Left)
@@ -1698,7 +1753,7 @@ class PlayState extends MusicBeatSubState
 
     if (startTimestamp > 0)
     {
-      FlxG.sound.music.time = startTimestamp;
+      FlxG.sound.music.time = startTimestamp - Conductor.instrumentalOffset;
       handleSkippedNotes();
     }
   }
@@ -1710,13 +1765,13 @@ class PlayState extends MusicBeatSubState
   {
     if (_exiting || vocals == null) return;
 
-    // Skip this if the music is paused (GameOver, Pause menu, etc.)
+    // Skip this if the music is paused (GameOver, Pause menu, start-of-song offset, etc.)
     if (!FlxG.sound.music.playing) return;
+    if (songStartDelay > 0) return;
 
     vocals.pause();
 
     FlxG.sound.music.play();
-    Conductor.update();
 
     vocals.time = FlxG.sound.music.time;
     vocals.play(false, FlxG.sound.music.time);
diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx
index 3d7903724..1cba42f60 100644
--- a/source/funkin/play/song/Song.hx
+++ b/source/funkin/play/song/Song.hx
@@ -1,21 +1,22 @@
 package funkin.play.song;
 
-import funkin.util.SortUtil;
 import flixel.sound.FlxSound;
-import openfl.utils.Assets;
-import funkin.modding.events.ScriptEvent;
-import funkin.modding.IScriptedClass;
 import funkin.audio.VoicesGroup;
-import funkin.data.song.SongRegistry;
+import funkin.data.IRegistryEntry;
+import funkin.data.song.SongData.SongCharacterData;
 import funkin.data.song.SongData.SongChartData;
 import funkin.data.song.SongData.SongEventData;
-import funkin.data.song.SongData.SongNoteData;
-import funkin.data.song.SongRegistry;
 import funkin.data.song.SongData.SongMetadata;
-import funkin.data.song.SongData.SongCharacterData;
+import funkin.data.song.SongData.SongNoteData;
+import funkin.data.song.SongData.SongOffsets;
 import funkin.data.song.SongData.SongTimeChange;
 import funkin.data.song.SongData.SongTimeFormat;
-import funkin.data.IRegistryEntry;
+import funkin.data.song.SongRegistry;
+import funkin.data.song.SongRegistry;
+import funkin.modding.events.ScriptEvent;
+import funkin.modding.IScriptedClass;
+import funkin.util.SortUtil;
+import openfl.utils.Assets;
 
 /**
  * This is a data structure managing information about the current song.
@@ -172,6 +173,7 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
         difficulty.timeChanges = metadata.timeChanges;
         difficulty.looped = metadata.looped;
         difficulty.generatedBy = metadata.generatedBy;
+        difficulty.offsets = metadata.offsets;
 
         difficulty.stage = metadata.playData.stage;
         difficulty.noteStyle = metadata.playData.noteStyle;
@@ -391,6 +393,7 @@ class SongDifficulty
   public var timeFormat:SongTimeFormat = Constants.DEFAULT_TIMEFORMAT;
   public var divisions:Null<Int> = null;
   public var looped:Bool = false;
+  public var offsets:SongOffsets = new SongOffsets();
   public var generatedBy:String = SongRegistry.DEFAULT_GENERATEDBY;
 
   public var timeChanges:Array<SongTimeChange> = [];
@@ -542,6 +545,9 @@ class SongDifficulty
       }
     }
 
+    result.playerVoicesOffset = offsets.getVocalOffset(characters.player);
+    result.opponentVoicesOffset = offsets.getVocalOffset(characters.opponent);
+
     return result;
   }
 }
diff --git a/source/funkin/ui/MusicBeatState.hx b/source/funkin/ui/MusicBeatState.hx
index 4e0e19d5e..7bdf8689c 100644
--- a/source/funkin/ui/MusicBeatState.hx
+++ b/source/funkin/ui/MusicBeatState.hx
@@ -84,6 +84,7 @@ class MusicBeatState extends FlxTransitionableState implements IEventHandler
   {
     // Display Conductor info in the watch window.
     FlxG.watch.addQuick("songPosition", Conductor.songPosition);
+    FlxG.watch.addQuick("musicTime", FlxG.sound.music?.time ?? 0.0);
     FlxG.watch.addQuick("bpm", Conductor.bpm);
     FlxG.watch.addQuick("currentMeasureTime", Conductor.currentBeatTime);
     FlxG.watch.addQuick("currentBeatTime", Conductor.currentBeatTime);
diff --git a/source/funkin/ui/MusicBeatSubState.hx b/source/funkin/ui/MusicBeatSubState.hx
index 64df6ee71..9dd755b62 100644
--- a/source/funkin/ui/MusicBeatSubState.hx
+++ b/source/funkin/ui/MusicBeatSubState.hx
@@ -66,6 +66,7 @@ class MusicBeatSubState extends FlxTransitionableSubState implements IEventHandl
 
     // Display Conductor info in the watch window.
     FlxG.watch.addQuick("songPosition", Conductor.songPosition);
+    FlxG.watch.addQuick("musicTime", FlxG.sound.music?.time ?? 0.0);
     FlxG.watch.addQuick("bpm", Conductor.bpm);
     FlxG.watch.addQuick("currentMeasureTime", Conductor.currentBeatTime);
     FlxG.watch.addQuick("currentBeatTime", Conductor.currentBeatTime);
diff --git a/source/funkin/ui/debug/latency/LatencyState.hx b/source/funkin/ui/debug/latency/LatencyState.hx
index 673b866f8..18b0010b2 100644
--- a/source/funkin/ui/debug/latency/LatencyState.hx
+++ b/source/funkin/ui/debug/latency/LatencyState.hx
@@ -192,15 +192,15 @@ class LatencyState extends MusicBeatSubState
 
     if (FlxG.keys.pressed.D) FlxG.sound.music.time += 1000 * FlxG.elapsed;
 
-    Conductor.update(swagSong.getTimeWithDiff() - Conductor.offset);
+    Conductor.update(swagSong.getTimeWithDiff() - Conductor.inputOffset);
     // Conductor.songPosition += (Timer.stamp() * 1000) - FlxG.sound.music.prevTimestamp;
 
     songPosVis.x = songPosToX(Conductor.songPosition);
-    songVisFollowAudio.x = songPosToX(Conductor.songPosition - Conductor.audioOffset);
-    songVisFollowVideo.x = songPosToX(Conductor.songPosition - Conductor.visualOffset);
+    songVisFollowAudio.x = songPosToX(Conductor.songPosition - Conductor.instrumentalOffset);
+    songVisFollowVideo.x = songPosToX(Conductor.songPosition - Conductor.inputOffset);
 
-    offsetText.text = "AUDIO Offset: " + Conductor.audioOffset + "ms";
-    offsetText.text += "\nVIDOE Offset: " + Conductor.visualOffset + "ms";
+    offsetText.text = "INST Offset: " + Conductor.instrumentalOffset + "ms";
+    offsetText.text += "\nINPUT Offset: " + Conductor.inputOffset + "ms";
     offsetText.text += "\ncurrentStep: " + Conductor.currentStep;
     offsetText.text += "\ncurrentBeat: " + Conductor.currentBeat;
 
@@ -221,24 +221,24 @@ class LatencyState extends MusicBeatSubState
     {
       if (FlxG.keys.justPressed.RIGHT)
       {
-        Conductor.audioOffset += 1 * multiply;
+        Conductor.instrumentalOffset += 1.0 * multiply;
       }
 
       if (FlxG.keys.justPressed.LEFT)
       {
-        Conductor.audioOffset -= 1 * multiply;
+        Conductor.instrumentalOffset -= 1.0 * multiply;
       }
     }
     else
     {
       if (FlxG.keys.justPressed.RIGHT)
       {
-        Conductor.visualOffset += 1 * multiply;
+        Conductor.inputOffset += 1.0 * multiply;
       }
 
       if (FlxG.keys.justPressed.LEFT)
       {
-        Conductor.visualOffset -= 1 * multiply;
+        Conductor.inputOffset -= 1.0 * multiply;
       }
     }
 
@@ -250,7 +250,7 @@ class LatencyState extends MusicBeatSubState
     }*/
 
     noteGrp.forEach(function(daNote:NoteSprite) {
-      daNote.y = (strumLine.y - ((Conductor.songPosition - Conductor.audioOffset) - daNote.noteData.time) * 0.45);
+      daNote.y = (strumLine.y - ((Conductor.songPosition - Conductor.instrumentalOffset) - daNote.noteData.time) * 0.45);
       daNote.x = strumLine.x + 30;
 
       if (daNote.y < strumLine.y) daNote.alpha = 0.5;
@@ -271,7 +271,7 @@ class LatencyState extends MusicBeatSubState
 
     var closestBeat:Int = Math.round(Conductor.songPosition / Conductor.beatLengthMs) % diffGrp.members.length;
     var getDiff:Float = Conductor.songPosition - (closestBeat * Conductor.beatLengthMs);
-    getDiff -= Conductor.visualOffset;
+    getDiff -= Conductor.inputOffset;
 
     // lil fix for end of song
     if (closestBeat == 0 && getDiff >= Conductor.beatLengthMs * 2) getDiff -= FlxG.sound.music.length;