diff --git a/source/backend/ClientPrefs.hx b/source/backend/ClientPrefs.hx
index 60b328dd..34f3a0a9 100644
--- a/source/backend/ClientPrefs.hx
+++ b/source/backend/ClientPrefs.hx
@@ -116,6 +116,7 @@ class ClientPrefs {
 		'pause'			=> [ENTER, ESCAPE],
 		'reset'			=> [R],
 		'taunt'			=> [SPACE],
+		'sidebar'		=> [GRAVEACCENT],
 		
 		'volume_mute'	=> [ZERO],
 		'volume_up'		=> [NUMPADPLUS, PLUS],
@@ -139,7 +140,8 @@ class ClientPrefs {
 		'back'			=> [B],
 		'pause'			=> [START],
 		'reset'			=> [BACK],
-		'taunt'			=> [A]
+		'taunt'			=> [A],
+		'sidebar'		=> []
 	];
 	public static var defaultKeys:Map<String, Array<FlxKey>> = null;
 	public static var defaultButtons:Map<String, Array<FlxGamepadInputID>> = null;
diff --git a/source/backend/Controls.hx b/source/backend/Controls.hx
index 65e88a2c..03fc2ad8 100644
--- a/source/backend/Controls.hx
+++ b/source/backend/Controls.hx
@@ -80,11 +80,13 @@ class Controls
 	public var PAUSE(get, never):Bool;
 	public var RESET(get, never):Bool;
 	public var TAUNT(get, never):Bool;
+	public var SIDEBAR(get, never):Bool;
 	private function get_ACCEPT() return justPressed('accept');
 	private function get_BACK() return justPressed('back');
 	private function get_PAUSE() return justPressed('pause');
 	private function get_RESET() return justPressed('reset');
 	private function get_TAUNT() return justPressed('taunt');
+	private function get_SIDEBAR() return justPressed('sidebar');
 
 	//Gamepad & Keyboard stuff
 	public var keyboardBinds:Map<String, Array<FlxKey>>;
diff --git a/source/backend/InputFormatter.hx b/source/backend/InputFormatter.hx
index 027e787e..431da230 100644
--- a/source/backend/InputFormatter.hx
+++ b/source/backend/InputFormatter.hx
@@ -77,7 +77,7 @@ class InputFormatter {
 			//case SLASH:
 			//	return "/";
 			case GRAVEACCENT:
-				return "`";
+				return "Tilde";
 			case LBRACKET:
 				return "[";
 			//case BACKSLASH:
diff --git a/source/online/FunkinPoints.hx b/source/online/FunkinPoints.hx
index 1409d56a..90473d2a 100644
--- a/source/online/FunkinPoints.hx
+++ b/source/online/FunkinPoints.hx
@@ -2,7 +2,7 @@ package online;
 
 @:build(online.backend.Macros.getSetForwarder())
 class FunkinPoints {
-	@:forwardField(FlxG.save.data.funkinPointsv3, 0)
+	@:forwardField(FlxG.save.data.funkinPointsv4, 0)
 	public static var funkinPoints(get, set):Float;
 
     public static function calcFP(accuracy:Float, misses:Float, denseNotes:Float, notesHit:Float, maxCombo:Float):Float {
@@ -10,11 +10,13 @@ class FunkinPoints {
             return 0;
 
 		// depends on player's hitted notes and the density of all notes
+		// hard songs will average somewhere between 3.0 (spookeez erect) and 13.0 (ballistic)
+		// so for a song with 3.0 density (ex. spookeez erect), for every ~65 hitted notes a player will gain 1 fp (without combo bonus)
 		var fp:Float = Math.max(1, 1 + denseNotes) * (notesHit / 200);
-		// depends on player's note streak (1000 combo doubles base fp)
-		fp *= 1 + maxCombo / 1000;
-		// depends on player's note accuracy (weighted by power of 4; 95% = x0.81, 90% = x0.65, 80% = x0.40)
-		fp *= Math.pow(accuracy, 5) / (1 + misses * 0.25);
+		// depends on player's note streak (2000 combo will double base fp)
+		fp *= 1 + maxCombo / 2000;
+		// depends on player's note accuracy (weighted by power of 3; 95% = x0.85, 90% = x0.72, 80% = x0.512)
+		fp *= Math.pow(accuracy, 3) / (1 + misses * 0.25);
 		return Math.ffloor(fp);
     }
 
diff --git a/source/online/gui/sidebar/SideUI.hx b/source/online/gui/sidebar/SideUI.hx
index 1f7491f8..5b2e8454 100644
--- a/source/online/gui/sidebar/SideUI.hx
+++ b/source/online/gui/sidebar/SideUI.hx
@@ -36,7 +36,7 @@ class SideUI extends Sprite {
 		x = -width;
 
 		stage.addEventListener(KeyboardEvent.KEY_DOWN, (e:KeyboardEvent) -> {
-			if (e.keyCode == 192 && stage.focus == null) {
+			if (checkKey(e.keyCode, ClientPrefs.keyBinds.get('sidebar')) && stage.focus == null) {
 				if (FunkinNetwork.loggedIn) {
 					active = !active;
 					return;
@@ -104,6 +104,14 @@ class SideUI extends Sprite {
 		}
 		return active = show;
 	}
+	
+	public static function checkKey(key:Int, keys:Array<Int>):Bool {
+		for (k in keys) {
+			if (key == k)
+				return true;
+		}
+		return false;
+	}
 }
 
 @:bitmap("assets/images/ui/cursor.png")
diff --git a/source/online/replay/ReplayPlayer.hx b/source/online/replay/ReplayPlayer.hx
index 3cca30c6..89bd196e 100644
--- a/source/online/replay/ReplayPlayer.hx
+++ b/source/online/replay/ReplayPlayer.hx
@@ -51,8 +51,8 @@ class ReplayPlayer extends FlxBasic {
         }
         else if (state.controls.UI_RIGHT) {
 			state.playbackRate += elapsed * 0.25 * shiftMult;
-            if (state.playbackRate > 4) {
-				state.playbackRate = 4;
+            if (state.playbackRate > 6) {
+				state.playbackRate = 6;
             }
 			state.botplayTxt.text = data.player + "'s\nREPLAY\n" + '(${CoolUtil.floorDecimal(state.playbackRate, 2)}x)';
         }
diff --git a/source/online/replay/ReplayRecorder.hx b/source/online/replay/ReplayRecorder.hx
index b5087c79..8440c940 100644
--- a/source/online/replay/ReplayRecorder.hx
+++ b/source/online/replay/ReplayRecorder.hx
@@ -40,7 +40,7 @@ class ReplayRecorder extends FlxBasic {
 		ghost_tapping: true,
 		rating_offset: null,
 		safe_frames: null,
-		version: 2,
+		version: 3,
 		mod_url: ''
     };
 
diff --git a/source/options/ControlsSubState.hx b/source/options/ControlsSubState.hx
index 02cf3742..c58db57e 100644
--- a/source/options/ControlsSubState.hx
+++ b/source/options/ControlsSubState.hx
@@ -34,6 +34,7 @@ class ControlsSubState extends MusicBeatSubstate
 		[true, 'Accept', 'accept', 'Accept'],
 		[true, 'Back', 'back', 'Back'],
 		[true, 'Pause', 'pause', 'Pause'],
+		[true, 'Sidebar', 'sidebar', 'Sidebar'],
 		[false],
 		[false, 'VOLUME'],
 		[false, 'Mute', 'volume_mute', 'Volume Mute'],
diff --git a/source/states/PlayState.hx b/source/states/PlayState.hx
index 734173bd..377a696b 100644
--- a/source/states/PlayState.hx
+++ b/source/states/PlayState.hx
@@ -1942,7 +1942,9 @@ class PlayState extends MusicBeatState
 		var densLastStrumTime:Float = -1;
 		var densNotes:Float = 0;
 		var densNotesCount:Float = 0;
-		var _densNotesBonus:Float = 0;
+		var densNotesBonus:Float = 0;
+		var csc = 125; //Conductor.stepCrochet * 1.5;
+		trace('step crochet: ' + csc);
 		for (section in noteData)
 		{
 			for (songNotes in section.sectionNotes)
@@ -1986,22 +1988,21 @@ class PlayState extends MusicBeatState
 					var noteDiff = (daStrumTime - densLastStrumTime) / playbackRate;
 
 					if (densLastStrumTime != -1 && noteDiff > 10) {
-						var keepCombo = tenseCombo < 2 || Math.pow(150 - noteDiff, 2) <= tension / tenseCombo + 10;
+						var keepCombo = tenseCombo < 2 || csc - noteDiff + 10 >= tension / tenseCombo;
 
-						if (noteDiff <= 150) {
+						if (noteDiff <= csc) {
 							if (keepCombo) {
 								tenseCombo++;
-								tension += Math.pow(150 - noteDiff, 2);
+								tension += csc - noteDiff;
 							}
 							densNotesCount++;
-							densNotes += (150 - noteDiff) / 50;
+							densNotes += (csc - noteDiff) / csc;
 						}
 						
-						if (noteDiff > 150 || !keepCombo) {
+						if (noteDiff > csc || !keepCombo) {
 							if (tenseCombo > 0 && tension > 0) {
-								var temp = tension / tenseCombo * tenseCombo / 500;
-								_densNotesBonus += temp;
-								densNotes += temp;
+								var temp = tension / (csc * 5) * Math.pow(tenseCombo, 1.15);
+								densNotesBonus += temp;
 								if (ClientPrefs.isDebug())
 									trace(temp, tenseCombo);
 							}
@@ -2097,9 +2098,9 @@ class PlayState extends MusicBeatState
 				}
 			}
 		}
-		denseNotes = densNotesCount == 0 ? 0 : densNotes / 1000;
+		denseNotes = densNotesCount == 0 ? 0 : (densNotes + densNotesBonus) / 1000;
 		trace(' + predensity: ' + densNotes);
-		trace(' + bonus: ' + _densNotesBonus);
+		trace(' + bonus: ' + densNotesBonus);
 		trace("note density score: " + denseNotes);
 		for (event in songData.events) //Event Notes
 			for (i in 0...event[1].length)
@@ -2388,16 +2389,6 @@ class PlayState extends MusicBeatState
 			return;
 		}
 
-		if (!GameClient.isConnected() && !finishingSong && elapsed >= 0.1 && Conductor.songPosition > lastLagPos) {
-			setSongTime(Conductor.songPosition - 3000);
-			lastLagPos = Conductor.songPosition;
-			Alert.alert("Mod Lag Detected (-3s)");
-		}
-
-		if (!GameClient.isConnected() && FlxG.keys.justPressed.F6) {
-			swingMode = !swingMode;
-		}
-
 		if (FlxG.keys.justPressed.F7) {
 			ClientPrefs.data.showFP = !ClientPrefs.data.showFP;
 			ClientPrefs.saveSettings();
@@ -2409,15 +2400,49 @@ class PlayState extends MusicBeatState
 			ClientPrefs.saveSettings();
 			Alert.alert("Replay Submiting: " + (ClientPrefs.data.disableSubmiting ? "OFF" : "ON"));
 		}
+		
+		if (!GameClient.isConnected()) {
+			if (!finishingSong && elapsed >= 0.1 && Conductor.songPosition > lastLagPos) {
+				setSongTime(Conductor.songPosition - 3000);
+				lastLagPos = Conductor.songPosition;
+				Alert.alert("Mod Lag Detected (-3s)");
+			}
+
+			if (FlxG.keys.justPressed.F6) {
+				swingMode = !swingMode;
+			}
 
-		if (!GameClient.isConnected() && FlxG.keys.justPressed.F8 && replayPlayer == null) {
-			opponentMode = !opponentMode;
-			remove(replayRecorder);
-			replayRecorder.destroy();
-			songScore = 0;
-			boyfriend.isPlayer = !boyfriend.isPlayer;
-			dad.isPlayer = !dad.isPlayer;
-			addHealth(2);
+			if (FlxG.keys.justPressed.F8 && replayPlayer == null) {
+				opponentMode = !opponentMode;
+				remove(replayRecorder);
+				replayRecorder.destroy();
+				songScore = 0;
+				boyfriend.isPlayer = !boyfriend.isPlayer;
+				dad.isPlayer = !dad.isPlayer;
+				addHealth(2);
+			}
+
+			if (cpuControlled) {
+				var shiftMult = FlxG.keys.pressed.SHIFT ? 3 : 1;
+				if (controls.UI_LEFT) {
+					if (playbackRate - elapsed * 0.25 * shiftMult > 0)
+						playbackRate -= elapsed * 0.25 * shiftMult;
+					if (playbackRate < 0.01) {
+						playbackRate = 0.01;
+					}
+					botplayTxt.text = "BOTPLAY\n" + '(${CoolUtil.floorDecimal(playbackRate, 2)}x)';
+				}
+				else if (controls.UI_RIGHT) {
+					playbackRate += elapsed * 0.25 * shiftMult;
+					if (playbackRate > 8) {
+						playbackRate = 8;
+					}
+					botplayTxt.text = "BOTPLAY\n" + '(${CoolUtil.floorDecimal(playbackRate, 2)}x)';
+				}
+				else if (controls.RESET) {
+					playbackRate = 1;
+				}
+			}
 		}
 
 		if (FlxG.keys.justPressed.F11 && GameClient.isConnected()) {
@@ -2553,7 +2578,7 @@ class PlayState extends MusicBeatState
 		FlxG.watch.addQuick("stepShit", curStep);
 
 		// RESET = Quick Game Over Screen
-		if (!GameClient.isConnected() && !ClientPrefs.data.noReset && controls.RESET && canReset && !inCutscene && startedCountdown && !endingSong && canInput() && replayData == null)
+		if (!GameClient.isConnected() && !ClientPrefs.data.noReset && controls.RESET && canReset && !inCutscene && startedCountdown && !endingSong && canInput() && replayData == null && !cpuControlled)
 		{
 			health = 0;
 			trace("RESET = True");