diff --git a/source/Main.hx b/source/Main.hx
index 7f7d25235..724b118f8 100644
--- a/source/Main.hx
+++ b/source/Main.hx
@@ -67,14 +67,9 @@ class Main extends Sprite
   function init(?event:Event):Void
   {
     #if web
-    untyped js.Syntax.code("
-    window.requestAnimationFrame = function(callback, element) {
-                    var currTime = new Date().getTime();
-                    var timeToCall = 0;
-                    var id = window.setTimeout(function() { callback(currTime + timeToCall); },
-                      timeToCall);
-                    return id;
-                }");
+    // set this variable (which is a function) from the lime version at lime/_internal/backend/html5/HTML5Application.hx
+    // The framerate cap will more thoroughly initialize via Preferences in InitState.hx
+    funkin.Preferences.lockedFramerateFunction = untyped js.Syntax.code("window.requestAnimationFrame");
     #end
 
     if (hasEventListener(Event.ADDED_TO_STAGE))
diff --git a/source/funkin/Preferences.hx b/source/funkin/Preferences.hx
index b2050c6a2..daeded897 100644
--- a/source/funkin/Preferences.hx
+++ b/source/funkin/Preferences.hx
@@ -128,6 +128,48 @@ class Preferences
     return value;
   }
 
+  public static var unlockedFramerate(get, set):Bool;
+
+  static function get_unlockedFramerate():Bool
+  {
+    return Save?.instance?.options?.unlockedFramerate;
+  }
+
+  static function set_unlockedFramerate(value:Bool):Bool
+  {
+    if (value != Save.instance.options.unlockedFramerate)
+    {
+      #if web
+      toggleFramerateCap(value);
+      #end
+    }
+
+    var save:Save = Save.instance;
+    save.options.unlockedFramerate = value;
+    save.flush();
+    return value;
+  }
+
+  #if web
+  // We create a haxe version of this just for readability.
+  // We use these to override `window.requestAnimationFrame` in Javascript to uncap the framerate / "animation" request rate
+  // Javascript is crazy since u can just do stuff like that lol
+
+  public static function unlockedFramerateFunction(callback, element)
+  {
+    var currTime = Date.now().getTime();
+    var timeToCall = 0;
+    var id = js.Browser.window.setTimeout(function() {
+      callback(currTime + timeToCall);
+    }, timeToCall);
+    return id;
+  }
+
+  // Lime already implements their own little framerate cap, so we can just use that
+  // This also gets set in the init function in Main.hx, since we need to definitely override it
+  public static var lockedFramerateFunction = untyped js.Syntax.code("window.requestAnimationFrame");
+  #end
+
   /**
    * Loads the user's preferences from the save data and apply them.
    */
@@ -137,6 +179,17 @@ class Preferences
     FlxG.autoPause = Preferences.autoPause;
     // Apply the debugDisplay setting (enables the FPS and RAM display).
     toggleDebugDisplay(Preferences.debugDisplay);
+    #if web
+    toggleFramerateCap(Preferences.unlockedFramerate);
+    #end
+  }
+
+  static function toggleFramerateCap(unlocked:Bool):Void
+  {
+    #if web
+    var framerateFunction = unlocked ? unlockedFramerateFunction : lockedFramerateFunction;
+    untyped js.Syntax.code("window.requestAnimationFrame = framerateFunction;");
+    #end
   }
 
   static function toggleDebugDisplay(show:Bool):Void
diff --git a/source/funkin/save/Save.hx b/source/funkin/save/Save.hx
index 1fa283b26..4b1649c5b 100644
--- a/source/funkin/save/Save.hx
+++ b/source/funkin/save/Save.hx
@@ -97,6 +97,7 @@ class Save
           autoPause: true,
           inputOffset: 0,
           audioVisualOffset: 0,
+          unlockedFramerate: false,
 
           controls:
             {
@@ -1171,6 +1172,12 @@ typedef SaveDataOptions =
    */
   var audioVisualOffset:Int;
 
+  /**
+   * If we want the framerate to be unlocked on HTML5.
+   * @default `false
+   */
+  var unlockedFramerate:Bool;
+
   var controls:
     {
       var p1:
diff --git a/source/funkin/ui/options/PreferencesMenu.hx b/source/funkin/ui/options/PreferencesMenu.hx
index 5fbefceed..eb7b88792 100644
--- a/source/funkin/ui/options/PreferencesMenu.hx
+++ b/source/funkin/ui/options/PreferencesMenu.hx
@@ -72,6 +72,12 @@ class PreferencesMenu extends Page
     createPrefItemCheckbox('Auto Pause', 'Automatically pause the game when it loses focus', function(value:Bool):Void {
       Preferences.autoPause = value;
     }, Preferences.autoPause);
+
+    #if web
+    createPrefItemCheckbox('Unlocked Framerate', 'Enable to unlock the framerate', function(value:Bool):Void {
+      Preferences.unlockedFramerate = value;
+    }, Preferences.unlockedFramerate);
+    #end
   }
 
   override function update(elapsed:Float):Void