diff --git a/hmm.json b/hmm.json
index 0134944ed..884f9eedc 100644
--- a/hmm.json
+++ b/hmm.json
@@ -1,5 +1,48 @@
 {
   "dependencies": [
+    {
+      "name": "discord_rpc",
+      "type": "git",
+      "dir": null,
+      "ref": "2d83fa8",
+      "url": "https://github.com/Aidan63/linc_discord-rpc"
+    },
+    {
+      "name": "flixel",
+      "type": "git",
+      "dir": null,
+      "ref": "75a2789",
+      "url": "https://github.com/haxeflixel/flixel"
+    },
+    {
+      "name": "flixel-addons",
+      "type": "git",
+      "dir": null,
+      "ref": "dev",
+      "url": "https://github.com/MasterEric/flixel-addons"
+    },
+    {
+      "name": "flixel-ui",
+      "type": "haxelib",
+      "version": "2.4.0"
+    },
+    {
+      "name": "flxanimate",
+      "type": "git",
+      "dir": null,
+      "ref": "90c44ca",
+      "url": "https://github.com/Dot-Stuff/flxanimate"
+    },
+    {
+      "name": "hmm",
+      "type": "haxelib",
+      "version": "2.1.0"
+    },
+    {
+      "name": "hscript",
+      "type": "haxelib",
+      "version": "2.5.0"
+    },
     {
       "name": "hxcpp",
       "type": "haxelib",
@@ -10,11 +53,6 @@
       "type": "haxelib",
       "version": "1.2.4"
     },
-    {
-      "name": "thx.semver",
-      "type": "haxelib",
-      "version": "0.2.2"
-    },
     {
       "name": "lime",
       "type": "haxelib",
@@ -25,48 +63,17 @@
       "type": "haxelib",
       "version": "9.1.0"
     },
-    {
-      "name": "flixel",
-      "type": "git",
-      "dir": null,
-      "ref": "75a2789",
-      "url": "https://github.com/haxeflixel/flixel"
-    },
-    {
-      "name": "flixel-addons",
-      "type": "haxelib",
-      "version": "2.11.0"
-    },
-    {
-      "name": "flixel-ui",
-      "type": "haxelib",
-      "version": "2.4.0"
-    },
-    {
-      "name": "discord_rpc",
-      "type": "git",
-      "dir": null,
-      "ref": "2d83fa8",
-      "url": "https://github.com/Aidan63/linc_discord-rpc"
-    },
-    {
-      "name": "hscript",
-      "type": "haxelib",
-      "version": "2.5.0"
-    },
     {
       "name": "polymod",
       "type": "git",
       "dir": null,
-      "ref": "4c2c949",
+      "ref": "bde0604",
       "url": "https://github.com/larsiusprime/polymod"
     },
     {
-      "name": "flxanimate",
-      "type": "git",
-      "dir": null,
-      "ref": "90c44ca",
-      "url": "https://github.com/Dot-Stuff/flxanimate"
+      "name": "thx.semver",
+      "type": "haxelib",
+      "version": "0.2.2"
     }
   ]
 }
\ No newline at end of file
diff --git a/source/funkin/MusicBeatState.hx b/source/funkin/MusicBeatState.hx
index 5e86ea022..74dcdb2d3 100644
--- a/source/funkin/MusicBeatState.hx
+++ b/source/funkin/MusicBeatState.hx
@@ -55,6 +55,9 @@ class MusicBeatState extends FlxUIState
 	{
 		super.update(elapsed);
 
+		if (FlxG.keys.justPressed.F4)
+			FlxG.switchState(new MainMenuState());
+
 		// This can now be used in EVERY STATE YAY!
 		if (FlxG.keys.justPressed.F5)
 			debug_refreshModules();
diff --git a/source/funkin/Paths.hx b/source/funkin/Paths.hx
index cf127df90..c80d465aa 100644
--- a/source/funkin/Paths.hx
+++ b/source/funkin/Paths.hx
@@ -59,6 +59,16 @@ class Paths
 		return getPath('data/$key.txt', TEXT, library);
 	}
 
+	inline static public function frag(key:String, ?library:String)
+	{
+		return getPath('shaders/$key.frag', TEXT, library);
+	}
+
+	inline static public function vert(key:String, ?library:String)
+	{
+		return getPath('shaders/$key.vert', TEXT, library);
+	}
+
 	inline static public function xml(key:String, ?library:String)
 	{
 		return getPath('data/$key.xml', TEXT, library);
diff --git a/source/funkin/TitleState.hx b/source/funkin/TitleState.hx
index cf8c6ea80..e65462232 100644
--- a/source/funkin/TitleState.hx
+++ b/source/funkin/TitleState.hx
@@ -1,16 +1,16 @@
 package funkin;
 
-import flixel.FlxObject;
 import flixel.FlxSprite;
 import flixel.FlxState;
+import flixel.addons.display.FlxRuntimeShader;
 import flixel.group.FlxGroup;
 import flixel.input.gamepad.FlxGamepad;
 import flixel.tweens.FlxEase;
 import flixel.tweens.FlxTween;
 import flixel.util.FlxColor;
+import flixel.util.FlxDirectionFlags;
 import flixel.util.FlxTimer;
 import funkin.audiovis.SpectogramSprite;
-import funkin.shaderslmfao.BuildingShaders;
 import funkin.shaderslmfao.ColorSwap;
 import funkin.shaderslmfao.TitleOutline;
 import funkin.ui.AtlasText;
@@ -20,6 +20,7 @@ import openfl.display.Sprite;
 import openfl.events.AsyncErrorEvent;
 import openfl.events.MouseEvent;
 import openfl.events.NetStatusEvent;
+import openfl.filters.ShaderFilter;
 import openfl.media.Video;
 import openfl.net.NetStream;
 
@@ -39,7 +40,6 @@ class TitleState extends MusicBeatState
 	var curWacky:Array<String> = [];
 	var lastBeat:Int = 0;
 	var swagShader:ColorSwap;
-	var alphaShader:BuildingShaders;
 
 	var video:Video;
 	var netStream:NetStream;
@@ -48,7 +48,6 @@ class TitleState extends MusicBeatState
 	override public function create():Void
 	{
 		swagShader = new ColorSwap();
-		alphaShader = new BuildingShaders();
 
 		curWacky = FlxG.random.getObject(getIntroTextShit());
 		FlxG.sound.cache(Paths.music('freakyMenu'));
@@ -160,26 +159,15 @@ class TitleState extends MusicBeatState
 		logoBl.updateHitbox();
 
 		outlineShaderShit = new TitleOutline();
-		// logoBl.shader = swagShader.shader;
-		// logoBl.shader = outlineShaderShit;
-
-		// trace();
-		// logoBl.screenCenter();
-		// logoBl.color = FlxColor.BLACK;
 
 		gfDance = new FlxSprite(FlxG.width * 0.4, FlxG.height * 0.07);
 		gfDance.frames = Paths.getSparrowAtlas('gfDanceTitle');
 		gfDance.animation.addByIndices('danceLeft', 'gfDance', [30, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], "", 24, false);
 		gfDance.animation.addByIndices('danceRight', 'gfDance', [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], "", 24, false);
 		gfDance.antialiasing = true;
+
 		add(gfDance);
 
-		// alphaShader.shader.funnyShit.input = gfDance.pixels; // old shit
-
-		logoBl.shader = alphaShader.shader;
-
-		// trace(alphaShader.shader.glFragmentSource)
-
 		// gfDance.shader = swagShader.shader;
 
 		// gfDance.shader = new TitleOutline();
@@ -455,13 +443,13 @@ class TitleState extends MusicBeatState
 		if (FlxG.keys.justPressed.ANY)
 		{
 			if (controls.NOTE_DOWN_P || controls.UI_DOWN_P)
-				codePress(FlxObject.DOWN);
+				codePress(FlxDirectionFlags.DOWN);
 			if (controls.NOTE_UP_P || controls.UI_UP_P)
-				codePress(FlxObject.UP);
+				codePress(FlxDirectionFlags.UP);
 			if (controls.NOTE_LEFT_P || controls.UI_LEFT_P)
-				codePress(FlxObject.LEFT);
+				codePress(FlxDirectionFlags.LEFT);
 			if (controls.NOTE_RIGHT_P || controls.UI_RIGHT_P)
-				codePress(FlxObject.RIGHT);
+				codePress(FlxDirectionFlags.RIGHT);
 		}
 	}
 
@@ -563,10 +551,12 @@ class TitleState extends MusicBeatState
 							createCoolText(['In association', 'with']);
 						case 7:
 							addMoreText('newgrounds');
-							ngSpr.visible = true;
+							if (ngSpr != null)
+								ngSpr.visible = true;
 						case 8:
 							deleteCoolText();
-							ngSpr.visible = false;
+							if (ngSpr != null)
+								ngSpr.visible = false;
 						case 9:
 							createCoolText([curWacky[0]]);
 						case 11:
diff --git a/source/funkin/animate/TimelineFrame.hx b/source/funkin/animate/TimelineFrame.hx
index 8cae38d76..6355a036f 100644
--- a/source/funkin/animate/TimelineFrame.hx
+++ b/source/funkin/animate/TimelineFrame.hx
@@ -1,9 +1,9 @@
 package funkin.animate;
 
-import funkin.animate.ParseAnimate.Frame;
 import flixel.FlxSprite;
 import flixel.input.mouse.FlxMouseEvent;
 import flixel.util.FlxColor;
+import funkin.animate.ParseAnimate.Frame;
 
 class TimelineFrame extends FlxSprite
 {
diff --git a/source/funkin/audiovis/SpectogramSprite.hx b/source/funkin/audiovis/SpectogramSprite.hx
index 89a57cbd9..8e5b69e48 100644
--- a/source/funkin/audiovis/SpectogramSprite.hx
+++ b/source/funkin/audiovis/SpectogramSprite.hx
@@ -1,16 +1,14 @@
 package funkin.audiovis;
 
-import funkin.audiovis.PolygonSpectogram.VISTYPE;
-import funkin.audiovis.VisShit.CurAudioInfo;
-import funkin.audiovis.dsp.FFT;
 import flixel.FlxSprite;
-import flixel.group.FlxGroup;
 import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
 import flixel.math.FlxMath;
 import flixel.math.FlxPoint;
 import flixel.math.FlxVector;
 import flixel.system.FlxSound;
 import flixel.util.FlxColor;
+import funkin.audiovis.PolygonSpectogram.VISTYPE;
+import funkin.audiovis.VisShit.CurAudioInfo;
 import lime.utils.Int16Array;
 
 using Lambda;
@@ -113,7 +111,7 @@ class SpectogramSprite extends FlxTypedSpriteGroup<FlxSprite>
 				prevLine.x = (curAud.balanced * swagheight / 2 + swagheight / 2) + x;
 				prevLine.y = (i / group.members.length * daHeight) + y;
 
-				var line = FlxPoint.get(prevLine.x - group.members[i].x, prevLine.y - group.members[i].y);
+				var line = FlxVector.get(prevLine.x - group.members[i].x, prevLine.y - group.members[i].y);
 
 				group.members[i].setGraphicSize(Std.int(Math.max(line.length, 1)), Std.int(1));
 				group.members[i].angle = line.degrees;
@@ -290,7 +288,7 @@ class SpectogramSprite extends FlxTypedSpriteGroup<FlxSprite>
 					prevLine.x = (curAud.balanced * swagheight / 2 + swagheight / 2) + x;
 					prevLine.y = (Std.int(remappedSample) / lengthOfShit * daHeight) + y;
 
-					var line = FlxPoint.get(prevLine.x - group.members[Std.int(remappedSample)].x, prevLine.y - group.members[Std.int(remappedSample)].y);
+					var line = FlxVector.get(prevLine.x - group.members[Std.int(remappedSample)].x, prevLine.y - group.members[Std.int(remappedSample)].y);
 
 					group.members[Std.int(remappedSample)].setGraphicSize(Std.int(Math.max(line.length, 1)), Std.int(1));
 					group.members[Std.int(remappedSample)].angle = line.degrees;
diff --git a/source/funkin/modding/PolymodHandler.hx b/source/funkin/modding/PolymodHandler.hx
index d0e0bce4d..32cff8884 100644
--- a/source/funkin/modding/PolymodHandler.hx
+++ b/source/funkin/modding/PolymodHandler.hx
@@ -1,7 +1,7 @@
 package funkin.modding;
 
-import funkin.play.character.CharacterData.CharacterDataParser;
 import funkin.modding.module.ModuleHandler;
+import funkin.play.character.CharacterData.CharacterDataParser;
 import funkin.play.stage.StageData;
 import polymod.Polymod;
 import polymod.backends.PolymodAssets.PolymodAssetType;
@@ -157,7 +157,7 @@ class PolymodHandler
 	{
 		return {
 			assetLibraryPaths: [
-				"songs" => "songs",     "shared" => "", "tutorial" => "tutorial", "scripts" => "scripts", "week1" => "week1", "week2" => "week2",
+				"songs" => "songs",     "shared" => "", "tutorial" => "tutorial", "scripts" => "scripts", "week1" => "week1",      "week2" => "week2",
 				"week3" => "week3", "week4" => "week4",       "week5" => "week5",     "week6" => "week6", "week7" => "week7", "weekend1" => "weekend1",
 			]
 		}
@@ -223,7 +223,8 @@ class PolymodHandler
 		polymod.hscript.PolymodScriptClass.clearScriptClasses();
 
 		// Forcibly reload Polymod so it finds any new files.
-		loadEnabledMods();
+		// TODO: Replace this with loadEnabledMods().
+		funkin.modding.PolymodHandler.loadAllMods();
 
 		// Reload scripted classes so stages and modules will update.
 		polymod.hscript.PolymodScriptClass.registerAllScriptClasses();
diff --git a/source/funkin/modding/base/ScriptedFlxRuntimeShader.hx b/source/funkin/modding/base/ScriptedFlxRuntimeShader.hx
new file mode 100644
index 000000000..5b3bebd16
--- /dev/null
+++ b/source/funkin/modding/base/ScriptedFlxRuntimeShader.hx
@@ -0,0 +1,7 @@
+package funkin.modding.base;
+
+import flixel.addons.display.FlxRuntimeShader;
+import funkin.modding.IHook;
+
+@:hscriptClass
+class ScriptedFlxRuntimeShader extends FlxRuntimeShader implements IHook {}
diff --git a/source/funkin/modding/base/ScriptedFlxState.hx b/source/funkin/modding/base/ScriptedFlxState.hx
index 7be51230b..73aaf4247 100644
--- a/source/funkin/modding/base/ScriptedFlxState.hx
+++ b/source/funkin/modding/base/ScriptedFlxState.hx
@@ -3,6 +3,5 @@ package funkin.modding.base;
 import flixel.FlxState;
 import funkin.modding.IHook;
 
-// This doesn't work and is still a WIP
-// @:hscriptClass
-// class ScriptedFlxState extends FlxState implements IHook {}
+@:hscriptClass
+class ScriptedFlxState extends FlxState implements IHook {}
diff --git a/source/funkin/modding/base/ScriptedFlxSubState.hx b/source/funkin/modding/base/ScriptedFlxSubState.hx
new file mode 100644
index 000000000..46c54adeb
--- /dev/null
+++ b/source/funkin/modding/base/ScriptedFlxSubState.hx
@@ -0,0 +1,7 @@
+package funkin.modding.base;
+
+import flixel.FlxSubState;
+import funkin.modding.IHook;
+
+@:hscriptClass
+class ScriptedFlxSubState extends FlxSubState implements IHook {}
diff --git a/source/funkin/modding/base/ScriptedFlxTransitionableState.hx b/source/funkin/modding/base/ScriptedFlxTransitionableState.hx
new file mode 100644
index 000000000..b66124de3
--- /dev/null
+++ b/source/funkin/modding/base/ScriptedFlxTransitionableState.hx
@@ -0,0 +1,7 @@
+package funkin.modding.base;
+
+import flixel.addons.transition.FlxTransitionableState;
+import funkin.modding.IHook;
+
+@:hscriptClass
+class ScriptedFlxTransitionableState extends FlxTransitionableState implements IHook {}
diff --git a/source/funkin/modding/base/ScriptedFlxUIState.hx b/source/funkin/modding/base/ScriptedFlxUIState.hx
new file mode 100644
index 000000000..55358f43f
--- /dev/null
+++ b/source/funkin/modding/base/ScriptedFlxUIState.hx
@@ -0,0 +1,7 @@
+package funkin.modding.base;
+
+import flixel.addons.ui.FlxUIState;
+import funkin.modding.IHook;
+
+@:hscriptClass
+class ScriptedFlxUIState extends FlxUIState implements IHook {}
diff --git a/source/funkin/modding/base/ScriptedMusicBeatState.hx b/source/funkin/modding/base/ScriptedMusicBeatState.hx
new file mode 100644
index 000000000..31850c24c
--- /dev/null
+++ b/source/funkin/modding/base/ScriptedMusicBeatState.hx
@@ -0,0 +1,7 @@
+package funkin.modding.base;
+
+import funkin.MusicBeatState;
+import funkin.modding.IHook;
+
+@:hscriptClass
+class ScriptedMusicBeatState extends MusicBeatState implements IHook {}
diff --git a/source/funkin/modding/base/ScriptedMusicBeatSubstate.hx b/source/funkin/modding/base/ScriptedMusicBeatSubstate.hx
new file mode 100644
index 000000000..d18448b64
--- /dev/null
+++ b/source/funkin/modding/base/ScriptedMusicBeatSubstate.hx
@@ -0,0 +1,7 @@
+package funkin.modding.base;
+
+import funkin.MusicBeatSubstate;
+import funkin.modding.IHook;
+
+@:hscriptClass
+class ScriptedMusicBeatSubstate extends MusicBeatSubstate implements IHook {}
diff --git a/source/funkin/modding/module/ModuleHandler.hx b/source/funkin/modding/module/ModuleHandler.hx
index e533a39fd..4617fa35b 100644
--- a/source/funkin/modding/module/ModuleHandler.hx
+++ b/source/funkin/modding/module/ModuleHandler.hx
@@ -1,8 +1,10 @@
 package funkin.modding.module;
 
-import funkin.modding.events.ScriptEventDispatcher;
-import funkin.modding.events.ScriptEvent;
 import funkin.modding.events.ScriptEvent.UpdateScriptEvent;
+import funkin.modding.events.ScriptEvent;
+import funkin.modding.events.ScriptEventDispatcher;
+import funkin.modding.module.Module;
+import funkin.modding.module.ScriptedModule;
 
 using funkin.util.IteratorTools;
 
diff --git a/source/funkin/play/PicoFight.hx b/source/funkin/play/PicoFight.hx
index cafed7598..406062aba 100644
--- a/source/funkin/play/PicoFight.hx
+++ b/source/funkin/play/PicoFight.hx
@@ -6,9 +6,9 @@ import flixel.addons.effects.FlxTrail;
 import flixel.group.FlxGroup.FlxTypedGroup;
 import flixel.math.FlxMath;
 import flixel.util.FlxColor;
+import flixel.util.FlxDirectionFlags;
 import funkin.Note.NoteData;
 import funkin.audiovis.PolygonSpectogram;
-import flixel.util.FlxDirectionFlags;
 
 class PicoFight extends MusicBeatState
 {
@@ -137,7 +137,7 @@ class PicoFight extends MusicBeatState
 			// i forget how to make rhythm games
 			nt.x = (nt.ID - Conductor.songPosition) * (nt.ID / (Conductor.songPosition * 0.8));
 
-			if (nt.facing == FlxObject.RIGHT)
+			if (nt.facing == FlxDirectionFlags.RIGHT)
 			{
 				nt.x = FlxMath.remapToRange(nt.x, 0, FlxG.width, FlxG.width, 0);
 				nt.x -= FlxG.width / 2;
diff --git a/source/funkin/play/PlayState.hx b/source/funkin/play/PlayState.hx
index d27c4d99c..73c97ec00 100644
--- a/source/funkin/play/PlayState.hx
+++ b/source/funkin/play/PlayState.hx
@@ -5,6 +5,7 @@ import flixel.FlxObject;
 import flixel.FlxSprite;
 import flixel.FlxState;
 import flixel.FlxSubState;
+import flixel.addons.display.FlxRuntimeShader;
 import flixel.addons.transition.FlxTransitionableState;
 import flixel.group.FlxGroup;
 import flixel.math.FlxMath;
@@ -37,6 +38,8 @@ import funkin.ui.stageBuildShit.StageOffsetSubstate;
 import funkin.util.Constants;
 import funkin.util.SortUtil;
 import lime.ui.Haptic;
+import openfl.Assets;
+import openfl.filters.ShaderFilter;
 
 using StringTools;
 
@@ -280,11 +283,10 @@ class PlayState extends MusicBeatState implements IHook
 	{
 		super.create();
 
-		if (currentSong == null) {
-			lime.app.Application.current.window.alert(
-				"There was a critical error while accessing the selected song. Click OK to return to the main menu.",
-				"Error loading PlayState"
-			);
+		if (currentSong == null)
+		{
+			lime.app.Application.current.window.alert("There was a critical error while accessing the selected song. Click OK to return to the main menu.",
+				"Error loading PlayState");
 			FlxG.switchState(new MainMenuState());
 		}
 
@@ -474,8 +476,8 @@ class PlayState extends MusicBeatState implements IHook
 				currentStageId = 'mallXmas';
 			case 'winter-horrorland':
 				currentStageId = 'mallEvil';
-				case 'senpai' | 'roses':
-					currentStageId = 'school';
+			case 'senpai' | 'roses':
+				currentStageId = 'school';
 			case "darnell" | "lit-up" | "2hot":
 				// currentStageId = 'phillyStreets';
 				currentStageId = 'pyro';
@@ -592,15 +594,18 @@ class PlayState extends MusicBeatState implements IHook
 		{
 			// We're using Eric's stage handler.
 			// Characters get added to the stage, not the main scene.
-			if (girlfriend != null) {
+			if (girlfriend != null)
+			{
 				currentStage.addCharacter(girlfriend, GF);
 			}
 
-			if (boyfriend != null) {
+			if (boyfriend != null)
+			{
 				currentStage.addCharacter(boyfriend, BF);
 			}
 
-			if (dad != null) {
+			if (dad != null)
+			{
 				currentStage.addCharacter(dad, DAD);
 				// Camera starts at dad.
 				cameraFollowPoint.setPosition(dad.cameraFocusPoint.x, dad.cameraFocusPoint.y);
@@ -1061,7 +1066,7 @@ class PlayState extends MusicBeatState implements IHook
 
 			if (!event.eventCanceled)
 			{
-				// Pause updates while the substate is open, preventing the game state from advancing. 
+				// Pause updates while the substate is open, preventing the game state from advancing.
 				persistentUpdate = false;
 				// Enable drawing while the substate is open, allowing the game state to be shown behind the pause menu.
 				persistentDraw = true;
@@ -1191,11 +1196,14 @@ class PlayState extends MusicBeatState implements IHook
 				// Disable updates, preventing animations in the background from playing.
 				persistentUpdate = false;
 				#if debug
-				if (FlxG.keys.pressed.THREE) {
+				if (FlxG.keys.pressed.THREE)
+				{
 					// TODO: Change the key or delete this?
 					// In debug builds, pressing 3 to kill the player makes the background transparent.
 					persistentDraw = true;
-				} else {
+				}
+				else
+				{
 				#end
 					persistentDraw = false;
 				#if debug
diff --git a/source/funkin/play/character/ScriptedCharacter.hx b/source/funkin/play/character/ScriptedCharacter.hx
index 1ce8f7f93..9a87af968 100644
--- a/source/funkin/play/character/ScriptedCharacter.hx
+++ b/source/funkin/play/character/ScriptedCharacter.hx
@@ -1,9 +1,9 @@
 package funkin.play.character;
 
+import funkin.modding.IHook;
+import funkin.play.character.MultiSparrowCharacter;
 import funkin.play.character.PackerCharacter;
 import funkin.play.character.SparrowCharacter;
-import funkin.play.character.MultiSparrowCharacter;
-import funkin.modding.IHook;
 
 /**
  * Note: Making a scripted class extending BaseCharacter is not recommended.
diff --git a/source/funkin/play/stage/StageData.hx b/source/funkin/play/stage/StageData.hx
index e4d1904fd..e42eba5a3 100644
--- a/source/funkin/play/stage/StageData.hx
+++ b/source/funkin/play/stage/StageData.hx
@@ -1,6 +1,8 @@
 package funkin.play.stage;
 
 import flixel.util.typeLimit.OneOfTwo;
+import funkin.play.stage.ScriptedStage;
+import funkin.play.stage.Stage;
 import funkin.util.VersionUtil;
 import funkin.util.assets.DataAssets;
 import haxe.Json;
diff --git a/source/funkin/shaderslmfao/BuildingShaders.hx b/source/funkin/shaderslmfao/BuildingShaders.hx
deleted file mode 100644
index 62b9b0154..000000000
--- a/source/funkin/shaderslmfao/BuildingShaders.hx
+++ /dev/null
@@ -1,51 +0,0 @@
-package funkin.shaderslmfao;
-
-import flixel.system.FlxAssets.FlxShader;
-
-class BuildingShaders
-{
-	public var shader(default, null):BuildingShader;
-	public var daAlpha:Float = 1;
-
-	public function new():Void
-	{
-		shader = new BuildingShader();
-		shader.alphaShit.value = [0];
-	}
-
-	public function update(elapsed:Float):Void
-	{
-		shader.alphaShit.value[0] += elapsed;
-	}
-
-	public function reset()
-	{
-		shader.alphaShit.value[0] = 0;
-	}
-}
-
-class BuildingShader extends FlxShader
-{
-	@:glFragmentSource('
-        #pragma header
-
-        uniform float alphaShit;
-
-        void main()
-        {
-            vec4 color = flixel_texture2D(bitmap, openfl_TextureCoordv);
-
-			
-
-            if (color.a > 0.0)
-                color -= alphaShit;
-            
-            gl_FragColor = color;
-        }
-
-    ')
-	public function new()
-	{
-		super();
-	}
-}
diff --git a/source/funkin/shaderslmfao/WiggleEffect.hx b/source/funkin/shaderslmfao/WiggleEffect.hx
deleted file mode 100644
index bae55c6b1..000000000
--- a/source/funkin/shaderslmfao/WiggleEffect.hx
+++ /dev/null
@@ -1,159 +0,0 @@
-package funkin.shaderslmfao;
-
-// STOLEN FROM HAXEFLIXEL DEMO LOL
-import flixel.system.FlxAssets.FlxShader;
-
-enum WiggleEffectType
-{
-	DREAMY;
-	WAVY;
-	HEAT_WAVE_HORIZONTAL;
-	HEAT_WAVE_VERTICAL;
-	FLAG;
-}
-
-class WiggleEffect
-{
-	public var shader(default, null):WiggleShader = new WiggleShader();
-	public var effectType(default, set):WiggleEffectType = DREAMY;
-	public var waveSpeed(default, set):Float = 0;
-	public var waveFrequency(default, set):Float = 0;
-	public var waveAmplitude(default, set):Float = 0;
-
-	public function new(speed:Float, freq:Float, amplitude:Float, ?effect:WiggleEffectType = DREAMY):Void
-	{
-		shader.uTime.value = [0];
-		this.waveSpeed = speed;
-		this.waveFrequency = freq;
-		this.waveAmplitude = amplitude;
-		this.effectType = effect;
-	}
-
-	public function update(elapsed:Float):Void
-	{
-		shader.uTime.value[0] += elapsed;
-	}
-
-	function set_effectType(v:WiggleEffectType):WiggleEffectType
-	{
-		effectType = v;
-		shader.effectType.value = [WiggleEffectType.getConstructors().indexOf(Std.string(v))];
-		return v;
-	}
-
-	function set_waveSpeed(v:Float):Float
-	{
-		waveSpeed = v;
-		shader.uSpeed.value = [waveSpeed];
-		return v;
-	}
-
-	function set_waveFrequency(v:Float):Float
-	{
-		waveFrequency = v;
-		shader.uFrequency.value = [waveFrequency];
-		return v;
-	}
-
-	function set_waveAmplitude(v:Float):Float
-	{
-		waveAmplitude = v;
-		shader.uWaveAmplitude.value = [waveAmplitude];
-		return v;
-	}
-
-	function toString()
-	{
-		return 'WiggleEffect(${shader.uTime.value[0]})';
-	}
-}
-
-class WiggleShader extends FlxShader
-{
-	@:glFragmentSource('
-		#pragma header
-		//uniform float tx, ty; // x,y waves phase
-		uniform float uTime;
-		
-		const int EFFECT_TYPE_DREAMY = 0;
-		const int EFFECT_TYPE_WAVY = 1;
-		const int EFFECT_TYPE_HEAT_WAVE_HORIZONTAL = 2;
-		const int EFFECT_TYPE_HEAT_WAVE_VERTICAL = 3;
-		const int EFFECT_TYPE_FLAG = 4;
-		
-		uniform int effectType;
-		
-		/**
-		 * How fast the waves move over time
-		 */
-		uniform float uSpeed;
-		
-		/**
-		 * Number of waves over time
-		 */
-		uniform float uFrequency;
-		
-		/**
-		 * How much the pixels are going to stretch over the waves
-		 */
-		uniform float uWaveAmplitude;
-
-		vec2 sineWave(vec2 pt)
-		{
-			float x = 0.0;
-			float y = 0.0;
-			
-			if (effectType == EFFECT_TYPE_DREAMY) 
-			{
-
-				float w = 1 / openfl_TextureSize.y;
-                float h = 1 / openfl_TextureSize.x;
-
-				// look mom, I know how to write shaders now
-
-				pt.x = floor(pt.x / h) * h;
-
-				float offsetX = sin(pt.x * uFrequency + uTime * uSpeed) * uWaveAmplitude;
-                pt.y += floor(offsetX / w) * w; // * (pt.y - 1.0); // <- Uncomment to stop bottom part of the screen from moving
-
-				
-				pt.y = floor(pt.y / w) * w;
-
-				float offsetY = sin(pt.y * (uFrequency / 2.0) + uTime * (uSpeed / 2.0)) * (uWaveAmplitude / 2.0);
-                pt.x += floor(offsetY / h) * h; // * (pt.y - 1.0); // <- Uncomment to stop bottom part of the screen from moving
-
-				
-
-			}
-			else if (effectType == EFFECT_TYPE_WAVY) 
-			{
-				float offsetY = sin(pt.x * uFrequency + uTime * uSpeed) * uWaveAmplitude;
-				pt.y += offsetY; // * (pt.y - 1.0); // <- Uncomment to stop bottom part of the screen from moving
-			}
-			else if (effectType == EFFECT_TYPE_HEAT_WAVE_HORIZONTAL)
-			{
-				x = sin(pt.x * uFrequency + uTime * uSpeed) * uWaveAmplitude;
-			}
-			else if (effectType == EFFECT_TYPE_HEAT_WAVE_VERTICAL)
-			{
-				y = sin(pt.y * uFrequency + uTime * uSpeed) * uWaveAmplitude;
-			}
-			else if (effectType == EFFECT_TYPE_FLAG)
-			{
-				y = sin(pt.y * uFrequency + 10.0 * pt.x + uTime * uSpeed) * uWaveAmplitude;
-				x = sin(pt.x * uFrequency + 5.0 * pt.y + uTime * uSpeed) * uWaveAmplitude;
-			}
-			
-			return vec2(pt.x + x, pt.y + y);
-		}
-
-		void main()
-		{
-			vec2 uv = sineWave(openfl_TextureCoordv);
-			gl_FragColor = texture2D(bitmap, uv);
-		}')
-	public function new()
-	{
-		super();
-	}
-}
diff --git a/source/funkin/shaderslmfao/WiggleEffectRuntime.hx b/source/funkin/shaderslmfao/WiggleEffectRuntime.hx
new file mode 100644
index 000000000..cfa87f3d0
--- /dev/null
+++ b/source/funkin/shaderslmfao/WiggleEffectRuntime.hx
@@ -0,0 +1,84 @@
+package funkin.shaderslmfao;
+
+import flixel.addons.display.FlxRuntimeShader;
+import openfl.Assets;
+
+enum WiggleEffectType
+{
+	DREAMY; // 0
+	WAVY; // 1
+	HEAT_WAVE_HORIZONTAL; // 2
+	HEAT_WAVE_VERTICAL; // 3
+	FLAG; // 4
+}
+
+/**
+ * To use:
+ * 1. Create an instance of the class, specifying speed, frequency, and amplitude.
+ * 2. Call `sprite.shader = wiggleEffect` on the target sprite.
+ * 3. Call the update() method on the instance every frame.
+ */
+class WiggleEffectRuntime extends FlxRuntimeShader
+{
+	public static function getEffectTypeId(v:WiggleEffectType):Int
+	{
+		return WiggleEffectType.getConstructors().indexOf(Std.string(v));
+	}
+
+	public var effectType(default, set):WiggleEffectType = DREAMY;
+
+	function set_effectType(v:WiggleEffectType):WiggleEffectType
+	{
+		this.setInt('effectType', getEffectTypeId(v));
+		return effectType = v;
+	}
+
+	public var waveSpeed(default, set):Float = 0;
+
+	function set_waveSpeed(v:Float):Float
+	{
+		this.setFloat('uSpeed', v);
+		return waveSpeed = v;
+	}
+
+	public var waveFrequency(default, set):Float = 0;
+
+	function set_waveFrequency(v:Float):Float
+	{
+		this.setFloat('uFrequency', v);
+		return waveFrequency = v;
+	}
+
+	public var waveAmplitude(default, set):Float = 0;
+
+	function set_waveAmplitude(v:Float):Float
+	{
+		this.setFloat('uWaveAmplitude', v);
+		return waveAmplitude = v;
+	}
+
+	var time(default, set):Float = 0;
+
+	function set_time(v:Float):Float
+	{
+		this.setFloat('uTime', v);
+		return time = v;
+	}
+
+	public function new(speed:Float, freq:Float, amplitude:Float, ?effect:WiggleEffectType = DREAMY):Void
+	{
+		super(Assets.getText(Paths.frag('wiggle')));
+
+		// These values may not propagate to the shader until later.
+		this.waveSpeed = speed;
+		this.waveFrequency = freq;
+		this.waveAmplitude = amplitude;
+		this.effectType = effect;
+	}
+
+	public function update(elapsed:Float)
+	{
+		// The setter tied to this value automatically propagates the value to the shader.
+		this.time += elapsed;
+	}
+}