diff --git a/.github/workflows/build-shit.yml b/.github/workflows/build-shit.yml
index 49bab1ac1..e217d1f18 100644
--- a/.github/workflows/build-shit.yml
+++ b/.github/workflows/build-shit.yml
@@ -13,8 +13,9 @@ jobs:
           apt update
           apt install -y sudo git curl unzip
       - name: Fix git config on posix runner
+        # this can't be {{ github.workspace }} because that's not docker-aware
         run: |
-          git config --global --add safe.directory ${{ github.workspace }}
+          git config --global --add safe.directory $GITHUB_WORKSPACE
       - name: Get checkout token
         uses: actions/create-github-app-token@v1
         id: app_token
@@ -90,8 +91,9 @@ jobs:
     runs-on: [self-hosted, macos]
     steps:
       - name: Fix git config on posix runner
+        # this can't be {{ github.workspace }} because that's not docker-aware
         run: |
-          git config --global --add safe.directory ${{ github.workspace }}
+          git config --global --add safe.directory $GITHUB_WORKSPACE
       - name: Get checkout token
         uses: actions/create-github-app-token@v1
         id: app_token
diff --git a/source/funkin/Conductor.hx b/source/funkin/Conductor.hx
index 01147ce31..7ac6382f2 100644
--- a/source/funkin/Conductor.hx
+++ b/source/funkin/Conductor.hx
@@ -35,7 +35,15 @@ class Conductor
    * You can also do stuff like store a reference to the Conductor and pass it around or temporarily replace it,
    * or have a second Conductor running at the same time, or other weird stuff like that if you need to.
    */
-  public static var instance:Conductor = new Conductor();
+  public static var instance(get, never):Conductor;
+
+  static var _instance:Null<Conductor> = null;
+
+  static function get_instance():Conductor
+  {
+    if (_instance == null) _instance = new Conductor();
+    return _instance;
+  }
 
   /**
    * Signal fired when the current Conductor instance advances to a new measure.
@@ -523,6 +531,6 @@ class Conductor
    */
   public static function reset():Void
   {
-    Conductor.instance = new Conductor();
+    _instance = new Conductor();
   }
 }
diff --git a/source/funkin/data/BaseRegistry.hx b/source/funkin/data/BaseRegistry.hx
index ad028fa94..7419d9425 100644
--- a/source/funkin/data/BaseRegistry.hx
+++ b/source/funkin/data/BaseRegistry.hx
@@ -55,6 +55,13 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo
 
     this.entries = new Map<String, T>();
     this.scriptedEntryIds = [];
+
+    // Lazy initialization of singletons should let this get called,
+    // but we have this check just in case.
+    if (FlxG.game != null)
+    {
+      FlxG.console.registerObject('registry$registryId', this);
+    }
   }
 
   /**
diff --git a/source/funkin/data/dialogue/ConversationRegistry.hx b/source/funkin/data/dialogue/ConversationRegistry.hx
index 9186ef786..4a8af2162 100644
--- a/source/funkin/data/dialogue/ConversationRegistry.hx
+++ b/source/funkin/data/dialogue/ConversationRegistry.hx
@@ -15,7 +15,14 @@ class ConversationRegistry extends BaseRegistry<Conversation, ConversationData>
 
   public static final CONVERSATION_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x";
 
-  public static final instance:ConversationRegistry = new ConversationRegistry();
+  public static var instance(get, never):ConversationRegistry;
+  static var _instance:Null<ConversationRegistry> = null;
+
+  static function get_instance():ConversationRegistry
+  {
+    if (_instance == null) _instance = new ConversationRegistry();
+    return _instance;
+  }
 
   public function new()
   {
diff --git a/source/funkin/data/dialogue/DialogueBoxRegistry.hx b/source/funkin/data/dialogue/DialogueBoxRegistry.hx
index 87205d96c..d07ea6da2 100644
--- a/source/funkin/data/dialogue/DialogueBoxRegistry.hx
+++ b/source/funkin/data/dialogue/DialogueBoxRegistry.hx
@@ -15,7 +15,14 @@ class DialogueBoxRegistry extends BaseRegistry<DialogueBox, DialogueBoxData>
 
   public static final DIALOGUEBOX_DATA_VERSION_RULE:thx.semver.VersionRule = "1.1.x";
 
-  public static final instance:DialogueBoxRegistry = new DialogueBoxRegistry();
+  public static var instance(get, never):DialogueBoxRegistry;
+  static var _instance:Null<DialogueBoxRegistry> = null;
+
+  static function get_instance():DialogueBoxRegistry
+  {
+    if (_instance == null) _instance = new DialogueBoxRegistry();
+    return _instance;
+  }
 
   public function new()
   {
diff --git a/source/funkin/data/dialogue/SpeakerRegistry.hx b/source/funkin/data/dialogue/SpeakerRegistry.hx
index 6bd301dd7..c76c6d766 100644
--- a/source/funkin/data/dialogue/SpeakerRegistry.hx
+++ b/source/funkin/data/dialogue/SpeakerRegistry.hx
@@ -15,7 +15,14 @@ class SpeakerRegistry extends BaseRegistry<Speaker, SpeakerData>
 
   public static final SPEAKER_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x";
 
-  public static final instance:SpeakerRegistry = new SpeakerRegistry();
+  public static var instance(get, never):SpeakerRegistry;
+  static var _instance:Null<SpeakerRegistry> = null;
+
+  static function get_instance():SpeakerRegistry
+  {
+    if (_instance == null) _instance = new SpeakerRegistry();
+    return _instance;
+  }
 
   public function new()
   {
diff --git a/source/funkin/data/level/LevelRegistry.hx b/source/funkin/data/level/LevelRegistry.hx
index 96712cba5..e37e78d8c 100644
--- a/source/funkin/data/level/LevelRegistry.hx
+++ b/source/funkin/data/level/LevelRegistry.hx
@@ -15,7 +15,14 @@ class LevelRegistry extends BaseRegistry<Level, LevelData>
 
   public static final LEVEL_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x";
 
-  public static final instance:LevelRegistry = new LevelRegistry();
+  public static var instance(get, never):LevelRegistry;
+  static var _instance:Null<LevelRegistry> = null;
+
+  static function get_instance():LevelRegistry
+  {
+    if (_instance == null) _instance = new LevelRegistry();
+    return _instance;
+  }
 
   public function new()
   {
diff --git a/source/funkin/data/notestyle/NoteStyleRegistry.hx b/source/funkin/data/notestyle/NoteStyleRegistry.hx
index ffb9bf490..5e9fa9a3d 100644
--- a/source/funkin/data/notestyle/NoteStyleRegistry.hx
+++ b/source/funkin/data/notestyle/NoteStyleRegistry.hx
@@ -15,7 +15,14 @@ class NoteStyleRegistry extends BaseRegistry<NoteStyle, NoteStyleData>
 
   public static final NOTE_STYLE_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x";
 
-  public static final instance:NoteStyleRegistry = new NoteStyleRegistry();
+  public static var instance(get, never):NoteStyleRegistry;
+  static var _instance:Null<NoteStyleRegistry> = null;
+
+  static function get_instance():NoteStyleRegistry
+  {
+    if (_instance == null) _instance = new NoteStyleRegistry();
+    return _instance;
+  }
 
   public function new()
   {
diff --git a/source/funkin/data/song/SongRegistry.hx b/source/funkin/data/song/SongRegistry.hx
index e7f74569c..d82e184a5 100644
--- a/source/funkin/data/song/SongRegistry.hx
+++ b/source/funkin/data/song/SongRegistry.hx
@@ -40,10 +40,17 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
   }
 
   /**
-   * TODO: What if there was a Singleton macro which created static functions
-   * that redirected to the instance?
+   * TODO: What if there was a Singleton macro which automatically created the property for us?
    */
-  public static final instance:SongRegistry = new SongRegistry();
+  public static var instance(get, never):SongRegistry;
+
+  static var _instance:Null<SongRegistry> = null;
+
+  static function get_instance():SongRegistry
+  {
+    if (_instance == null) _instance = new SongRegistry();
+    return _instance;
+  }
 
   public function new()
   {
diff --git a/source/funkin/data/stage/StageRegistry.hx b/source/funkin/data/stage/StageRegistry.hx
index b78292e5b..13a5afb8d 100644
--- a/source/funkin/data/stage/StageRegistry.hx
+++ b/source/funkin/data/stage/StageRegistry.hx
@@ -15,7 +15,14 @@ class StageRegistry extends BaseRegistry<Stage, StageData>
 
   public static final STAGE_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x";
 
-  public static final instance:StageRegistry = new StageRegistry();
+  public static var instance(get, never):StageRegistry;
+  static var _instance:Null<StageRegistry> = null;
+
+  static function get_instance():StageRegistry
+  {
+    if (_instance == null) _instance = new StageRegistry();
+    return _instance;
+  }
 
   public function new()
   {
diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx
index 8734d0c1d..a8cb879a3 100644
--- a/source/funkin/play/PlayState.hx
+++ b/source/funkin/play/PlayState.hx
@@ -1364,7 +1364,10 @@ class PlayState extends MusicBeatSubState
     }
 
     // Only zoom camera if we are zoomed by less than 35%.
-    if (FlxG.camera.zoom < (1.35 * defaultCameraZoom) && cameraZoomRate > 0 && Conductor.instance.currentBeat % cameraZoomRate == 0)
+    if (Preferences.zoomCamera
+      && FlxG.camera.zoom < (1.35 * defaultCameraZoom)
+      && cameraZoomRate > 0
+      && Conductor.instance.currentBeat % cameraZoomRate == 0)
     {
       // Zoom camera in (1.5%)
       currentCameraZoom += cameraZoomIntensity * defaultCameraZoom;
@@ -2106,8 +2109,7 @@ class PlayState extends MusicBeatSubState
         holdNote.handledMiss = true;
 
         // We dropped a hold note.
-        // Mute vocals and play miss animation, but don't penalize.
-        vocals.opponentVolume = 0;
+        // Play miss animation, but don't penalize.
         currentStage.getOpponent().playSingAnimation(holdNote.noteData.getDirection(), true);
       }
     }
diff --git a/source/funkin/play/cutscene/VideoCutscene.hx b/source/funkin/play/cutscene/VideoCutscene.hx
index ff56e0919..3da51185f 100644
--- a/source/funkin/play/cutscene/VideoCutscene.hx
+++ b/source/funkin/play/cutscene/VideoCutscene.hx
@@ -5,6 +5,7 @@ import flixel.FlxSprite;
 import flixel.tweens.FlxEase;
 import flixel.tweens.FlxTween;
 import flixel.util.FlxColor;
+import flixel.util.FlxSignal;
 import flixel.util.FlxTimer;
 #if html5
 import funkin.graphics.video.FlxVideo;
@@ -28,6 +29,31 @@ class VideoCutscene
   static var vid:FlxVideoSprite;
   #end
 
+  /**
+   * Called when the video is started.
+   */
+  public static final onVideoStarted:FlxSignal = new FlxSignal();
+
+  /**
+   * Called if the video is paused.
+   */
+  public static final onVideoPaused:FlxSignal = new FlxSignal();
+
+  /**
+   * Called if the video is resumed.
+   */
+  public static final onVideoResumed:FlxSignal = new FlxSignal();
+
+  /**
+   * Called if the video is restarted. onVideoStarted is not called.
+   */
+  public static final onVideoRestarted:FlxSignal = new FlxSignal();
+
+  /**
+   * Called when the video is ended or skipped.
+   */
+  public static final onVideoEnded:FlxSignal = new FlxSignal();
+
   /**
    * Play a video cutscene.
    * TODO: Currently this is hardcoded to start the countdown after the video is done.
@@ -94,6 +120,8 @@ class VideoCutscene
       PlayState.instance.add(vid);
 
       PlayState.instance.refresh();
+
+      onVideoStarted.dispatch();
     }
     else
     {
@@ -129,6 +157,8 @@ class VideoCutscene
         vid.y = 0;
         // vid.scale.set(0.5, 0.5);
       });
+
+      onVideoStarted.dispatch();
     }
     else
     {
@@ -143,6 +173,7 @@ class VideoCutscene
     if (vid != null)
     {
       vid.restartVideo();
+      onVideoRestarted.dispatch();
     }
     #end
 
@@ -156,6 +187,8 @@ class VideoCutscene
         // Resume the video if it was paused.
         vid.resume();
       }
+
+      onVideoRestarted.dispatch();
     }
     #end
   }
@@ -166,6 +199,7 @@ class VideoCutscene
     if (vid != null)
     {
       vid.pauseVideo();
+      onVideoPaused.dispatch();
     }
     #end
 
@@ -173,6 +207,45 @@ class VideoCutscene
     if (vid != null)
     {
       vid.pause();
+      onVideoPaused.dispatch();
+    }
+    #end
+  }
+
+  public static function hideVideo():Void
+  {
+    #if html5
+    if (vid != null)
+    {
+      vid.visible = false;
+      blackScreen.visible = false;
+    }
+    #end
+
+    #if hxCodec
+    if (vid != null)
+    {
+      vid.visible = false;
+      blackScreen.visible = false;
+    }
+    #end
+  }
+
+  public static function showVideo():Void
+  {
+    #if html5
+    if (vid != null)
+    {
+      vid.visible = true;
+      blackScreen.visible = false;
+    }
+    #end
+
+    #if hxCodec
+    if (vid != null)
+    {
+      vid.visible = true;
+      blackScreen.visible = false;
     }
     #end
   }
@@ -183,6 +256,7 @@ class VideoCutscene
     if (vid != null)
     {
       vid.resumeVideo();
+      onVideoResumed.dispatch();
     }
     #end
 
@@ -190,6 +264,7 @@ class VideoCutscene
     if (vid != null)
     {
       vid.resume();
+      onVideoResumed.dispatch();
     }
     #end
   }
@@ -240,6 +315,7 @@ class VideoCutscene
       {
         ease: FlxEase.quadInOut,
         onComplete: function(twn:FlxTween) {
+          onVideoEnded.dispatch();
           onCutsceneFinish(cutsceneType);
         }
       });
diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx
index 567c388c7..0248e09ee 100644
--- a/source/funkin/play/song/Song.hx
+++ b/source/funkin/play/song/Song.hx
@@ -139,7 +139,16 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
       for (vari in _data.playData.songVariations)
       {
         var variMeta:Null<SongMetadata> = fetchVariationMetadata(id, vari);
-        if (variMeta != null) _metadata.set(variMeta.variation, variMeta);
+        if (variMeta != null)
+        {
+          _metadata.set(variMeta.variation, variMeta);
+          trace('  Loaded variation: $vari');
+        }
+        else
+        {
+          FlxG.log.warn('[SONG] Failed to load variation metadata (${id}:${vari}), is the path correct?');
+          trace('  FAILED to load variation: $vari');
+        }
       }
     }
 
@@ -374,12 +383,17 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
 
   public function getFirstValidVariation(?diffId:String, ?possibleVariations:Array<String>):Null<String>
   {
-    if (variations == null) possibleVariations = variations;
+    if (possibleVariations == null)
+    {
+      possibleVariations = variations;
+      possibleVariations.sort(SortUtil.defaultsThenAlphabetically.bind(Constants.DEFAULT_VARIATION_LIST));
+    }
     if (diffId == null) diffId = listDifficulties(null, possibleVariations)[0];
 
-    for (variation in variations)
+    for (variationId in possibleVariations)
     {
-      if (difficulties.exists('$diffId-$variation')) return variation;
+      var variationSuffix = (variationId != Constants.DEFAULT_VARIATION) ? '-$variationId' : '';
+      if (difficulties.exists('$diffId$variationSuffix')) return variationId;
     }
 
     return null;
diff --git a/source/funkin/ui/debug/charting/ChartEditorState.hx b/source/funkin/ui/debug/charting/ChartEditorState.hx
index 8e82c0fd8..888398f34 100644
--- a/source/funkin/ui/debug/charting/ChartEditorState.hx
+++ b/source/funkin/ui/debug/charting/ChartEditorState.hx
@@ -4948,7 +4948,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
 
     playbarNoteSnap.text = '1/${noteSnapQuant}';
     playbarDifficulty.text = '${selectedDifficulty.toTitleCase()}';
-    // playbarBPM.text = 'BPM: ${(Conductor.currentTimeChange?.bpm ?? 0.0)}';
+    playbarBPM.text = 'BPM: ${(Conductor.instance.bpm ?? 0.0)}';
   }
 
   function handlePlayhead():Void
diff --git a/source/funkin/ui/freeplay/FreeplayState.hx b/source/funkin/ui/freeplay/FreeplayState.hx
index 249c7ffae..f7554197f 100644
--- a/source/funkin/ui/freeplay/FreeplayState.hx
+++ b/source/funkin/ui/freeplay/FreeplayState.hx
@@ -1017,7 +1017,14 @@ class FreeplayState extends MusicBeatSubState
 
     // Set the difficulty star count on the right.
     albumRoll.setDifficultyStars(daSong?.songRating);
-    albumRoll.albumId = daSong?.albumId ?? Constants.DEFAULT_ALBUM_ID;
+
+    // Set the album graphic and play the animation if relevant.
+    var newAlbumId:String = daSong?.albumId ?? Constants.DEFAULT_ALBUM_ID;
+    if (albumRoll.albumId != newAlbumId)
+    {
+      albumRoll.albumId = newAlbumId;
+      albumRoll.playIntro();
+    }
   }
 
   // Clears the cache of songs, frees up memory, they' ll have to be loaded in later tho function clearDaCache(actualSongTho:String)
diff --git a/source/funkin/ui/story/StoryMenuState.hx b/source/funkin/ui/story/StoryMenuState.hx
index 315444fb0..d3aa68c49 100644
--- a/source/funkin/ui/story/StoryMenuState.hx
+++ b/source/funkin/ui/story/StoryMenuState.hx
@@ -450,7 +450,11 @@ class StoryMenuState extends MusicBeatState
    */
   function changeDifficulty(change:Int = 0):Void
   {
-    var difficultyList:Array<String> = currentLevel.getDifficulties();
+    // "For now, NO erect in story mode" -Dave
+
+    var difficultyList:Array<String> = Constants.DEFAULT_DIFFICULTY_LIST;
+    // Use this line to displays all difficulties
+    // var difficultyList:Array<String> = currentLevel.getDifficulties();
     var currentIndex:Int = difficultyList.indexOf(currentDifficultyId);
 
     currentIndex += change;
diff --git a/source/funkin/util/Constants.hx b/source/funkin/util/Constants.hx
index c9b99ed46..c7bc03139 100644
--- a/source/funkin/util/Constants.hx
+++ b/source/funkin/util/Constants.hx
@@ -157,6 +157,11 @@ class Constants
    */
   public static final DEFAULT_VARIATION:String = 'default';
 
+  /**
+   * Standard variations used by the game.
+   */
+  public static final DEFAULT_VARIATION_LIST:Array<String> = ['default', 'erect', 'pico'];
+
   /**
    * The default intensity for camera zooms.
    */