Freecam BETA
Key RequiredUniversal

Freecam BETA

Universal ScriptItzPnsdgItzPnsdg 2K views 9.8K executesUpdated Apr 23, 2026
5.0/ 5(3 votes)
30100% Positive
Lua Script•2495 lines (101131 chars)
local pi = math.pi
local abs = math.abs
local clamp = math.clamp
local exp = math.exp
local rad = math.rad
local sign = math.sign
local sqrt = math.sqrt
local tan = math.tan
local ContextActionService = game:GetService("ContextActionService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local StarterGui = game:GetService("StarterGui")
local UserInputService = game:GetService("UserInputService")
local Workspace = game:GetService("Workspace")
local Settings = UserSettings()
local GameSettings = Settings.GameSettings
local Lighting = game:GetService("Lighting")
local TweenService = game:GetService("TweenService")
local GuiService = game:GetService("GuiService")
local CoreGui = game:GetService("CoreGui")
local LocalPlayer = Players.LocalPlayer
if not LocalPlayer then
	Players:GetPropertyChangedSignal("LocalPlayer"):Wait()
	LocalPlayer = Players.LocalPlayer
end
local Camera = Workspace.CurrentCamera
Workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(function()
	local newCamera = Workspace.CurrentCamera
	if newCamera then
		Camera = newCamera
	end
end)
local freecamGui = Instance.new("ScreenGui")
freecamGui.Name = "Freecam"
freecamGui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
freecamGui.ResetOnSpawn = false
freecamGui.Parent = CoreGui
freecamGui.Enabled = true
local touchGui = nil
local floatingButton = nil
local uiMainFrame = nil
local FFlagUserExitFreecamBreaksWithShiftlock = false
local FFlagUserShowGuiHideToggles = false
local FFlagUserFixFreecamDeltaTimeCalculation = false
local FFlagUserFixFreecamGuiChangeVisibility = false
local FFlagUserFreecamControlSpeed = true
local FFlagUserFreecamTiltControl = true
local FFlagUserFreecamSmoothnessControl = true
local FFlagUserFreecamGuiDestabilization = false
local FFlagUserFreecamDepthOfFieldEffect = true
local FFlagUserFreecamPlayerLock = true
local FFlagUserFreecamPlayerLockResetHeight = false
local FFlagUserFreecamCustomGui = true
local FREECAM_ENABLED_ATTRIBUTE_NAME = "FreecamEnabled"
local TOGGLE_INPUT_PRIORITY = Enum.ContextActionPriority.Low.Value
local INPUT_PRIORITY = Enum.ContextActionPriority.High.Value
local NAV_GAIN = Vector3.new(1, 1, 1)*64
local PAN_GAIN = Vector2.new(0.75, 1)*8
local FOV_GAIN = 300
local ROLL_GAIN = -pi/2
local PITCH_LIMIT = rad(90)
local VEL_STIFFNESS = 1.5
local PAN_STIFFNESS = 1.0
local FOV_STIFFNESS = 4.0
local ROLL_STIFFNESS = 1.0
local VEL_ADJ_STIFFNESS = 0.75
local PAN_ADJ_STIFFNESS = 0.75
local FOV_ADJ_STIFFNESS = 0.75
local ROLL_ADJ_STIFFNESS = 0.75
local VEL_MIN_STIFFNESS = 0.01
local PAN_MIN_STIFFNESS = 0.01
local FOV_MIN_STIFFNESS = 0.01
local ROLL_MIN_STIFFNESS = 0.01
local VEL_MAX_STIFFNESS = 10.0
local PAN_MAX_STIFFNESS = 10.0
local FOV_MAX_STIFFNESS = 10.0
local ROLL_MAX_STIFFNESS = 10.0
local lastPressTime = {}
local lastResetTime = 0
local DOUBLE_TAP_TIME_THRESHOLD = 0.25
local DOUBLE_TAP_DEBOUNCE_TIME = 0.1
local postEffects = {}
local playerGuiConnection = nil
local cameraConnection = nil
local lightingConnection = nil
local playerAddedConnection = nil
local playerRemovingConnection = nil
local PLAYER_LOCK_DEFAULT_ZOOM = 20
local PLAYER_LOCK_MIN_ZOOM = 5
local PLAYER_LOCK_MAX_ZOOM = 50
local PLAYER_LOCK_DEFAULT_HEIGHT = 0
local PLAYER_LOCK_HEIGHT_RANGE = 10
local playerLockEnabled = false
local playerLockZoom = 20
local playerLockHeight = 0
local playerList = {}
local currentTargetIndex = 1
local rootPart = nil
local screenGuisEnabled = false
local leaderboardEnabled = false
local mobileMoveVector = Vector3.new(0, 0, 0)
local mobileLookVector = Vector2.new(0, 0)
local mobileFovDelta = 0
local mobileNavSpeed = 1
local flyUpActive = false
local flyDownActive = false
local tiltLeftActive = false
local tiltRightActive = false
local customTiltLeftKeys = {"Z"}
local customTiltRightKeys = {"C"}
local mouseSensitivity = 1
local touchLookSensitivity = 1
local fovSensitivity = 1
local Spring = {}
Spring.__index = Spring
function Spring.new(freq, pos)
	local self = setmetatable({}, Spring)
	self.f = freq
	self.p = pos
	self.v = pos*0
	return self
end
function Spring:Update(dt, goal)
	local f = self.f*2*pi
	local p0 = self.p
	local v0 = self.v
	local offset = goal - p0
	local decay = exp(-f*dt)
	local p1 = goal + (v0*dt - offset*(f*dt + 1))*decay
	local v1 = (f*dt*(offset*f - v0) + v0)*decay
	self.p = p1
	self.v = v1
	return p1
end
function Spring:SetFreq(freq)
	self.f = freq
end
function Spring:Reset(pos)
	self.p = pos
	self.v = pos*0
end
local cameraPos = Vector3.new()
local cameraRot = Vector3.new()
local cameraFov = 0
local velSpring = Spring.new(VEL_STIFFNESS, Vector3.new())
local panSpring = Spring.new(PAN_STIFFNESS, Vector2.new())
local fovSpring = Spring.new(FOV_STIFFNESS, 0)
local rollSpring = Spring.new(ROLL_STIFFNESS, 0)
local tiltLeftActiveKB = false
local tiltRightActiveKB = false
function CreateFlyButtons(parent)
	local PADDING = 9
	local buttonFrame = Instance.new("Frame")
	buttonFrame.Name = "FreeCamFlybtn"
	buttonFrame.BackgroundTransparency = 1
	buttonFrame.Parent = parent
	buttonFrame.ZIndex = 15
	local flyDownBtn = Instance.new("ImageButton")
	flyDownBtn.Name = "FlyDownBtn"
	flyDownBtn.BackgroundTransparency = 1
	flyDownBtn.Image = "rbxasset://textures/ui/Input/TouchControlsSheetV2.png"
	flyDownBtn.ImageRectOffset = Vector2.new(1, 146)
	flyDownBtn.ImageRectSize = Vector2.new(144, 144)
	flyDownBtn.Rotation = 180
	flyDownBtn.Parent = buttonFrame
	flyDownBtn.ZIndex = 15
	local flyUpBtn = Instance.new("ImageButton")
	flyUpBtn.Name = "FlyUpBtn"
	flyUpBtn.BackgroundTransparency = 1
	flyUpBtn.Image = "rbxasset://textures/ui/Input/TouchControlsSheetV2.png"
	flyUpBtn.ImageRectOffset = Vector2.new(1, 146)
	flyUpBtn.ImageRectSize = Vector2.new(144, 144)
	flyUpBtn.Rotation = 0
	flyUpBtn.Parent = buttonFrame
	flyUpBtn.ZIndex = 15
	flyDownBtn.InputBegan:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.Touch and input.UserInputState == Enum.UserInputState.Begin then
			flyDownBtn.ImageRectOffset = Vector2.new(146, 146)
			flyDownActive = true
		end
	end)
	flyDownBtn.InputEnded:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.Touch then
			flyDownBtn.ImageRectOffset = Vector2.new(1, 146)
			flyDownActive = false
		end
	end)
	flyUpBtn.InputBegan:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.Touch and input.UserInputState == Enum.UserInputState.Begin then
			flyUpBtn.ImageRectOffset = Vector2.new(146, 146)
			flyUpActive = true
		end
	end)
	flyUpBtn.InputEnded:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.Touch then
			flyUpBtn.ImageRectOffset = Vector2.new(1, 146)
			flyUpActive = false
		end
	end)
	function updateButtonSize()
		local screenSize = parent.AbsoluteSize
		local isSmallScreen = math.min(screenSize.X, screenSize.Y) <= 500
		local buttonSize = isSmallScreen and 70 or 90
		local totalWidth = (buttonSize * 2) + (PADDING * 3)
		local totalHeight = buttonSize + (PADDING * 2)
		buttonFrame.Size = UDim2.new(0, totalWidth, 0, totalHeight)
		buttonFrame.Position = UDim2.new(1, -(totalWidth + PADDING), 1, -(totalHeight + PADDING))
		flyDownBtn.Size = UDim2.new(0, buttonSize, 0, buttonSize)
		flyDownBtn.Position = UDim2.new(0, PADDING, 0, PADDING)
		flyUpBtn.Size = UDim2.new(0, buttonSize, 0, buttonSize)
		flyUpBtn.Position = UDim2.new(0, PADDING + buttonSize + PADDING, 0, PADDING)
	end
	parent:GetPropertyChangedSignal("AbsoluteSize"):Connect(updateButtonSize)
	updateButtonSize()
	GuiService.MenuOpened:Connect(function()
		flyDownBtn.ImageRectOffset = Vector2.new(1, 146)
		flyUpBtn.ImageRectOffset = Vector2.new(1, 146)
		flyDownActive = false
		flyUpActive = false
	end)
	return buttonFrame, flyDownBtn, flyUpBtn
end
function CreateTiltButtons(parent)
	local PADDING = 9
	local buttonFrame = Instance.new("Frame")
	buttonFrame.Name = "TiltButtons"
	buttonFrame.BackgroundTransparency = 1
	buttonFrame.Parent = parent
	buttonFrame.ZIndex = 15
	local tiltLeftBtn = Instance.new("ImageButton")
	tiltLeftBtn.Name = "TiltLeftBtn"
	tiltLeftBtn.BackgroundTransparency = 1
	tiltLeftBtn.Image = "rbxassetid://138041015580154"
	tiltLeftBtn.Parent = buttonFrame
	tiltLeftBtn.ZIndex = 15
	local tiltRightBtn = Instance.new("ImageButton")
	tiltRightBtn.Name = "TiltRightBtn"
	tiltRightBtn.BackgroundTransparency = 1
	tiltRightBtn.Image = "rbxassetid://122289559454004"
	tiltRightBtn.Parent = buttonFrame
	tiltRightBtn.ZIndex = 15
	tiltLeftBtn.InputBegan:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.Touch and input.UserInputState == Enum.UserInputState.Begin then
			tiltLeftBtn.ImageTransparency = 0.3
			tiltLeftActive = true
		end
	end)
	tiltLeftBtn.InputEnded:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.Touch then
			tiltLeftBtn.ImageTransparency = 0
			tiltLeftActive = false
		end
	end)
	tiltRightBtn.InputBegan:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.Touch and input.UserInputState == Enum.UserInputState.Begin then
			tiltRightBtn.ImageTransparency = 0.3
			tiltRightActive = true
		end
	end)
	tiltRightBtn.InputEnded:Connect(function(input)
		if input.UserInputType == Enum.UserInputType.Touch then
			tiltRightBtn.ImageTransparency = 0
			tiltRightActive = false
		end
	end)
	function updateButtonSize()
		local screenSize = parent.AbsoluteSize
		local isSmallScreen = math.min(screenSize.X, screenSize.Y) <= 500
		local buttonSize = isSmallScreen and 60 or 75
		local totalWidth = (buttonSize * 2) + (PADDING * 3)
		buttonFrame.Size = UDim2.new(0, totalWidth, 0, buttonSize + (PADDING * 2))
		buttonFrame.Position = UDim2.new(0, PADDING, 1, -(buttonSize + (PADDING * 2)))
		tiltLeftBtn.Size = UDim2.new(0, buttonSize, 0, buttonSize)
		tiltLeftBtn.Position = UDim2.new(0, PADDING, 0, PADDING)
		tiltRightBtn.Size = UDim2.new(0, buttonSize, 0, buttonSize)
		tiltRightBtn.Position = UDim2.new(0, PADDING + buttonSize + PADDING, 0, PADDING)
	end
	parent:GetPropertyChangedSignal("AbsoluteSize"):Connect(updateButtonSize)
	updateButtonSize()
	GuiService.MenuOpened:Connect(function()
		tiltLeftActive = false
		tiltRightActive = false
		tiltLeftBtn.ImageTransparency = 0
		tiltRightBtn.ImageTransparency = 0
	end)
	return buttonFrame, tiltLeftBtn, tiltRightBtn
end
function CreatePlayerLockBar(parent)
	local PADDING = 10
	local barFrame = Instance.new("Frame")
	barFrame.Name = "PlayerLockBar"
	barFrame.BackgroundColor3 = Color3.fromRGB(30, 30, 40)
	barFrame.BackgroundTransparency = 0.2
	barFrame.BorderSizePixel = 0
	barFrame.Parent = parent
	barFrame.ZIndex = 15
	local barCorner = Instance.new("UICorner")
	barCorner.CornerRadius = UDim.new(0, 25)
	barCorner.Parent = barFrame
	local prevBtn = Instance.new("TextButton")
	prevBtn.Name = "PrevBtn"
	prevBtn.Text = "<"
	prevBtn.TextColor3 = Color3.fromRGB(255, 255, 255)
	prevBtn.TextSize = 24
	prevBtn.Font = Enum.Font.GothamBold
	prevBtn.BackgroundColor3 = Color3.fromRGB(50, 50, 70)
	prevBtn.BackgroundTransparency = 0.5
	prevBtn.BorderSizePixel = 0
	prevBtn.Parent = barFrame
	prevBtn.ZIndex = 15
	local prevCorner = Instance.new("UICorner")
	prevCorner.CornerRadius = UDim.new(0, 20)
	prevCorner.Parent = prevBtn
	local playerLabel = Instance.new("TextLabel")
	playerLabel.Name = "PlayerLabel"
	playerLabel.Text = "Player Lock: OFF"
	playerLabel.TextColor3 = Color3.fromRGB(255, 255, 255)
	playerLabel.TextSize = 14
	playerLabel.Font = Enum.Font.GothamBold
	playerLabel.BackgroundColor3 = Color3.fromRGB(40, 40, 60)
	playerLabel.BackgroundTransparency = 0.5
	playerLabel.BorderSizePixel = 0
	playerLabel.Parent = barFrame
	playerLabel.ZIndex = 15
	local playerCorner = Instance.new("UICorner")
	playerCorner.CornerRadius = UDim.new(0, 20)
	playerCorner.Parent = playerLabel
	local nextBtn = Instance.new("TextButton")
	nextBtn.Name = "NextBtn"
	nextBtn.Text = ">"
	nextBtn.TextColor3 = Color3.fromRGB(255, 255, 255)
	nextBtn.TextSize = 24
	nextBtn.Font = Enum.Font.GothamBold
	nextBtn.BackgroundColor3 = Color3.fromRGB(50, 50, 70)
	nextBtn.BackgroundTransparency = 0.5
	nextBtn.BorderSizePixel = 0
	nextBtn.Parent = barFrame
	nextBtn.ZIndex = 15
	local nextCorner = Instance.new("UICorner")
	nextCorner.CornerRadius = UDim.new(0, 20)
	nextCorner.Parent = nextBtn
	local toggleBtn = Instance.new("TextButton")
	toggleBtn.Name = "ToggleBtn"
	toggleBtn.Text = "Lock"
	toggleBtn.TextColor3 = Color3.fromRGB(255, 255, 255)
	toggleBtn.TextSize = 12
	toggleBtn.Font = Enum.Font.GothamBold
	toggleBtn.BackgroundColor3 = Color3.fromRGB(100, 200, 255)
	toggleBtn.BackgroundTransparency = 0.3
	toggleBtn.BorderSizePixel = 0
	toggleBtn.Parent = barFrame
	toggleBtn.ZIndex = 15
	local toggleCorner = Instance.new("UICorner")
	toggleCorner.CornerRadius = UDim.new(0, 15)
	toggleCorner.Parent = toggleBtn
	function updateBarSize()
		local screenSize = parent.AbsoluteSize
		local isSmallScreen = math.min(screenSize.X, screenSize.Y) <= 500
		local btnSize = isSmallScreen and 45 or 55
		local labelWidth = 160
		local totalWidth = btnSize + labelWidth + btnSize + 55
		barFrame.Size = UDim2.new(0, totalWidth, 0, btnSize)
		barFrame.Position = UDim2.new(0.5, -totalWidth/2, 1, -(btnSize + PADDING * 2))
		prevBtn.Size = UDim2.new(0, btnSize, 0, btnSize)
		prevBtn.Position = UDim2.new(0, PADDING, 0, 0)
		playerLabel.Size = UDim2.new(0, labelWidth, 0, btnSize)
		playerLabel.Position = UDim2.new(0, btnSize + PADDING * 2, 0, 0)
		nextBtn.Size = UDim2.new(0, btnSize, 0, btnSize)
		nextBtn.Position = UDim2.new(1, -(btnSize + PADDING), 0, 0)
		toggleBtn.Size = UDim2.new(0, 45, 0, btnSize - 10)
		toggleBtn.Position = UDim2.new(1, -(btnSize + PADDING + 50), 0, 5)
	end
	parent:GetPropertyChangedSignal("AbsoluteSize"):Connect(updateBarSize)
	updateBarSize()
	local function updatePlayerLabel()
		if playerLockEnabled and #playerList > 0 then
			playerLabel.Text = "Lock: " .. playerList[currentTargetIndex].Name
			toggleBtn.Text = "Unlock"
			toggleBtn.BackgroundColor3 = Color3.fromRGB(255, 100, 100)
		else
			playerLabel.Text = "Player Lock: OFF"
			toggleBtn.Text = "Lock"
			toggleBtn.BackgroundColor3 = Color3.fromRGB(100, 200, 255)
		end
	end
	prevBtn.MouseButton1Click:Connect(function()
		if playerLockEnabled and #playerList > 0 then
			for i = 1, #playerList do
				currentTargetIndex = currentTargetIndex - 1
				if currentTargetIndex < 1 then currentTargetIndex = #playerList end
				local targetPlayer = playerList[currentTargetIndex]
				local targetCharacter = targetPlayer and targetPlayer.Character
				if targetCharacter then
					local humanoid = targetCharacter:FindFirstChildOfClass("Humanoid")
					if humanoid and humanoid.RootPart then
						rootPart = humanoid.RootPart
						break
					end
				end
			end
			updatePlayerLabel()
		end
	end)
	nextBtn.MouseButton1Click:Connect(function()
		if playerLockEnabled and #playerList > 0 then
			for i = 1, #playerList do
				currentTargetIndex = currentTargetIndex + 1
				if currentTargetIndex > #playerList then currentTargetIndex = 1 end
				local targetPlayer = playerList[currentTargetIndex]
				local targetCharacter = targetPlayer and targetPlayer.Character
				if targetCharacter then
					local humanoid = targetCharacter:FindFirstChildOfClass("Humanoid")
					if humanoid and humanoid.RootPart then
						rootPart = humanoid.RootPart
						break
					end
				end
			end
			updatePlayerLabel()
		end
	end)
	toggleBtn.MouseButton1Click:Connect(function()
		playerLockEnabled = not playerLockEnabled
		if playerLockEnabled then
			playerLockZoom = PLAYER_LOCK_DEFAULT_ZOOM
			playerLockHeight = PLAYER_LOCK_DEFAULT_HEIGHT
			if #playerList > 0 then
				local targetPlayer = playerList[currentTargetIndex]
				local targetCharacter = targetPlayer and targetPlayer.Character
				if targetCharacter then
					local humanoid = targetCharacter:FindFirstChildOfClass("Humanoid")
					if humanoid and humanoid.RootPart then
						rootPart = humanoid.RootPart
					end
				end
			end
		end
		updatePlayerLabel()
	end)
	return barFrame
end
function CreateTouchControls()
	local gui = Instance.new("ScreenGui")
	gui.Name = "FreeCamTouchControll"
	gui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
	gui.ResetOnSpawn = false
	gui.Parent = LocalPlayer:WaitForChild("PlayerGui")
	local thumbstickFrame = Instance.new("Frame")
	thumbstickFrame.Name = "DynamicThumbstickFrame"
	thumbstickFrame.BorderSizePixel = 0
	thumbstickFrame.Visible = true
	thumbstickFrame.BackgroundTransparency = 1
	thumbstickFrame.BackgroundColor3 = Color3.new(0, 0, 0)
	thumbstickFrame.Active = true
	thumbstickFrame.Size = UDim2.new(0.4, 0, 0.6666666666666666, 0)
	thumbstickFrame.Position = UDim2.new(0, 0, 0.3333333333333333, 0)
	thumbstickFrame.Parent = gui
	thumbstickFrame.ZIndex = 10
	local startImage = Instance.new("ImageLabel")
	startImage.Name = "ThumbstickStart"
	startImage.Visible = true
	startImage.BackgroundTransparency = 1
	startImage.Image = "rbxasset://textures/ui/Input/TouchControlsSheetV2.png"
	startImage.ImageRectOffset = Vector2.new(1, 1)
	startImage.ImageRectSize = Vector2.new(144, 144)
	startImage.ImageColor3 = Color3.new(0, 0, 0)
	startImage.ImageTransparency = 1
	startImage.AnchorPoint = Vector2.new(0.5, 0.5)
	startImage.ZIndex = 10
	startImage.Parent = thumbstickFrame
	local endImage = Instance.new("ImageLabel")
	endImage.Name = "ThumbstickEnd"
	endImage.Visible = true
	endImage.BackgroundTransparency = 1
	endImage.Image = "rbxasset://textures/ui/Input/TouchControlsSheetV2.png"
	endImage.ImageRectOffset = Vector2.new(1, 1)
	endImage.ImageRectSize = Vector2.new(144, 144)
	endImage.ImageColor3 = Color3.new(0, 0, 0)
	endImage.ImageTransparency = 1
	endImage.AnchorPoint = Vector2.new(0.5, 0.5)
	endImage.ZIndex = 10
	endImage.Parent = thumbstickFrame
	local middleImages = {}
	local originalTransparencies = {0.11, 0.30, 0.40, 0.50, 0.60, 0.70, 0.75}
	for i = 1, 7 do
		local middle = Instance.new("ImageLabel")
		middle.Name = "ThumbstickMiddle"
		middle.Visible = false
		middle.BackgroundTransparency = 1
		middle.Image = "rbxasset://textures/ui/Input/TouchControlsSheetV2.png"
		middle.ImageRectOffset = Vector2.new(1, 1)
		middle.ImageRectSize = Vector2.new(144, 144)
		middle.ImageTransparency = originalTransparencies[i]
		middle.AnchorPoint = Vector2.new(0.5, 0.5)
		middle.ZIndex = 9
		middle.Parent = thumbstickFrame
		middleImages[i] = middle
	end
	local thumbstickSize = 45
	local thumbstickRingSize = 20
	local middleSize = 10
	local middleSpacing = 14
	local radiusDeadZone = 2
	local radiusMaxSpeed = 20
	local moveTouchObject = nil
	local moveTouchStartPosition = nil
	local moveTouchLockedIn = false
	local moveTouchFirstChanged = false
	local isFirstTouch = true
	function updatePositions()
		local baseX = thumbstickRingSize * 3.3
		local baseY = thumbstickRingSize * 2.8
		startImage.Position = UDim2.new(0, baseX, 1, -baseY)
		startImage.Size = UDim2.new(0, thumbstickRingSize * 3.7, 0, thumbstickRingSize * 3.7)
		endImage.Position = startImage.Position
		endImage.Size = UDim2.new(0, thumbstickSize * 0.8, 0, thumbstickSize * 0.8)
	end
	thumbstickFrame:GetPropertyChangedSignal("AbsoluteSize"):Connect(updatePositions)
	task.wait(0.1)
	updatePositions()
	function layoutMiddleImages(startPos, endPos)
		local centerOffset = thumbstickSize / 2 + middleSize
		local delta = endPos - startPos
		local dist = delta.Magnitude - thumbstickRingSize / 2 - middleSize
		local direction = delta.Unit
		local totalSpacing = middleSpacing * 7
		local stepSize = middleSpacing
		if totalSpacing < dist then
			stepSize = dist / 7
		end
		for i = 1, 7 do
			local img = middleImages[i]
			local startDist = centerOffset + stepSize * (i - 2)
			local endDist = centerOffset + stepSize * (i - 1)
			if startDist < dist then
				local pos = endPos - direction * endDist
				local alpha = 1 - (endDist - dist) / stepSize
				local clampedAlpha = math.clamp(alpha, 0, 1)
				img.Visible = true
				img.Position = UDim2.new(0, pos.X, 0, pos.Y)
				img.Size = UDim2.new(0, middleSize * clampedAlpha, 0, middleSize * clampedAlpha)
			else
				img.Visible = false
			end
		end
	end
	function moveStick(touchPos)
		local startPos = Vector2.new(moveTouchStartPosition.X, moveTouchStartPosition.Y) - thumbstickFrame.AbsolutePosition
		local endPos = Vector2.new(touchPos.X, touchPos.Y) - thumbstickFrame.AbsolutePosition
		endImage.Position = UDim2.new(0, endPos.X, 0, endPos.Y)
		layoutMiddleImages(startPos, endPos)
	end
	function doMove(offset)
		local mag = offset.Magnitude
		if mag < radiusDeadZone then
			mobileMoveVector = Vector3.new(0, 0, 0)
		else
			local unit = offset.Unit
			local speedFactor = (radiusMaxSpeed - mag) / radiusMaxSpeed
			local scaled = unit * (1 - math.max(0, speedFactor))
			mobileMoveVector = Vector3.new(scaled.X, 0, scaled.Y)
		end
	end
	function fadeThumbstick(fadeIn)
		local tweenInfo = TweenInfo.new(0.15, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut)
		if fadeIn then
			TweenService:Create(startImage, tweenInfo, {ImageTransparency = 0}):Play()
			TweenService:Create(endImage, tweenInfo, {ImageTransparency = 0.2}):Play()
			for i = 1, 7 do
				TweenService:Create(middleImages[i], tweenInfo, {ImageTransparency = originalTransparencies[i]}):Play()
			end
		else
			TweenService:Create(startImage, tweenInfo, {ImageTransparency = 1}):Play()
			TweenService:Create(endImage, tweenInfo, {ImageTransparency = 1}):Play()
			for i = 1, 7 do
				TweenService:Create(middleImages[i], tweenInfo, {ImageTransparency = 1}):Play()
			end
		end
	end
	function onInputEnded()
		moveTouchObject = nil
		mobileMoveVector = Vector3.new(0, 0, 0)
		fadeThumbstick(false)
	end
	function inputInFrame(touch)
		local absPos = thumbstickFrame.AbsolutePosition
		local absSize = thumbstickFrame.AbsoluteSize
		local touchPos = touch.Position
		return touchPos.X >= absPos.X and touchPos.Y >= absPos.Y and
			touchPos.X <= absPos.X + absSize.X and touchPos.Y <= absPos.Y + absSize.Y
	end
	function onTouchBegin(touch)
		if moveTouchObject then return end
		if not inputInFrame(touch) then return end
		if isFirstTouch then
			isFirstTouch = false
			local growTween = TweenInfo.new(0.5, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
			TweenService:Create(startImage, growTween, {Size = UDim2.new(0, 0, 0, 0)}):Play()
			TweenService:Create(endImage, growTween, {
				Size = UDim2.new(0, thumbstickSize, 0, thumbstickSize),
				ImageColor3 = Color3.new(0, 0, 0)
			}):Play()
		end
		moveTouchLockedIn = false
		moveTouchObject = touch
		moveTouchStartPosition = touch.Position
		moveTouchFirstChanged = true
	end
	function onTouchMove(touch)
		if touch ~= moveTouchObject then return end
		if moveTouchFirstChanged then
			moveTouchFirstChanged = false
			local startPosRel = Vector2.new(touch.Position.X - thumbstickFrame.AbsolutePosition.X, touch.Position.Y - thumbstickFrame.AbsolutePosition.Y)
			startImage.Visible = true
			startImage.Position = UDim2.new(0, startPosRel.X, 0, startPosRel.Y)
			endImage.Visible = true
			endImage.Position = startImage.Position
			fadeThumbstick(true)
			moveStick(touch.Position)
		end
		moveTouchLockedIn = true
		local offset = Vector2.new(touch.Position.X - moveTouchStartPosition.X, touch.Position.Y - moveTouchStartPosition.Y)
		if math.abs(offset.X) > 0 or math.abs(offset.Y) > 0 then
			doMove(offset)
			moveStick(touch.Position)
		end
	end
	function onTouchEnd(touch)
		if touch == moveTouchObject then
			onInputEnded()
		end
	end
	local beginCon = UserInputService.TouchStarted:Connect(onTouchBegin)
	local moveCon = UserInputService.TouchMoved:Connect(onTouchMove)
	local endCon = UserInputService.TouchEnded:Connect(onTouchEnd)
	GuiService.MenuOpened:Connect(function()
		if moveTouchObject then
			onInputEnded()
		end
	end)
	local rightDragActive = false
	local rightDragStart = nil
	local rightDragTouch = nil
	UserInputService.TouchStarted:Connect(function(touch)
		local screenSize = Camera.ViewportSize
		if touch.Position.X > screenSize.X / 2 then
			rightDragActive = true
			rightDragStart = touch.Position
			rightDragTouch = touch
		end
	end)
	UserInputService.TouchMoved:Connect(function(touch)
		if rightDragActive and rightDragTouch == touch and rightDragStart then
			local delta = touch.Position - rightDragStart
			mobileLookVector = Vector2.new(-delta.Y * 0.1 * touchLookSensitivity, -delta.X * 0.1 * touchLookSensitivity)
			rightDragStart = touch.Position
		end
	end)
	UserInputService.TouchEnded:Connect(function(touch)
		if rightDragTouch == touch then
			rightDragActive = false
			rightDragTouch = nil
			mobileLookVector = Vector2.new(0, 0)
		end
	end)
	local pinchTouch1 = nil
	local pinchTouch2 = nil
	local pinchActive = false
	local lastPinchDistance = 0
	UserInputService.TouchStarted:Connect(function(touch)
		local screenSize = Camera.ViewportSize
		if touch.Position.X > screenSize.X / 2 then
			if not pinchTouch1 then
				pinchTouch1 = touch
			elseif not pinchTouch2 and touch ~= pinchTouch1 then
				pinchTouch2 = touch
				pinchActive = true
				lastPinchDistance = (pinchTouch1.Position - pinchTouch2.Position).Magnitude
			end
		end
	end)
	UserInputService.TouchMoved:Connect(function(touch)
		if pinchActive and pinchTouch1 and pinchTouch2 then
			if touch == pinchTouch1 then
				pinchTouch1 = touch
			elseif touch == pinchTouch2 then
				pinchTouch2 = touch
			end
			local currentDistance = (pinchTouch1.Position - pinchTouch2.Position).Magnitude
			local delta = currentDistance - lastPinchDistance
			if math.abs(delta) > 0.5 then
				mobileFovDelta = -delta * 0.35 * fovSensitivity
				lastPinchDistance = currentDistance
			end
		end
	end)
	UserInputService.TouchEnded:Connect(function(touch)
		if touch == pinchTouch1 then
			pinchTouch1 = pinchTouch2
			pinchTouch2 = nil
		elseif touch == pinchTouch2 then
			pinchTouch2 = nil
		end
		if not pinchTouch1 or not pinchTouch2 then
			pinchActive = false
			lastPinchDistance = 0
		end
	end)
	local flyButtons = CreateFlyButtons(gui)
	local tiltButtons = CreateTiltButtons(gui)
	local playerLockBar = CreatePlayerLockBar(gui)
	return gui
end
local Input = {}
do
	local thumbstickCurve
	do
		local K_CURVATURE = 2.0
		local K_DEADZONE = 0.15
		function fCurve(x)
			return (exp(K_CURVATURE*x) - 1)/(exp(K_CURVATURE) - 1)
		end
		function fDeadzone(x)
			return fCurve((x - K_DEADZONE)/(1 - K_DEADZONE))
		end
		function thumbstickCurve(x)
			return sign(x)*clamp(fDeadzone(abs(x)), 0, 1)
		end
	end
	local gamepad = {
		ButtonX = 0, ButtonY = 0, DPadDown = 0, DPadUp = 0,
		DPadLeft = 0, DPadRight = 0, ButtonL2 = 0, ButtonR2 = 0,
		ButtonL1 = 0, ButtonR1 = 0, Thumbstick1 = Vector2.new(), Thumbstick2 = Vector2.new(),
	}
	local keyboard = {
		W = 0, A = 0, S = 0, D = 0, E = 0, Q = 0, U = 0, H = 0, J = 0, K = 0, I = 0, Y = 0,
		Up = 0, Down = 0, Left = 0, Right = 0, LeftShift = 0, RightShift = 0,
		Z = 0, C = 0, Comma = 0, Period = 0, LeftBracket = 0, RightBracket = 0,
		Semicolon = 0, Quote = 0, V = 0, B = 0, N = 0, M = 0, BackSlash = 0,
		Minus = 0, Equals = 0, Slash = 0, R = 0, T = 0, G = 0, X = 0, L = 0
	}
	local mouse = { Delta = Vector2.new(), MouseWheel = 0 }
	local DEFAULT_FPS = 60
	local NAV_GAMEPAD_SPEED = Vector3.new(1, 1, 1)
	local NAV_KEYBOARD_SPEED = Vector3.new(1, 1, 1)
	local PAN_MOUSE_SPEED = Vector2.new(1, 1)*(pi/64)
	local PAN_MOUSE_SPEED_DT = PAN_MOUSE_SPEED/DEFAULT_FPS
	local PAN_GAMEPAD_SPEED = Vector2.new(1, 1)*(pi/8)
	local FOV_WHEEL_SPEED = 1.0
	local FOV_WHEEL_SPEED_DT = FOV_WHEEL_SPEED/DEFAULT_FPS
	local FOV_GAMEPAD_SPEED = 0.25
	local ROLL_GAMEPAD_SPEED = 1.0
	local ROLL_KEYBOARD_SPEED = 1.0
	local NAV_ADJ_SPEED = 0.75
	local NAV_MIN_SPEED = 0.01
	local NAV_MAX_SPEED = 4.0
	local NAV_SHIFT_MUL = 0.25
	local FOV_ADJ_SPEED = 0.75
	local FOV_MIN_SPEED = 0.01
	local FOV_MAX_SPEED = 4.0
	local ROLL_ADJ_SPEED = 0.75
	local ROLL_MIN_SPEED = 0.01
	local ROLL_MAX_SPEED = 4.0
	local DoFConstants = {
		FarIntensity = { ADJ = 0.1, MIN = 0.0, MAX = 1.0 },
		NearIntensity = { ADJ = 0.1, MIN = 0.0, MAX = 1.0 },
		FocusDistance = { ADJ = 20.0, MIN = 0.0, MAX = 200.0 },
		FocusRadius = { ADJ = 5.0, MIN = 0.0, MAX = 50.0 },
	}
	local navSpeed = 1
	local rollSpeed = 1
	local fovSpeed = 1
	function Input.Vel(dt)
		if FFlagUserFreecamControlSpeed then
			navSpeed = clamp(navSpeed + dt*(keyboard.Up - keyboard.Down + gamepad.DPadUp - gamepad.DPadDown)*NAV_ADJ_SPEED, NAV_MIN_SPEED, NAV_MAX_SPEED)
		else
			navSpeed = clamp(navSpeed + dt*(keyboard.Up - keyboard.Down)*NAV_ADJ_SPEED, 0.01, 4)
		end
		local flyVertical = 0
		if flyUpActive then flyVertical = flyVertical + 1 end
		if flyDownActive then flyVertical = flyVertical - 1 end
		local mobileMove = Vector3.new(mobileMoveVector.X, flyVertical, mobileMoveVector.Z) * mobileNavSpeed
		local kGamepad = Vector3.new(
			thumbstickCurve(gamepad.Thumbstick1.X),
			thumbstickCurve(gamepad.ButtonR2) - thumbstickCurve(gamepad.ButtonL2),
			thumbstickCurve(-gamepad.Thumbstick1.Y)
		)*NAV_GAMEPAD_SPEED
		local kKeyboard = Vector3.new(
			keyboard.D - keyboard.A + keyboard.K - keyboard.H,
			keyboard.E - keyboard.Q + keyboard.I - keyboard.Y,
			keyboard.S - keyboard.W + keyboard.J - keyboard.U
		)*NAV_KEYBOARD_SPEED
		local shift = UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) or UserInputService:IsKeyDown(Enum.KeyCode.RightShift)
		return (kGamepad + kKeyboard + mobileMove)*(navSpeed*(shift and NAV_SHIFT_MUL or 1))
	end
	function Input.Pan(dt)
		local mobilePan = mobileLookVector
		local kGamepad = Vector2.new(
			thumbstickCurve(gamepad.Thumbstick2.Y),
			thumbstickCurve(-gamepad.Thumbstick2.X)
		)*PAN_GAMEPAD_SPEED
		local kMouse = mouse.Delta*PAN_MOUSE_SPEED * mouseSensitivity
		if FFlagUserFixFreecamDeltaTimeCalculation then
			if dt > 0 then
				kMouse = (mouse.Delta/dt)*PAN_MOUSE_SPEED_DT * mouseSensitivity
			end
		end
		mouse.Delta = Vector2.new()
		return kGamepad + kMouse + mobilePan
	end
	function Input.Fov(dt)
		if FFlagUserFreecamControlSpeed then
			fovSpeed = clamp(fovSpeed + dt*(keyboard.Right - keyboard.Left + gamepad.DPadRight - gamepad.DPadLeft)*FOV_ADJ_SPEED, FOV_MIN_SPEED, FOV_MAX_SPEED)
		end
		local kGamepad = (gamepad.ButtonX - gamepad.ButtonY)*FOV_GAMEPAD_SPEED
		local kMouse = mouse.MouseWheel*FOV_WHEEL_SPEED * fovSensitivity
		local kMobile = mobileFovDelta
		mobileFovDelta = 0
		if FFlagUserFixFreecamDeltaTimeCalculation then
			if dt > 0 then
				kMouse = (mouse.MouseWheel/dt)*FOV_WHEEL_SPEED_DT * fovSensitivity
			end
		end
		mouse.MouseWheel = 0
		if FFlagUserFreecamControlSpeed then
			return (kGamepad + kMouse + kMobile)*fovSpeed
		else
			return kGamepad + kMouse + kMobile
		end
	end
	function Input.Roll(dt)
		rollSpeed = clamp(rollSpeed + dt*(keyboard.Period - keyboard.Comma)*ROLL_ADJ_SPEED, ROLL_MIN_SPEED, ROLL_MAX_SPEED)
		local kGamepad = (gamepad.ButtonR1 - gamepad.ButtonL1)*ROLL_GAMEPAD_SPEED
		local kKeyboard = (keyboard.C - keyboard.Z)*ROLL_KEYBOARD_SPEED
		local kTouch = 0
		if tiltLeftActive then kTouch = kTouch + 1 end
		if tiltRightActive then kTouch = kTouch - 1 end
		local kCustomTilt = 0
		if tiltLeftActiveKB then kCustomTilt = kCustomTilt + 1 end
		if tiltRightActiveKB then kCustomTilt = kCustomTilt - 1 end
		return (kGamepad + kKeyboard + kTouch + kCustomTilt)*rollSpeed
	end
	function Input.SpringControl(dt)
		if FFlagUserFreecamDepthOfFieldEffect then
			local shiftIsDown = UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) or UserInputService:IsKeyDown(Enum.KeyCode.RightShift)
			local ctrlIsDown = UserInputService:IsKeyDown(Enum.KeyCode.LeftControl) or UserInputService:IsKeyDown(Enum.KeyCode.RightControl)
			if shiftIsDown or ctrlIsDown then return end
		end
		VEL_STIFFNESS = clamp(VEL_STIFFNESS + dt*(keyboard.RightBracket - keyboard.LeftBracket)*VEL_ADJ_STIFFNESS, VEL_MIN_STIFFNESS, VEL_MAX_STIFFNESS)
		velSpring:SetFreq(VEL_STIFFNESS)
		PAN_STIFFNESS = clamp(PAN_STIFFNESS + dt*(keyboard.Quote - keyboard.Semicolon)*PAN_ADJ_STIFFNESS, PAN_MIN_STIFFNESS, PAN_MAX_STIFFNESS)
		panSpring:SetFreq(PAN_STIFFNESS)
		FOV_STIFFNESS = clamp(FOV_STIFFNESS + dt*(keyboard.B - keyboard.V)*FOV_ADJ_STIFFNESS, FOV_MIN_STIFFNESS, FOV_MAX_STIFFNESS)
		fovSpring:SetFreq(FOV_STIFFNESS)
		ROLL_STIFFNESS = clamp(ROLL_STIFFNESS + dt*(keyboard.M - keyboard.N)*ROLL_ADJ_STIFFNESS, ROLL_MIN_STIFFNESS, ROLL_MAX_STIFFNESS)
		rollSpring:SetFreq(ROLL_STIFFNESS)
	end
	function Input.DoF(dt)
		local shiftIsDown = UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) or UserInputService:IsKeyDown(Enum.KeyCode.RightShift)
		local ctrlIsDown = UserInputService:IsKeyDown(Enum.KeyCode.LeftControl) or UserInputService:IsKeyDown(Enum.KeyCode.RightControl)
		if shiftIsDown then
			FreecamDepthOfField.FarIntensity = clamp(
				FreecamDepthOfField.FarIntensity + dt * (keyboard.RightBracket - keyboard.LeftBracket) * DoFConstants.FarIntensity.ADJ,
				DoFConstants.FarIntensity.MIN, DoFConstants.FarIntensity.MAX
			)
			FreecamDepthOfField.InFocusRadius = clamp(
				FreecamDepthOfField.InFocusRadius + dt * (keyboard.Equals - keyboard.Minus) * DoFConstants.FocusRadius.ADJ,
				DoFConstants.FocusRadius.MIN, DoFConstants.FocusRadius.MAX
			)
		elseif ctrlIsDown then
			FreecamDepthOfField.NearIntensity = clamp(
				FreecamDepthOfField.NearIntensity + dt * (keyboard.RightBracket - keyboard.LeftBracket) * DoFConstants.NearIntensity.ADJ,
				DoFConstants.NearIntensity.MIN, DoFConstants.NearIntensity.MAX
			)
		else
			FreecamDepthOfField.FocusDistance = clamp(
				FreecamDepthOfField.FocusDistance + dt * (keyboard.Equals - keyboard.Minus) * DoFConstants.FocusDistance.ADJ,
				DoFConstants.FocusDistance.MIN, DoFConstants.FocusDistance.MAX
			)
		end
	end
	do
		local FREECAM_TILT_RESET_GP = {
			[Enum.KeyCode.ButtonL1] = true,
			[Enum.KeyCode.ButtonR1] = true,
		}
		local FREECAM_TILT_RESET_KB = {
			[Enum.KeyCode.Z] = true,
			[Enum.KeyCode.C] = true,
		}
		local FREECAM_DOF_TOGGLE = {
			[Enum.KeyCode.BackSlash] = true,
		}
		local FREECAM_PLAYER_LOCK_TOGGLE = {
			[Enum.KeyCode.Slash] = true,
		}
		local FREECAM_PLAYER_LOCK_SWITCH = {
			[Enum.KeyCode.R] = true,
			[Enum.KeyCode.T] = true,
		}
		local FREECAM_CUSTOM_GUI_TOGGLE = {
			[Enum.KeyCode.G] = true,
		}
		local FREECAM_PLAYER_GUI_TOGGLE = {
			[Enum.KeyCode.X] = true,
		}
		local FREECAM_LEADERBOARD_TOGGLE = {
			[Enum.KeyCode.L] = true,
		}
		function resetKeys(keys, table)
			for keyEnum, _ in pairs(keys) do
				if table[keyEnum.Name] then
					table[keyEnum.Name] = 0
				end
			end
		end
		function handleDoubleTapReset(keyCode)
			local currentTime = os.clock()
			local previousPressTime = lastPressTime[keyCode]
			local timeSinceLastPress = previousPressTime and (currentTime - previousPressTime) or -1
			if previousPressTime and (timeSinceLastPress <= DOUBLE_TAP_TIME_THRESHOLD) then
				if (currentTime - lastResetTime) >= DOUBLE_TAP_DEBOUNCE_TIME then
					cameraRot = Vector3.new(cameraRot.x, cameraRot.y, 0)
					rollSpring:Reset(0)
					if FFlagUserFreecamDepthOfFieldEffect then
						resetKeys(FREECAM_TILT_RESET_GP, gamepad)
						resetKeys(FREECAM_TILT_RESET_KB, keyboard)
					else
						gamepad.ButtonL1 = 0
						gamepad.ButtonR1 = 0
						keyboard.C = 0
						keyboard.Z = 0
					end
					lastResetTime = currentTime
				end
			end
			lastPressTime[keyCode] = currentTime
		end
		function findPlayerLockRootPart(switchDirection)
			if not playerList or #playerList < 1 then
				return nil
			end
			local startIndex = currentTargetIndex
			for i = 1, #playerList do
				currentTargetIndex = ((currentTargetIndex - 1) + switchDirection) % #playerList + 1
				if currentTargetIndex < 1 then currentTargetIndex = #playerList end
				local targetPlayer = playerList[currentTargetIndex]
				local targetCharacter = targetPlayer and targetPlayer.Character
				if targetCharacter then
					local humanoid = targetCharacter:FindFirstChildOfClass("Humanoid")
					if humanoid and humanoid.RootPart then
						return humanoid.RootPart
					end
				end
				if currentTargetIndex == startIndex then break end
			end
			playerLockEnabled = false
			return nil
		end
		function Keypress(action, state, input)
			keyboard[input.KeyCode.Name] = state == Enum.UserInputState.Begin and 1 or 0
			if #customTiltLeftKeys > 0 then
				for _, keyName in ipairs(customTiltLeftKeys) do
					if input.KeyCode.Name == keyName then
						tiltLeftActiveKB = (state == Enum.UserInputState.Begin)
					end
				end
			end
			if #customTiltRightKeys > 0 then
				for _, keyName in ipairs(customTiltRightKeys) do
					if input.KeyCode.Name == keyName then
						tiltRightActiveKB = (state == Enum.UserInputState.Begin)
					end
				end
			end
			if FFlagUserFreecamTiltControl then
				if FREECAM_TILT_RESET_KB[input.KeyCode] and input.UserInputState == Enum.UserInputState.Begin then
					handleDoubleTapReset(input.KeyCode)
				end
			end
			if FFlagUserFreecamDepthOfFieldEffect then
				if FREECAM_DOF_TOGGLE[input.KeyCode] and input.UserInputState == Enum.UserInputState.Begin then
					if not FreecamDepthOfField.Enabled then
						postEffects = {}
						for _, effect in ipairs(Camera:GetChildren()) do
							if effect:IsA("DepthOfFieldEffect") and effect.Enabled then
								postEffects[#postEffects + 1] = effect
								effect.Enabled = false
							end
						end
						for _, effect in ipairs(Lighting:GetChildren()) do
							if effect:IsA("DepthOfFieldEffect") and effect.Enabled then
								postEffects[#postEffects + 1] = effect
								effect.Enabled = false
							end
						end
						cameraConnection = Camera.ChildAdded:Connect(function(child)
							if child:IsA("DepthOfFieldEffect") and child.Enabled then
								postEffects[#postEffects + 1] = child
								child.Enabled = false
							end
						end)
						lightingConnection = Lighting.ChildAdded:Connect(function(child)
							if child:IsA("DepthOfFieldEffect") and child.Enabled then
								postEffects[#postEffects + 1] = child
								child.Enabled = false
							end
						end)
					else
						for _, effect in ipairs(postEffects) do
							if effect.Parent then
								effect.Enabled = true
							end
						end
						if cameraConnection then
							cameraConnection:Disconnect()
							cameraConnection = nil
						end
						if lightingConnection then
							lightingConnection:Disconnect()
							lightingConnection = nil
						end
						postEffects = {}
					end
					FreecamDepthOfField.Enabled = not FreecamDepthOfField.Enabled
					resetKeys(FREECAM_DOF_TOGGLE, keyboard)
				end
			end
			if FFlagUserFreecamPlayerLock then
				if FREECAM_PLAYER_LOCK_TOGGLE[input.KeyCode] and input.UserInputState == Enum.UserInputState.Begin then
					playerLockEnabled = not playerLockEnabled
					if playerLockEnabled then
						playerLockZoom = PLAYER_LOCK_DEFAULT_ZOOM
						if FFlagUserFreecamPlayerLockResetHeight then
							playerLockHeight = PLAYER_LOCK_DEFAULT_HEIGHT
						end
						rootPart = findPlayerLockRootPart(0)
					end
					resetKeys(FREECAM_PLAYER_LOCK_TOGGLE, keyboard)
				end
				if FREECAM_PLAYER_LOCK_SWITCH[input.KeyCode] and input.UserInputState == Enum.UserInputState.Begin then
					if playerLockEnabled and #playerList > 0 then
						local switchDirection = 0
						if input.KeyCode == Enum.KeyCode.T then
							switchDirection = 1
						elseif input.KeyCode == Enum.KeyCode.R then
							switchDirection = -1
						end
						if switchDirection ~= 0 then
							rootPart = findPlayerLockRootPart(switchDirection)
						end
					end
					resetKeys(FREECAM_PLAYER_LOCK_SWITCH, keyboard)
				end
			end
			if FFlagUserFreecamCustomGui then
				if FREECAM_CUSTOM_GUI_TOGGLE[input.KeyCode] and input.UserInputState == Enum.UserInputState.Begin then
					if freecamGui and freecamGui.Parent then
						freecamGui.Enabled = not freecamGui.Enabled
					end
					resetKeys(FREECAM_CUSTOM_GUI_TOGGLE, keyboard)
				end
				if FREECAM_PLAYER_GUI_TOGGLE[input.KeyCode] and input.UserInputState == Enum.UserInputState.Begin then
					screenGuisEnabled = not screenGuisEnabled
					if PlayerState then
						local screenGuis = PlayerState.getScreenGuis()
						for _, gui in pairs(screenGuis) do
							if gui.Parent and gui ~= freecamGui then
								gui.Enabled = screenGuisEnabled
							end
						end
					end
					resetKeys(FREECAM_PLAYER_GUI_TOGGLE, keyboard)
				end
				if FREECAM_LEADERBOARD_TOGGLE[input.KeyCode] and input.UserInputState == Enum.UserInputState.Begin then
					leaderboardEnabled = not leaderboardEnabled
					StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.PlayerList, leaderboardEnabled)
					resetKeys(FREECAM_LEADERBOARD_TOGGLE, keyboard)
				end
			end
			return Enum.ContextActionResult.Sink
		end
		function GpButton(action, state, input)
			gamepad[input.KeyCode.Name] = state == Enum.UserInputState.Begin and 1 or 0
			if FFlagUserFreecamTiltControl then
				if FREECAM_TILT_RESET_GP[input.KeyCode] and input.UserInputState == Enum.UserInputState.Begin then
					handleDoubleTapReset(input.KeyCode)
				end
			end
			return Enum.ContextActionResult.Sink
		end
		function MousePan(action, state, input)
			local delta = input.Delta
			mouse.Delta = Vector2.new(-delta.y, -delta.x)
			return Enum.ContextActionResult.Sink
		end
		function Thumb(action, state, input)
			gamepad[input.KeyCode.Name] = input.Position
			return Enum.ContextActionResult.Sink
		end
		function Trigger(action, state, input)
			gamepad[input.KeyCode.Name] = input.Position.z
			return Enum.ContextActionResult.Sink
		end
		function MouseWheel(action, state, input)
			mouse[input.UserInputType.Name] = -input.Position.z
			return Enum.ContextActionResult.Sink
		end
		function Zero(t)
			for k, v in pairs(t) do
				t[k] = v*0
			end
		end
		function Input.StartCapture()
			if FFlagUserFreecamControlSpeed then
				ContextActionService:BindActionAtPriority("FreecamKeyboard", Keypress, false, INPUT_PRIORITY,
					Enum.KeyCode.W, Enum.KeyCode.U, Enum.KeyCode.A, Enum.KeyCode.H,
					Enum.KeyCode.S, Enum.KeyCode.J, Enum.KeyCode.D, Enum.KeyCode.K,
					Enum.KeyCode.E, Enum.KeyCode.I, Enum.KeyCode.Q, Enum.KeyCode.Y
				)
				ContextActionService:BindActionAtPriority("FreecamKeyboardControlSpeed", Keypress, false, INPUT_PRIORITY,
					Enum.KeyCode.Up, Enum.KeyCode.Down, Enum.KeyCode.Left, Enum.KeyCode.Right
				)
				ContextActionService:BindActionAtPriority("FreecamGamepadControlSpeed", GpButton, false, INPUT_PRIORITY,
					Enum.KeyCode.DPadUp, Enum.KeyCode.DPadDown, Enum.KeyCode.DPadLeft, Enum.KeyCode.DPadRight
				)
			else
				ContextActionService:BindActionAtPriority("FreecamKeyboard", Keypress, false, INPUT_PRIORITY,
					Enum.KeyCode.W, Enum.KeyCode.U, Enum.KeyCode.A, Enum.KeyCode.H,
					Enum.KeyCode.S, Enum.KeyCode.J, Enum.KeyCode.D, Enum.KeyCode.K,
					Enum.KeyCode.E, Enum.KeyCode.I, Enum.KeyCode.Q, Enum.KeyCode.Y,
					Enum.KeyCode.Up, Enum.KeyCode.Down
				)
			end
			if FFlagUserFreecamTiltControl then
				ContextActionService:BindActionAtPriority("FreecamKeyboardTiltControl", Keypress, false, INPUT_PRIORITY,
					Enum.KeyCode.Z, Enum.KeyCode.C
				)
				ContextActionService:BindActionAtPriority("FreecamGamepadTiltControl", GpButton, false, INPUT_PRIORITY,
					Enum.KeyCode.ButtonL1, Enum.KeyCode.ButtonR1
				)
				ContextActionService:BindActionAtPriority("FreecamKeyboardTiltControlSpeed", Keypress, false, INPUT_PRIORITY,
					Enum.KeyCode.Comma, Enum.KeyCode.Period
				)
				if FFlagUserFreecamSmoothnessControl then
					ContextActionService:BindActionAtPriority("FreecamKeyboardSmoothnessControl", Keypress, false, INPUT_PRIORITY,
						Enum.KeyCode.LeftBracket, Enum.KeyCode.RightBracket,
						Enum.KeyCode.Semicolon, Enum.KeyCode.Quote,
						Enum.KeyCode.V, Enum.KeyCode.B, Enum.KeyCode.N, Enum.KeyCode.M
					)
				end
			end
			if FFlagUserFreecamDepthOfFieldEffect then
				ContextActionService:BindActionAtPriority("FreecamKeyboardDoFToggle", Keypress, false, INPUT_PRIORITY, Enum.KeyCode.BackSlash)
				ContextActionService:BindActionAtPriority("FreecamKeyboardDoFControls", Keypress, false, INPUT_PRIORITY,
					Enum.KeyCode.Minus, Enum.KeyCode.Equals
				)
			end
			if FFlagUserFreecamPlayerLock then
				ContextActionService:BindActionAtPriority("FreecamKeyboardPlayerLockToggle", Keypress, false, INPUT_PRIORITY, Enum.KeyCode.Slash)
				ContextActionService:BindActionAtPriority("FreecamKeyboardPlayerLockSwitch", Keypress, false, INPUT_PRIORITY, Enum.KeyCode.R, Enum.KeyCode.T)
			end
			if FFlagUserFreecamCustomGui then
				ContextActionService:BindActionAtPriority("FreecamKeyboardCustomGuiToggle", Keypress, false, INPUT_PRIORITY, Enum.KeyCode.G)
				ContextActionService:BindActionAtPriority("FreecamKeyboardPlayerGuiToggle", Keypress, false, INPUT_PRIORITY, Enum.KeyCode.X)
				ContextActionService:BindActionAtPriority("FreecamKeyboardLeaderboardToggle", Keypress, false, INPUT_PRIORITY, Enum.KeyCode.L)
			end
			ContextActionService:BindActionAtPriority("FreecamMousePan", MousePan, false, INPUT_PRIORITY, Enum.UserInputType.MouseMovement)
			ContextActionService:BindActionAtPriority("FreecamMouseWheel", MouseWheel, false, INPUT_PRIORITY, Enum.UserInputType.MouseWheel)
			ContextActionService:BindActionAtPriority("FreecamGamepadButton", GpButton, false, INPUT_PRIORITY, Enum.KeyCode.ButtonX, Enum.KeyCode.ButtonY)
			ContextActionService:BindActionAtPriority("FreecamGamepadTrigger", Trigger, false, INPUT_PRIORITY, Enum.KeyCode.ButtonR2, Enum.KeyCode.ButtonL2)
			ContextActionService:BindActionAtPriority("FreecamGamepadThumbstick", Thumb, false, INPUT_PRIORITY, Enum.KeyCode.Thumbstick1, Enum.KeyCode.Thumbstick2)
		end
		function Input.StopCapture()
			if not FFlagUserFreecamCustomGui then
				navSpeed = 1
				if FFlagUserFreecamControlSpeed then fovSpeed = 1 end
				if FFlagUserFreecamTiltControl then rollSpeed = 1 end
			end
			tiltLeftActiveKB = false
			tiltRightActiveKB = false
			Zero(gamepad)
			Zero(keyboard)
			Zero(mouse)
			ContextActionService:UnbindAction("FreecamKeyboard")
			if FFlagUserFreecamControlSpeed then
				ContextActionService:UnbindAction("FreecamKeyboardControlSpeed")
				ContextActionService:UnbindAction("FreecamGamepadControlSpeed")
			end
			if FFlagUserFreecamTiltControl then
				ContextActionService:UnbindAction("FreecamKeyboardTiltControl")
				ContextActionService:UnbindAction("FreecamGamepadTiltControl")
				ContextActionService:UnbindAction("FreecamKeyboardTiltControlSpeed")
				if FFlagUserFreecamSmoothnessControl then
					ContextActionService:UnbindAction("FreecamKeyboardSmoothnessControl")
				end
			end
			if FFlagUserFreecamDepthOfFieldEffect then
				ContextActionService:UnbindAction("FreecamKeyboardDoFToggle")
				ContextActionService:UnbindAction("FreecamKeyboardDoFControls")
			end
			if FFlagUserFreecamPlayerLock then
				ContextActionService:UnbindAction("FreecamKeyboardPlayerLockToggle")
				ContextActionService:UnbindAction("FreecamKeyboardPlayerLockSwitch")
			end
			if FFlagUserFreecamCustomGui then
				ContextActionService:UnbindAction("FreecamKeyboardCustomGuiToggle")
				ContextActionService:UnbindAction("FreecamKeyboardPlayerGuiToggle")
				ContextActionService:UnbindAction("FreecamKeyboardLeaderboardToggle")
			end
			ContextActionService:UnbindAction("FreecamMousePan")
			ContextActionService:UnbindAction("FreecamMouseWheel")
			ContextActionService:UnbindAction("FreecamGamepadButton")
			ContextActionService:UnbindAction("FreecamGamepadTrigger")
			ContextActionService:UnbindAction("FreecamGamepadThumbstick")
		end
		function Input.getNavSpeed() return navSpeed end
		function Input.getFovSpeed() return fovSpeed end
		function Input.getRollSpeed() return rollSpeed end
	end
end
function StepFreecam(dt)
	if FFlagUserFreecamSmoothnessControl then Input.SpringControl(dt) end
	if FFlagUserFreecamDepthOfFieldEffect then
		if FreecamDepthOfField and FreecamDepthOfField.Parent then Input.DoF(dt) end
	end
	local vel = velSpring:Update(dt, Input.Vel(dt))
	local pan = panSpring:Update(dt, Input.Pan(dt))
	local fov = fovSpring:Update(dt, Input.Fov(dt))
	local roll
	if FFlagUserFreecamTiltControl then roll = rollSpring:Update(dt, Input.Roll(dt)) end
	local zoomFactor = sqrt(tan(rad(70/2))/tan(rad(cameraFov/2)))
	cameraFov = clamp(cameraFov + fov*FOV_GAIN*(dt/zoomFactor), 1, 120)
	local cameraCFrame
	if FFlagUserFreecamTiltControl then
		local panVector = pan*PAN_GAIN*(dt/zoomFactor)
		cameraRot = cameraRot + Vector3.new(panVector.X, panVector.Y, roll*ROLL_GAIN*(dt/zoomFactor))
		if FFlagUserFreecamSmoothnessControl then
			cameraRot = Vector3.new(cameraRot.x%(2*pi), cameraRot.y%(2*pi), cameraRot.z%(2*pi))
		else
			cameraRot = Vector3.new(clamp(cameraRot.x, -PITCH_LIMIT, PITCH_LIMIT), cameraRot.y%(2*pi), cameraRot.z)
		end
		cameraCFrame = CFrame.new(cameraPos)*CFrame.fromOrientation(cameraRot.x, cameraRot.y, cameraRot.z)*CFrame.new(vel*NAV_GAIN*dt)
	else
		cameraRot = cameraRot + pan*PAN_GAIN*(dt/zoomFactor)
		cameraRot = Vector2.new(clamp(cameraRot.x, -PITCH_LIMIT, PITCH_LIMIT), cameraRot.y%(2*pi))
		cameraCFrame = CFrame.new(cameraPos)*CFrame.fromOrientation(cameraRot.x, cameraRot.y, 0)*CFrame.new(vel*NAV_GAIN*dt)
	end
	if FFlagUserFreecamPlayerLock then
		if playerLockEnabled and rootPart then
			local zoomDelta = vel.Z * NAV_GAIN.Z * dt
			local heightDelta = vel.Y * NAV_GAIN.Y * dt
			playerLockZoom = clamp(playerLockZoom + zoomDelta, PLAYER_LOCK_MIN_ZOOM, PLAYER_LOCK_MAX_ZOOM)
			playerLockHeight = clamp(playerLockHeight + heightDelta, -PLAYER_LOCK_HEIGHT_RANGE, PLAYER_LOCK_HEIGHT_RANGE)
			local targetCFrame = CFrame.new(rootPart.Position + Vector3.new(0, playerLockHeight, 0))
			local rotationCFrame
			if FFlagUserFreecamTiltControl then
				rotationCFrame = CFrame.fromOrientation(cameraRot.x, cameraRot.y, cameraRot.z)
			else
				rotationCFrame = CFrame.fromOrientation(cameraRot.x, cameraRot.y, 0)
			end
			cameraCFrame = targetCFrame * rotationCFrame * CFrame.new(0, 0, playerLockZoom)
		end
	end
	cameraPos = cameraCFrame.p
	Camera.CFrame = cameraCFrame
	Camera.Focus = cameraCFrame
	Camera.FieldOfView = cameraFov
end
function CheckMouseLockAvailability()
	local devAllowsMouseLock = Players.LocalPlayer.DevEnableMouseLock
	local devMovementModeIsScriptable = Players.LocalPlayer.DevComputerMovementMode == Enum.DevComputerMovementMode.Scriptable
	local userHasMouseLockModeEnabled = GameSettings.ControlMode == Enum.ControlMode.MouseLockSwitch
	local userHasClickToMoveEnabled = GameSettings.ComputerMovementMode == Enum.ComputerMovementMode.ClickToMove
	local MouseLockAvailable = devAllowsMouseLock and userHasMouseLockModeEnabled and not userHasClickToMoveEnabled and not devMovementModeIsScriptable
	return MouseLockAvailable
end
PlayerState = {}
do
	local mouseBehavior = nil
	local mouseIconEnabled = nil
	local cameraType = nil
	local cameraFocus = nil
	local cameraCFrame = nil
	local cameraFieldOfView = nil
	local screenGuis = {}
	local coreGuis = { Backpack = true, Chat = true, Health = true, PlayerList = true }
	local setCores = { BadgesNotificationsActive = true, PointsNotificationsActive = true }
	function PlayerState.Push()
		for name in pairs(coreGuis) do
			coreGuis[name] = StarterGui:GetCoreGuiEnabled(Enum.CoreGuiType[name])
			StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType[name], false)
		end
		for name in pairs(setCores) do
			setCores[name] = StarterGui:GetCore(name)
			StarterGui:SetCore(name, false)
		end
		local playergui = LocalPlayer:FindFirstChildOfClass("PlayerGui")
		if playergui then
			local existingGuis = {}
			for _, gui in pairs(playergui:GetChildren()) do
				if gui:IsA("ScreenGui") and gui.Enabled and gui.Name ~= "FreeCamTouchControll" then
					existingGuis[gui] = true
					table.insert(screenGuis, gui)
					gui.Enabled = false
				end
			end
			if FFlagUserFixFreecamGuiChangeVisibility then
				playerGuiConnection = playergui.ChildAdded:Connect(function(child)
					if child:IsA("ScreenGui") and child.Enabled and child.Name ~= "FreeCamTouchControll" and not existingGuis[child] then
						table.insert(screenGuis, child)
						if FFlagUserFreecamCustomGui then
							child.Enabled = screenGuisEnabled
						else
							child.Enabled = false
						end
					end
				end)
			end
		end
		cameraFieldOfView = Camera.FieldOfView
		Camera.FieldOfView = 70
		cameraType = Camera.CameraType
		Camera.CameraType = Enum.CameraType.Custom
		cameraCFrame = Camera.CFrame
		cameraFocus = Camera.Focus
		mouseIconEnabled = UserInputService.MouseIconEnabled
		UserInputService.MouseIconEnabled = false
		if FFlagUserExitFreecamBreaksWithShiftlock and CheckMouseLockAvailability() then
			mouseBehavior = Enum.MouseBehavior.Default
		else
			mouseBehavior = UserInputService.MouseBehavior
		end
		UserInputService.MouseBehavior = Enum.MouseBehavior.Default
	end
	function PlayerState.Pop()
		for name, isEnabled in pairs(coreGuis) do
			if isEnabled ~= nil then
				pcall(function() StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType[name], isEnabled) end)
			end
		end
		for name, isEnabled in pairs(setCores) do
			if isEnabled ~= nil then
				pcall(function() StarterGui:SetCore(name, isEnabled) end)
			end
		end
		for _, gui in pairs(screenGuis) do
			if gui and gui.Parent then
				if FFlagUserFreecamCustomGui then
					if gui ~= freecamGui then
						pcall(function() gui.Enabled = true end)
					end
				else
					pcall(function() gui.Enabled = true end)
				end
			end
		end
		if FFlagUserFixFreecamGuiChangeVisibility then
			if playerGuiConnection then
				playerGuiConnection:Disconnect()
				playerGuiConnection = nil
			end
			screenGuis = {}
		end
		if cameraFieldOfView then
			pcall(function() Camera.FieldOfView = cameraFieldOfView end)
			cameraFieldOfView = nil
		end
		if cameraType then
			pcall(function() Camera.CameraType = cameraType end)
			cameraType = nil
		end
		if cameraCFrame then
			pcall(function() Camera.CFrame = cameraCFrame end)
			cameraCFrame = nil
		end
		if cameraFocus then
			pcall(function() Camera.Focus = cameraFocus end)
			cameraFocus = nil
		end
		if mouseIconEnabled ~= nil then
			pcall(function() UserInputService.MouseIconEnabled = mouseIconEnabled end)
			mouseIconEnabled = nil
		end
		if mouseBehavior ~= nil then
			pcall(function() UserInputService.MouseBehavior = mouseBehavior end)
			mouseBehavior = nil
		end
	end
	function PlayerState.getScreenGuis() return screenGuis end
end
function removePlayerFromList(player)
	for i, p in ipairs(playerList) do
		if p == player then
			table.remove(playerList, i)
			if currentTargetIndex == i and playerLockEnabled then
				playerLockEnabled = false
				currentTargetIndex = 1
			end
			if currentTargetIndex > i then
				currentTargetIndex = currentTargetIndex - 1
			end
			if currentTargetIndex > #playerList or currentTargetIndex < 1 then
				currentTargetIndex = 1
			end
			break
		end
	end
end
function initializePlayerList()
	playerList = Players:GetPlayers()
	for i, p in ipairs(playerList) do
		if p == LocalPlayer then
			currentTargetIndex = i
			break
		end
	end
	playerAddedConnection = Players.PlayerAdded:Connect(function(player)
		table.insert(playerList, player)
	end)
	playerRemovingConnection = Players.PlayerRemoving:Connect(removePlayerFromList)
end
local customGui = nil
local Draggable = {}
Draggable.__index = Draggable
function Draggable.new(dragFrame, targetFrame, callbacks)
	local self = setmetatable({}, Draggable)
	self.dragFrame = dragFrame
	self.targetFrame = targetFrame
	self.callbacks = callbacks or {}
	self.dragData = { dragging = false, dragStart = nil, startPos = nil, dragDistance = 0, isDraggingStarted = false, currentTouch = nil, originalZIndex = targetFrame.ZIndex, DRAG_THRESHOLD = 5 }
	self.connections = { mouseUp = nil, touchUp = nil, movement = nil, began = nil }
	self:setup()
	return self
end
function Draggable:cleanupConnections()
	if self.connections.mouseUp then self.connections.mouseUp:Disconnect() end
	if self.connections.touchUp then self.connections.touchUp:Disconnect() end
	if self.connections.movement then self.connections.movement:Disconnect() end
	self.connections.mouseUp = nil
	self.connections.touchUp = nil
	self.connections.movement = nil
end
function Draggable:resetState()
	self.dragData.dragging = false
	self.dragData.dragStart = nil
	self.dragData.startPos = nil
	self.dragData.dragDistance = 0
	self.dragData.isDraggingStarted = false
	self.dragData.currentTouch = nil
	self:cleanupConnections()
end
function Draggable:getDistance(pos1, pos2)
	return math.sqrt((pos2.X - pos1.X)^2 + (pos2.Y - pos1.Y)^2)
end
function Draggable:isMouseOverFrame(frame, position)
	local absPos, absSize = frame.AbsolutePosition, frame.AbsoluteSize
	return position.X >= absPos.X and position.X <= absPos.X + absSize.X and position.Y >= absPos.Y and position.Y <= absPos.Y + absSize.Y
end
function Draggable:updatePosition(input)
	if not self.dragData.dragging or not self.dragData.dragStart then return end
	local delta = input.Position - self.dragData.dragStart
	self.dragData.dragDistance = self:getDistance(input.Position, self.dragData.dragStart)
	self.targetFrame.Position = UDim2.new(self.dragData.startPos.X.Scale, self.dragData.startPos.X.Offset + delta.X, self.dragData.startPos.Y.Scale, self.dragData.startPos.Y.Offset + delta.Y)
end
function Draggable:onDragEnd()
	if self.dragData.dragging then
		local wasDragged = self.dragData.dragDistance > self.dragData.DRAG_THRESHOLD
		if not wasDragged and self.callbacks.onClick then self.callbacks.onClick() end
		if self.callbacks.onDragEnd then self.callbacks.onDragEnd(wasDragged) end
		self:resetState()
	end
end
function Draggable:onInputEnded(input)
	if not self.dragData.dragging then return end
	if input.UserInputType == Enum.UserInputType.MouseButton1 then
		self:onDragEnd()
	elseif input.UserInputType == Enum.UserInputType.Touch and input == self.dragData.currentTouch then
		self:onDragEnd()
	end
end
function Draggable:onMovement(input)
	if not self.dragData.dragging then return end
	if input.UserInputType == Enum.UserInputType.MouseMovement or input == self.dragData.currentTouch then
		if not self.dragData.isDraggingStarted and self.dragData.dragDistance > self.dragData.DRAG_THRESHOLD then
			self.dragData.isDraggingStarted = true
			if self.callbacks.onDragStart then self.callbacks.onDragStart() end
		end
		self:updatePosition(input)
	end
end
function Draggable:onInputBegan(input)
	local isClick = input.UserInputState == Enum.UserInputState.Begin and (input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch)
	if isClick and not self.dragData.dragging then
		if self:isMouseOverFrame(self.dragFrame, input.Position) then
			self.dragData.dragging = true
			self.dragData.dragStart = input.Position
			self.dragData.startPos = self.targetFrame.Position
			self.dragData.dragDistance = 0
			self.dragData.isDraggingStarted = false
			self.dragData.currentTouch = (input.UserInputType == Enum.UserInputType.Touch) and input or nil
			self:cleanupConnections()
			self.connections.mouseUp = UserInputService.InputEnded:Connect(function(endInput)
				if endInput.UserInputType == Enum.UserInputType.MouseButton1 then self:onInputEnded(endInput) end
			end)
			self.connections.touchUp = UserInputService.InputEnded:Connect(function(endInput)
				if endInput.UserInputType == Enum.UserInputType.Touch then self:onInputEnded(endInput) end
			end)
			self.connections.movement = UserInputService.InputChanged:Connect(function(moveInput)
				if moveInput.UserInputType == Enum.UserInputType.MouseMovement or moveInput.UserInputType == Enum.UserInputType.Touch then
					self:onMovement(moveInput)
				end
			end)
		end
	end
end
function Draggable:setup()
	self.connections.began = self.dragFrame.InputBegan:Connect(function(input) self:onInputBegan(input) end)
end
function Draggable:Destroy()
	self:cleanupConnections()
	if self.connections.began then self.connections.began:Disconnect() end
end
local Slider = {}
Slider.__index = Slider
function Slider.new(sliderBar, sliderHandle, onChange)
	local self = setmetatable({}, Slider)
	self.sliderBar = sliderBar
	self.sliderHandle = sliderHandle
	self.onChange = onChange
	self.dragData = { dragging = false, currentTouch = nil, lastValue = nil }
	self.connections = { began = nil, mouseUp = nil, touchUp = nil, movement = nil }
	self:setup()
	return self
end
function Slider:cleanupConnections()
	if self.connections.mouseUp then self.connections.mouseUp:Disconnect() end
	if self.connections.touchUp then self.connections.touchUp:Disconnect() end
	if self.connections.movement then self.connections.movement:Disconnect() end
	self.connections.mouseUp = nil
	self.connections.touchUp = nil
	self.connections.movement = nil
end
function Slider:isMouseOverFrame(frame, position)
	local absPos, absSize = frame.AbsolutePosition, frame.AbsoluteSize
	return position.X >= absPos.X and position.X <= absPos.X + absSize.X and position.Y >= absPos.Y and position.Y <= absPos.Y + absSize.Y
end
function Slider:updatePosition(input)
	if not self.dragData.dragging then return end
	local mousePos = input.Position.X
	local barPos = self.sliderBar.AbsolutePosition.X
	local barWidth = self.sliderBar.AbsoluteSize.X
	if barWidth <= 0 then return end
	local newX = math.clamp(mousePos - barPos - 7, -7, barWidth - 7)
	if not self.dragData.lastValue or math.abs(newX - self.dragData.lastValue) > 0.5 then
		self.sliderHandle.Position = UDim2.new(0, newX, 0, -5)
		local normalizedValue = math.clamp((newX + 7) / barWidth, 0, 1)
		if self.onChange then self.onChange(normalizedValue) end
		self.dragData.lastValue = newX
	end
end
function Slider:onDragEnd() if self.dragData.dragging then self:resetState() end end
function Slider:resetState() self.dragData.dragging = false; self.dragData.currentTouch = nil; self.dragData.lastValue = nil; self:cleanupConnections() end
function Slider:onInputEnded(input)
	if not self.dragData.dragging then return end
	if input.UserInputType == Enum.UserInputType.MouseButton1 then self:onDragEnd()
	elseif input.UserInputType == Enum.UserInputType.Touch and input == self.dragData.currentTouch then self:onDragEnd() end
end
function Slider:onMovement(input)
	if not self.dragData.dragging then return end
	if input.UserInputType == Enum.UserInputType.MouseMovement or input == self.dragData.currentTouch then
		self:updatePosition(input)
	end
end
function Slider:onInputBegan(input)
	local isClick = input.UserInputState == Enum.UserInputState.Begin and (input.UserInputType == Enum.UserInputType.MouseButton1 or input.UserInputType == Enum.UserInputType.Touch)
	if isClick and not self.dragData.dragging and self:isMouseOverFrame(self.sliderHandle, input.Position) then
		self.dragData.dragging = true
		self.dragData.currentTouch = (input.UserInputType == Enum.UserInputType.Touch) and input or nil
		self:cleanupConnections()
		self.connections.mouseUp = UserInputService.InputEnded:Connect(function(endInput)
			if endInput.UserInputType == Enum.UserInputType.MouseButton1 then self:onInputEnded(endInput) end
		end)
		self.connections.touchUp = UserInputService.InputEnded:Connect(function(endInput)
			if endInput.UserInputType == Enum.UserInputType.Touch then self:onInputEnded(endInput) end
		end)
		self.connections.movement = UserInputService.InputChanged:Connect(function(moveInput)
			if moveInput.UserInputType == Enum.UserInputType.MouseMovement or moveInput.UserInputType == Enum.UserInputType.Touch then
				self:onMovement(moveInput)
			end
		end)
		self:updatePosition(input)
	end
end
function Slider:setup() self.connections.began = self.sliderHandle.InputBegan:Connect(function(input) self:onInputBegan(input) end) end
function Slider:Destroy() self:cleanupConnections(); if self.connections.began then self.connections.began:Disconnect() end end
function Slider:SetValue(normalizedValue) local barWidth = self.sliderBar.AbsoluteSize.X; local newX = math.clamp(normalizedValue * barWidth - 7, -7, barWidth - 7); self.sliderHandle.Position = UDim2.new(0, newX, 0, -5) end
local Toggle = {}
Toggle.__index = Toggle
function Toggle.new(toggleContainer, toggleThumb, onChange)
	local self = setmetatable({}, Toggle)
	self.toggleContainer = toggleContainer
	self.toggleThumb = toggleThumb
	self.onChange = onChange
	self.state = false
	self:setup()
	return self
end
function Toggle:animateOn()
	local tweenInfo = TweenInfo.new(0.2, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
	TweenService:Create(self.toggleContainer, tweenInfo, {BackgroundColor3 = Color3.fromRGB(100, 200, 255)}):Play()
	TweenService:Create(self.toggleThumb, tweenInfo, {Position = UDim2.new(0, self.toggleContainer.AbsoluteSize.X - 22, 0, 2)}):Play()
end
function Toggle:animateOff()
	local tweenInfo = TweenInfo.new(0.2, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
	TweenService:Create(self.toggleContainer, tweenInfo, {BackgroundColor3 = Color3.fromRGB(80, 80, 100)}):Play()
	TweenService:Create(self.toggleThumb, tweenInfo, {Position = UDim2.new(0, 2, 0, 2)}):Play()
end
function Toggle:SetState(state) self.state = state; if state then self:animateOn() else self:animateOff() end; if self.onChange then self.onChange(state) end end
function Toggle:GetState() return self.state end
function Toggle:Toggle() self:SetState(not self.state) end
function Toggle:setup() local clickBtn = Instance.new("TextButton"); clickBtn.Size = UDim2.new(1,0,1,0); clickBtn.BackgroundTransparency = 1; clickBtn.Text = ""; clickBtn.AutoButtonColor = false; clickBtn.Parent = self.toggleContainer; clickBtn.MouseButton1Click:Connect(function() self:Toggle() end); self.toggleThumb.MouseButton1Click:Connect(function() self:Toggle() end) end
local DummyUI = {}
DummyUI.__index = DummyUI
function DummyUI.new(title, size)
	local self = setmetatable({}, DummyUI)
	self.title = title or "Freecam Control"
	self.size = size or UDim2.new(0, 300, 0, 380)
	self.position = UDim2.new(0.5, -150, 0.5, -200)
	self.tabs = {}
	self.currentTab = nil
	self.components = {}
	self:createUI()
	self:setupAlwaysOnTop()
	self:setupDraggable()
	return self
end
function DummyUI:createUI()
	self.mainFrame = Instance.new("Frame")
	self.mainFrame.Name = "DummyUI"
	self.mainFrame.Size = self.size
	self.mainFrame.Position = self.position
	self.mainFrame.BackgroundColor3 = Color3.fromRGB(25, 25, 35)
	self.mainFrame.BackgroundTransparency = 0.05
	self.mainFrame.BorderSizePixel = 0
	self.mainFrame.ClipsDescendants = true
	self.mainFrame.Active = true
	self.mainFrame.ZIndex = 1000
	self.mainFrame.Parent = freecamGui
	local mainCorner = Instance.new("UICorner")
	mainCorner.CornerRadius = UDim.new(0, 12)
	mainCorner.Parent = self.mainFrame
	self.topBar = Instance.new("TextButton")
	self.topBar.Size = UDim2.new(1, 0, 0, 40)
	self.topBar.Position = UDim2.new(0, 0, 0, 0)
	self.topBar.BackgroundColor3 = Color3.fromRGB(35, 35, 45)
	self.topBar.BackgroundTransparency = 0.2
	self.topBar.BorderSizePixel = 0
	self.topBar.Text = ""
	self.topBar.AutoButtonColor = false
	self.topBar.ZIndex = 1001
	self.topBar.Parent = self.mainFrame
	local topCorner = Instance.new("UICorner")
	topCorner.CornerRadius = UDim.new(0, 12)
	topCorner.Parent = self.topBar
	local titleLabel = Instance.new("TextLabel")
	titleLabel.Size = UDim2.new(1, -50, 1, 0)
	titleLabel.Position = UDim2.new(0, 15, 0, 0)
	titleLabel.BackgroundTransparency = 1
	titleLabel.TextColor3 = Color3.fromRGB(255, 255, 255)
	titleLabel.Font = Enum.Font.GothamBold
	titleLabel.TextSize = 14
	titleLabel.Text = " " .. self.title
	titleLabel.TextXAlignment = Enum.TextXAlignment.Left
	titleLabel.TextYAlignment = Enum.TextYAlignment.Center
	titleLabel.Parent = self.topBar
	self.closeButton = Instance.new("TextButton")
	self.closeButton.Size = UDim2.new(0, 32, 0, 32)
	self.closeButton.Position = UDim2.new(1, -38, 0, 4)
	self.closeButton.BackgroundColor3 = Color3.fromRGB(255, 70, 70)
	self.closeButton.BackgroundTransparency = 0.8
	self.closeButton.TextColor3 = Color3.fromRGB(255, 255, 255)
	self.closeButton.Font = Enum.Font.GothamBold
	self.closeButton.TextSize = 16
	self.closeButton.Text = "✕"
	self.closeButton.AutoButtonColor = false
	self.closeButton.BorderSizePixel = 0
	self.closeButton.Parent = self.topBar
	local closeCorner = Instance.new("UICorner")
	closeCorner.CornerRadius = UDim.new(0, 16)
	closeCorner.Parent = self.closeButton
	self.closeButton.MouseButton1Click:Connect(function()
		self.mainFrame.Visible = false
		if floatingButton then floatingButton.Visible = true end
	end)
	self.tabScrollingFrame = Instance.new("ScrollingFrame")
	self.tabScrollingFrame.Size = UDim2.new(1, -20, 0, 40)
	self.tabScrollingFrame.Position = UDim2.new(0, 10, 0, 48)
	self.tabScrollingFrame.BackgroundTransparency = 1
	self.tabScrollingFrame.BorderSizePixel = 0
	self.tabScrollingFrame.ScrollBarThickness = 3
	self.tabScrollingFrame.ScrollBarImageColor3 = Color3.fromRGB(80, 80, 100)
	self.tabScrollingFrame.CanvasSize = UDim2.new(0, 0, 0, 0)
	self.tabScrollingFrame.ScrollingDirection = Enum.ScrollingDirection.X
	self.tabScrollingFrame.Parent = self.mainFrame
	self.tabLayout = Instance.new("UIListLayout")
	self.tabLayout.FillDirection = Enum.FillDirection.Horizontal
	self.tabLayout.Padding = UDim.new(0, 8)
	self.tabLayout.HorizontalAlignment = Enum.HorizontalAlignment.Left
	self.tabLayout.VerticalAlignment = Enum.VerticalAlignment.Center
	self.tabLayout.Parent = self.tabScrollingFrame
	self.tabLayout:GetPropertyChangedSignal("AbsoluteContentSize"):Connect(function()
		self.tabScrollingFrame.CanvasSize = UDim2.new(0, self.tabLayout.AbsoluteContentSize.X + 20, 0, 0)
	end)
end
function DummyUI:setupAlwaysOnTop()
	self.mainFrame.ZIndex = 1000
	local function raiseToTop()
		local maxZIndex = 0
		for _, child in ipairs(self.mainFrame.Parent:GetChildren()) do
			if child:IsA("Frame") and child ~= self.mainFrame and child.Visible and child.ZIndex then
				maxZIndex = math.max(maxZIndex, child.ZIndex)
			end
		end
		self.mainFrame.ZIndex = math.max(maxZIndex + 1, 1000)
	end
	self.topBar.InputBegan:Connect(raiseToTop)
end
function DummyUI:setupDraggable()
	self.draggable = Draggable.new(self.topBar, self.mainFrame)
end
function DummyUI:AddTab(tabName)
	local tabButton = Instance.new("TextButton")
	tabButton.Name = tabName .. "Tab"
	tabButton.Size = UDim2.new(0, 100, 0, 34)
	tabButton.BackgroundColor3 = Color3.fromRGB(45, 45, 55)
	tabButton.TextColor3 = Color3.fromRGB(180, 180, 200)
	tabButton.Font = Enum.Font.GothamSemibold
	tabButton.TextSize = 13
	tabButton.Text = tabName
	tabButton.AutoButtonColor = false
	tabButton.BorderSizePixel = 0
	tabButton.Parent = self.tabScrollingFrame
	local buttonCorner = Instance.new("UICorner")
	buttonCorner.CornerRadius = UDim.new(0, 17)
	buttonCorner.Parent = tabButton
	local tabContent = Instance.new("ScrollingFrame")
	tabContent.Name = tabName .. "Content"
	tabContent.Size = UDim2.new(1, -20, 1, -105)
	tabContent.Position = UDim2.new(0, 10, 0, 96)
	tabContent.BackgroundTransparency = 1
	tabContent.BorderSizePixel = 0
	tabContent.ScrollBarThickness = 6
	tabContent.ScrollBarImageColor3 = Color3.fromRGB(80, 80, 100)
	tabContent.CanvasSize = UDim2.new(0, 0, 0, 0)
	tabContent.Visible = false
	tabContent.Parent = self.mainFrame
	local tabLayout = Instance.new("UIListLayout")
	tabLayout.Padding = UDim.new(0, 12)
	tabLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
	tabLayout.Parent = tabContent
	tabLayout:GetPropertyChangedSignal("AbsoluteContentSize"):Connect(function()
		tabContent.CanvasSize = UDim2.new(0, 0, 0, tabLayout.AbsoluteContentSize.Y + 20)
	end)
	self.tabs[tabName] = { button = tabButton, content = tabContent, layout = tabLayout }
	if not self.currentTab then self:SwitchTab(tabName) end
	tabButton.MouseButton1Click:Connect(function() self:SwitchTab(tabName) end)
	return tabContent
end
function DummyUI:SwitchTab(tabName)
	if not self.tabs[tabName] then return end
	self.currentTab = tabName
	local activeColor = Color3.fromRGB(100, 200, 255)
	local inactiveColor = Color3.fromRGB(45, 45, 55)
	local activeTextColor = Color3.fromRGB(255, 255, 255)
	local inactiveTextColor = Color3.fromRGB(180, 180, 200)
	for name, tab in pairs(self.tabs) do
		tab.button.BackgroundColor3 = (name == tabName) and activeColor or inactiveColor
		tab.button.TextColor3 = (name == tabName) and activeTextColor or inactiveTextColor
		tab.content.Visible = (name == tabName)
	end
end
function DummyUI:AddToggle(parent, config)
	local card = Instance.new("Frame")
	card.Size = UDim2.new(0, 260, 0, 60)
	card.BackgroundColor3 = Color3.fromRGB(35, 35, 45)
	card.BackgroundTransparency = 0.3
	card.BorderSizePixel = 0
	card.Parent = parent
	local cardCorner = Instance.new("UICorner")
	cardCorner.CornerRadius = UDim.new(0, 10)
	cardCorner.Parent = card
	local title = Instance.new("TextLabel")
	title.Size = UDim2.new(1, -20, 0, 24)
	title.Position = UDim2.new(0, 12, 0, 8)
	title.BackgroundTransparency = 1
	title.TextColor3 = Color3.fromRGB(255, 255, 255)
	title.Font = Enum.Font.GothamBold
	title.TextSize = 14
	title.Text = config.text or "Toggle"
	title.TextXAlignment = Enum.TextXAlignment.Left
	title.Parent = card
	local toggleContainer = Instance.new("Frame")
	toggleContainer.Size = UDim2.new(0, 44, 0, 24)
	toggleContainer.Position = UDim2.new(1, -56, 0, 18)
	toggleContainer.BackgroundColor3 = Color3.fromRGB(80, 80, 100)
	toggleContainer.BorderSizePixel = 0
	toggleContainer.ClipsDescendants = true
	toggleContainer.Parent = card
	local toggleCorner = Instance.new("UICorner")
	toggleCorner.CornerRadius = UDim.new(0, 12)
	toggleCorner.Parent = toggleContainer
	local toggleThumb = Instance.new("TextButton")
	toggleThumb.Size = UDim2.new(0, 20, 0, 20)
	toggleThumb.Position = UDim2.new(0, 2, 0, 2)
	toggleThumb.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
	toggleThumb.BorderSizePixel = 0
	toggleThumb.Text = ""
	toggleThumb.AutoButtonColor = false
	toggleThumb.Parent = toggleContainer
	local thumbCorner = Instance.new("UICorner")
	thumbCorner.CornerRadius = UDim.new(0, 10)
	thumbCorner.Parent = toggleThumb
	local toggle = Toggle.new(toggleContainer, toggleThumb, config.callback)
	if config.default then toggle:SetState(config.default) end
	table.insert(self.components, toggle)
	return toggle
end
function DummyUI:AddSlider(parent, config)
	local card = Instance.new("Frame")
	card.Size = UDim2.new(0, 260, 0, 70)
	card.BackgroundColor3 = Color3.fromRGB(35, 35, 45)
	card.BackgroundTransparency = 0.3
	card.BorderSizePixel = 0
	card.Parent = parent
	local cardCorner = Instance.new("UICorner")
	cardCorner.CornerRadius = UDim.new(0, 10)
	cardCorner.Parent = card
	local title = Instance.new("TextLabel")
	title.Size = UDim2.new(1, -20, 0, 24)
	title.Position = UDim2.new(0, 12, 0, 8)
	title.BackgroundTransparency = 1
	title.TextColor3 = Color3.fromRGB(255, 255, 255)
	title.Font = Enum.Font.GothamBold
	title.TextSize = 14
	title.Text = config.text or "Slider"
	title.TextXAlignment = Enum.TextXAlignment.Left
	title.Parent = card
	local valueLabel = Instance.new("TextLabel")
	valueLabel.Size = UDim2.new(0, 50, 0, 24)
	valueLabel.Position = UDim2.new(1, -62, 0, 8)
	valueLabel.BackgroundTransparency = 1
	valueLabel.TextColor3 = Color3.fromRGB(100, 200, 255)
	valueLabel.Font = Enum.Font.GothamBold
	valueLabel.TextSize = 14
	valueLabel.Text = tostring(config.default or 0)
	valueLabel.TextXAlignment = Enum.TextXAlignment.Right
	valueLabel.Parent = card
	local sliderBar = Instance.new("Frame")
	sliderBar.Size = UDim2.new(0, 220, 0, 4)
	sliderBar.Position = UDim2.new(0, 20, 0, 48)
	sliderBar.BackgroundColor3 = Color3.fromRGB(60, 60, 80)
	sliderBar.BorderSizePixel = 0
	sliderBar.Parent = card
	local barCorner = Instance.new("UICorner")
	barCorner.CornerRadius = UDim.new(0, 2)
	barCorner.Parent = sliderBar
	local sliderFill = Instance.new("Frame")
	local defaultPercent = (config.default or 0) / (config.max or 100)
	sliderFill.Size = UDim2.new(defaultPercent, 0, 1, 0)
	sliderFill.BackgroundColor3 = Color3.fromRGB(100, 200, 255)
	sliderFill.BorderSizePixel = 0
	sliderFill.Parent = sliderBar
	local fillCorner = Instance.new("UICorner")
	fillCorner.CornerRadius = UDim.new(0, 2)
	fillCorner.Parent = sliderFill
	local sliderHandle = Instance.new("TextButton")
	sliderHandle.Size = UDim2.new(0, 14, 0, 14)
	sliderHandle.Position = UDim2.new(0, defaultPercent * 220 - 7, 0, -5)
	sliderHandle.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
	sliderHandle.Text = ""
	sliderHandle.AutoButtonColor = false
	sliderHandle.BorderSizePixel = 0
	sliderHandle.Parent = sliderBar
	local handleCorner = Instance.new("UICorner")
	handleCorner.CornerRadius = UDim.new(0, 7)
	handleCorner.Parent = sliderHandle
	local min = config.min or 0
	local max = config.max or 100
	local slider = Slider.new(sliderBar, sliderHandle, function(normalized)
		local value = min + normalized * (max - min)
		valueLabel.Text = tostring(math.floor(value + 0.5))
		sliderFill.Size = UDim2.new(normalized, 0, 1, 0)
		if config.callback then config.callback(value) end
	end)
	table.insert(self.components, slider)
	return slider
end
function DummyUI:AddLabel(parent, text)
	local label = Instance.new("TextLabel")
	label.Size = UDim2.new(0, 260, 0, 40)
	label.BackgroundColor3 = Color3.fromRGB(35, 35, 45)
	label.BackgroundTransparency = 0.3
	label.TextColor3 = Color3.fromRGB(200, 200, 220)
	label.Font = Enum.Font.Gotham
	label.TextSize = 12
	label.Text = text
	label.TextWrapped = true
	label.Parent = parent
	local labelCorner = Instance.new("UICorner")
	labelCorner.CornerRadius = UDim.new(0, 10)
	labelCorner.Parent = label
	return label
end
function DummyUI:AddKeybind(parent, config)
	local card = Instance.new("Frame")
	card.Size = UDim2.new(0, 260, 0, 90)
	card.BackgroundColor3 = Color3.fromRGB(35, 35, 45)
	card.BackgroundTransparency = 0.3
	card.BorderSizePixel = 0
	card.Parent = parent
	local cardCorner = Instance.new("UICorner")
	cardCorner.CornerRadius = UDim.new(0, 10)
	cardCorner.Parent = card
	local title = Instance.new("TextLabel")
	title.Size = UDim2.new(1, -20, 0, 24)
	title.Position = UDim2.new(0, 12, 0, 8)
	title.BackgroundTransparency = 1
	title.TextColor3 = Color3.fromRGB(255, 255, 255)
	title.Font = Enum.Font.GothamBold
	title.TextSize = 14
	title.Text = config.text or "Keybind"
	title.TextXAlignment = Enum.TextXAlignment.Left
	title.Parent = card
	local desc = Instance.new("TextLabel")
	desc.Size = UDim2.new(1, -24, 0, 20)
	desc.Position = UDim2.new(0, 12, 0, 32)
	desc.BackgroundTransparency = 1
	desc.TextColor3 = Color3.fromRGB(150, 150, 170)
	desc.Font = Enum.Font.Gotham
	desc.TextSize = 11
	desc.Text = "Press to record new key combination (max 3 keys)"
	desc.TextXAlignment = Enum.TextXAlignment.Left
	desc.Parent = card
	local keybindButton = Instance.new("TextButton")
	keybindButton.Size = UDim2.new(0, 200, 0, 36)
	keybindButton.Position = UDim2.new(0, 30, 0, 54)
	keybindButton.BackgroundColor3 = Color3.fromRGB(45, 45, 55)
	keybindButton.TextColor3 = Color3.fromRGB(255, 255, 255)
	keybindButton.Font = Enum.Font.GothamBold
	keybindButton.TextSize = 13
	keybindButton.Text = table.concat(config.default or {"Shift", "P"}, "+")
	keybindButton.AutoButtonColor = false
	keybindButton.BorderSizePixel = 1
	keybindButton.BorderColor3 = Color3.fromRGB(80, 80, 100)
	keybindButton.Parent = card
	local keybindCorner = Instance.new("UICorner")
	keybindCorner.CornerRadius = UDim.new(0, 18)
	keybindCorner.Parent = keybindButton
	local selectedKeys = {}
	local isListening = false
	local activeConnection = nil
	local function getKeyName(keyCode)
		local name = keyCode.Name
		if name == "LeftControl" or name == "RightControl" then return "Ctrl"
		elseif name == "LeftAlt" or name == "RightAlt" then return "Alt"
		elseif name == "LeftShift" or name == "RightShift" then return "Shift"
		else return name end
	end
	local function updateDisplay()
		local names = {}
		for _, k in ipairs(selectedKeys) do table.insert(names, getKeyName(k)) end
		keybindButton.Text = (#names > 0 and table.concat(names, "+")) or "..."
		if config.onChange then config.onChange(selectedKeys) end
	end
	local function startListening()
		isListening = true
		selectedKeys = {}
		keybindButton.Text = "Press keys..."
		keybindButton.BackgroundColor3 = Color3.fromRGB(70, 70, 90)
	end
	local function stopListening()
		isListening = false
		keybindButton.BackgroundColor3 = Color3.fromRGB(45, 45, 55)
		updateDisplay()
	end
	if config.default then
		for _, keyName in ipairs(config.default) do
			for _, ek in pairs(Enum.KeyCode:GetEnumItems()) do
				if ek.Name == keyName then
					table.insert(selectedKeys, ek)
					break
				end
			end
		end
		updateDisplay()
	end
	keybindButton.MouseButton1Click:Connect(function()
		if isListening then
			stopListening()
			if activeConnection then
				activeConnection:Disconnect()
				activeConnection = nil
			end
		else
			startListening()
			if activeConnection then activeConnection:Disconnect() end
			activeConnection = UserInputService.InputBegan:Connect(function(input, gameProcessed)
				if gameProcessed then return end
				if isListening and input.KeyCode ~= Enum.KeyCode.Unknown then
					if #selectedKeys < 3 and not table.find(selectedKeys, input.KeyCode) then
						table.insert(selectedKeys, input.KeyCode)
						updateDisplay()
					end
					task.wait(0.2)
					if isListening and #selectedKeys > 0 then
						stopListening()
						if activeConnection then
							activeConnection:Disconnect()
							activeConnection = nil
						end
					end
				end
			end)
		end
	end)
	table.insert(self.components, {Destroy = function() if activeConnection then activeConnection:Disconnect() end end})
	return {GetKeys = function() return selectedKeys end}
end
function DummyUI:Destroy()
	if self.draggable then self.draggable:Destroy() end
	for _, component in ipairs(self.components) do
		if component.Destroy then component:Destroy() end
	end
	if self.mainFrame then self.mainFrame:Destroy() end
end
local ui = DummyUI.new("Freecam Control", UDim2.new(0, 300, 0, 380))
uiMainFrame = ui.mainFrame
floatingButton = Instance.new("TextButton")
floatingButton.Name = "OpenFCUI"
floatingButton.Size = UDim2.new(0, 120, 0, 40)
floatingButton.Position = UDim2.new(0.5, -235, 0.9, -372)
floatingButton.Text = "[OpenFC-UI]"
floatingButton.TextColor3 = Color3.fromRGB(255, 255, 255)
floatingButton.TextSize = 14
floatingButton.Draggable = true
floatingButton.Font = Enum.Font.GothamBold
floatingButton.BackgroundColor3 = Color3.fromRGB(100, 200, 255)
floatingButton.BackgroundTransparency = 0.3
floatingButton.BorderSizePixel = 0
floatingButton.Parent = freecamGui
floatingButton.ZIndex = 100
local floatCorner = Instance.new("UICorner")
floatCorner.CornerRadius = UDim.new(0, 20)
floatCorner.Parent = floatingButton
floatingButton.Visible = false
floatingButton.MouseButton1Click:Connect(function()
	if uiMainFrame then uiMainFrame.Visible = true end
	floatingButton.Visible = false
end)
local function updateFloatingButtonPosition()
	local screenSize = freecamGui.AbsoluteSize
	floatingButton.Position = UDim2.new(0.5, -60, 0.9, 0)
end
freecamGui:GetPropertyChangedSignal("AbsoluteSize"):Connect(updateFloatingButtonPosition)
updateFloatingButtonPosition()
local homeTab = ui:AddTab("Home")
local freecamToggle = ui:AddToggle(homeTab, { text = "Freecam Enabled", callback = function(state) if state then StartFreecam() else StopFreecam() end end, default = false })
ui:AddSlider(homeTab, { text = "Movement Speed", min = 0.25, max = 4, default = 1, callback = function(val) mobileNavSpeed = val end })
ui:AddSlider(homeTab, { text = "Mouse Sensitivity", min = 0.2, max = 3, default = 1, callback = function(val) mouseSensitivity = val end })
ui:AddSlider(homeTab, { text = "Touch Look Sensitivity", min = 0.2, max = 3, default = 1, callback = function(val) touchLookSensitivity = val end })
ui:AddSlider(homeTab, { text = "FOV Sensitivity", min = 0.2, max = 3, default = 1, callback = function(val) fovSensitivity = val end })
ui:AddSlider(homeTab, { text = "Movement Smoothing", min = 0.1, max = 5, default = 1.5, callback = function(val) VEL_STIFFNESS = val; velSpring:SetFreq(val) end })
ui:AddSlider(homeTab, { text = "Look Smoothing", min = 0.1, max = 5, default = 1, callback = function(val) PAN_STIFFNESS = val; panSpring:SetFreq(val) end })
ui:AddSlider(homeTab, { text = "FOV Smoothing", min = 0.1, max = 5, default = 4, callback = function(val) FOV_STIFFNESS = val; fovSpring:SetFreq(val) end })
ui:AddSlider(homeTab, { text = "Roll Smoothing", min = 0.1, max = 5, default = 1, callback = function(val) ROLL_STIFFNESS = val; rollSpring:SetFreq(val) end })
ui:AddSlider(homeTab, { text = "Tilt Speed", min = 0.2, max = 3, default = 1, callback = function(val) rollSpeed = val end })
ui:AddLabel(homeTab, "Controls:\nWASD / Thumbstick - Move\nMouse/Right Touch - Look\nQ/E - Up/Down\nZ/C - Tilt (Keyboard)\nTilt Buttons (Bottom Left)\nPlayer Lock Bar (Bottom Center)\nShift - Slow mode")
local keybindTab = ui:AddTab("Keybind")
local customToggleKeys = {"LeftShift", "P"}
local customPlayerLockKeys = {"Slash"}
local customPlayerLockPrevKeys = {"R"}
local customPlayerLockNextKeys = {"T"}
local customDoFKeys = {"BackSlash"}
local customHideGuiKeys = {"G"}
local customHidePlayerGuiKeys = {"X"}
local customLeaderboardKeys = {"L"}
local customTiltResetKeys = {"Z", "C"}
local customTiltLeftKeys = {"Z"}
local customTiltRightKeys = {"C"}
local function rebuildToggleFreecamKeybind()
	ContextActionService:UnbindAction("FreecamToggle")
	if #customToggleKeys >= 1 then
		local macroKeys = {}
		for i, keyName in ipairs(customToggleKeys) do
			for _, ek in pairs(Enum.KeyCode:GetEnumItems()) do
				if ek.Name == keyName then macroKeys[i] = ek; break end
			end
		end
		if #macroKeys > 0 then
			local lastKey = macroKeys[#macroKeys]
			ContextActionService:BindActionAtPriority("FreecamToggle", function(action, state, input)
				if state == Enum.UserInputState.Begin and input.KeyCode == lastKey then
					local allDown = true
					for i = 1, #macroKeys - 1 do
						if not UserInputService:IsKeyDown(macroKeys[i]) then allDown = false; break end
					end
					if allDown then ToggleFreecam() end
				end
				return Enum.ContextActionResult.Pass
			end, false, TOGGLE_INPUT_PRIORITY, lastKey)
		end
	end
end
local function rebuildPlayerLockKeybind()
	ContextActionService:UnbindAction("FreecamKeyboardPlayerLockToggle")
	if #customPlayerLockKeys >= 1 then
		local macroKeys = {}
		for i, keyName in ipairs(customPlayerLockKeys) do
			for _, ek in pairs(Enum.KeyCode:GetEnumItems()) do
				if ek.Name == keyName then macroKeys[i] = ek; break end
			end
		end
		if #macroKeys > 0 then
			local lastKey = macroKeys[#macroKeys]
			ContextActionService:BindActionAtPriority("FreecamKeyboardPlayerLockToggle", function(action, state, input)
				if state == Enum.UserInputState.Begin and input.KeyCode == lastKey then
					local allDown = true
					for i = 1, #macroKeys - 1 do
						if not UserInputService:IsKeyDown(macroKeys[i]) then allDown = false; break end
					end
					if allDown then
						playerLockEnabled = not playerLockEnabled
						if playerLockEnabled then
							playerLockZoom = PLAYER_LOCK_DEFAULT_ZOOM
							playerLockHeight = PLAYER_LOCK_DEFAULT_HEIGHT
							rootPart = findPlayerLockRootPart(0)
						end
					end
				end
				return Enum.ContextActionResult.Pass
			end, false, INPUT_PRIORITY, lastKey)
		end
	end
end
local function rebuildPlayerLockPrevKeybind()
	ContextActionService:UnbindAction("FreecamKeyboardPlayerLockPrev")
	if #customPlayerLockPrevKeys >= 1 then
		local macroKeys = {}
		for i, keyName in ipairs(customPlayerLockPrevKeys) do
			for _, ek in pairs(Enum.KeyCode:GetEnumItems()) do
				if ek.Name == keyName then macroKeys[i] = ek; break end
			end
		end
		if #macroKeys > 0 then
			local lastKey = macroKeys[#macroKeys]
			ContextActionService:BindActionAtPriority("FreecamKeyboardPlayerLockPrev", function(action, state, input)
				if state == Enum.UserInputState.Begin and input.KeyCode == lastKey then
					local allDown = true
					for i = 1, #macroKeys - 1 do
						if not UserInputService:IsKeyDown(macroKeys[i]) then allDown = false; break end
					end
					if allDown and playerLockEnabled and #playerList > 0 then
						rootPart = findPlayerLockRootPart(-1)
					end
				end
				return Enum.ContextActionResult.Pass
			end, false, INPUT_PRIORITY, lastKey)
		end
	end
end
local function rebuildPlayerLockNextKeybind()
	ContextActionService:UnbindAction("FreecamKeyboardPlayerLockNext")
	if #customPlayerLockNextKeys >= 1 then
		local macroKeys = {}
		for i, keyName in ipairs(customPlayerLockNextKeys) do
			for _, ek in pairs(Enum.KeyCode:GetEnumItems()) do
				if ek.Name == keyName then macroKeys[i] = ek; break end
			end
		end
		if #macroKeys > 0 then
			local lastKey = macroKeys[#macroKeys]
			ContextActionService:BindActionAtPriority("FreecamKeyboardPlayerLockNext", function(action, state, input)
				if state == Enum.UserInputState.Begin and input.KeyCode == lastKey then
					local allDown = true
					for i = 1, #macroKeys - 1 do
						if not UserInputService:IsKeyDown(macroKeys[i]) then allDown = false; break end
					end
					if allDown and playerLockEnabled and #playerList > 0 then
						rootPart = findPlayerLockRootPart(1)
					end
				end
				return Enum.ContextActionResult.Pass
			end, false, INPUT_PRIORITY, lastKey)
		end
	end
end
local function rebuildDoFKeybind()
	ContextActionService:UnbindAction("FreecamKeyboardDoFToggle")
	if #customDoFKeys >= 1 then
		local macroKeys = {}
		for i, keyName in ipairs(customDoFKeys) do
			for _, ek in pairs(Enum.KeyCode:GetEnumItems()) do
				if ek.Name == keyName then macroKeys[i] = ek; break end
			end
		end
		if #macroKeys > 0 then
			local lastKey = macroKeys[#macroKeys]
			ContextActionService:BindActionAtPriority("FreecamKeyboardDoFToggle", function(action, state, input)
				if state == Enum.UserInputState.Begin and input.KeyCode == lastKey then
					local allDown = true
					for i = 1, #macroKeys - 1 do
						if not UserInputService:IsKeyDown(macroKeys[i]) then allDown = false; break end
					end
					if allDown and FreecamDepthOfField then
						FreecamDepthOfField.Enabled = not FreecamDepthOfField.Enabled
					end
				end
				return Enum.ContextActionResult.Pass
			end, false, INPUT_PRIORITY, lastKey)
		end
	end
end
local function rebuildHideGuiKeybind()
	ContextActionService:UnbindAction("FreecamKeyboardCustomGuiToggle")
	if #customHideGuiKeys >= 1 then
		local macroKeys = {}
		for i, keyName in ipairs(customHideGuiKeys) do
			for _, ek in pairs(Enum.KeyCode:GetEnumItems()) do
				if ek.Name == keyName then macroKeys[i] = ek; break end
			end
		end
		if #macroKeys > 0 then
			local lastKey = macroKeys[#macroKeys]
			ContextActionService:BindActionAtPriority("FreecamKeyboardCustomGuiToggle", function(action, state, input)
				if state == Enum.UserInputState.Begin and input.KeyCode == lastKey then
					local allDown = true
					for i = 1, #macroKeys - 1 do
						if not UserInputService:IsKeyDown(macroKeys[i]) then allDown = false; break end
					end
					if allDown and freecamGui and freecamGui.Parent then
						freecamGui.Enabled = not freecamGui.Enabled
					end
				end
				return Enum.ContextActionResult.Pass
			end, false, INPUT_PRIORITY, lastKey)
		end
	end
end
local function rebuildHidePlayerGuiKeybind()
	ContextActionService:UnbindAction("FreecamKeyboardPlayerGuiToggle")
	if #customHidePlayerGuiKeys >= 1 then
		local macroKeys = {}
		for i, keyName in ipairs(customHidePlayerGuiKeys) do
			for _, ek in pairs(Enum.KeyCode:GetEnumItems()) do
				if ek.Name == keyName then macroKeys[i] = ek; break end
			end
		end
		if #macroKeys > 0 then
			local lastKey = macroKeys[#macroKeys]
			ContextActionService:BindActionAtPriority("FreecamKeyboardPlayerGuiToggle", function(action, state, input)
				if state == Enum.UserInputState.Begin and input.KeyCode == lastKey then
					local allDown = true
					for i = 1, #macroKeys - 1 do
						if not UserInputService:IsKeyDown(macroKeys[i]) then allDown = false; break end
					end
					if allDown then
						screenGuisEnabled = not screenGuisEnabled
						local screenGuis = PlayerState.getScreenGuis()
						for _, gui in pairs(screenGuis) do
							if gui.Parent and gui ~= freecamGui then
								gui.Enabled = screenGuisEnabled
							end
						end
					end
				end
				return Enum.ContextActionResult.Pass
			end, false, INPUT_PRIORITY, lastKey)
		end
	end
end
local function rebuildLeaderboardKeybind()
	ContextActionService:UnbindAction("FreecamKeyboardLeaderboardToggle")
	if #customLeaderboardKeys >= 1 then
		local macroKeys = {}
		for i, keyName in ipairs(customLeaderboardKeys) do
			for _, ek in pairs(Enum.KeyCode:GetEnumItems()) do
				if ek.Name == keyName then macroKeys[i] = ek; break end
			end
		end
		if #macroKeys > 0 then
			local lastKey = macroKeys[#macroKeys]
			ContextActionService:BindActionAtPriority("FreecamKeyboardLeaderboardToggle", function(action, state, input)
				if state == Enum.UserInputState.Begin and input.KeyCode == lastKey then
					local allDown = true
					for i = 1, #macroKeys - 1 do
						if not UserInputService:IsKeyDown(macroKeys[i]) then allDown = false; break end
					end
					if allDown then
						leaderboardEnabled = not leaderboardEnabled
						StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType.PlayerList, leaderboardEnabled)
					end
				end
				return Enum.ContextActionResult.Pass
			end, false, INPUT_PRIORITY, lastKey)
		end
	end
end
local function rebuildTiltResetKeybind()
	ContextActionService:UnbindAction("FreecamKeyboardTiltReset")
	if #customTiltResetKeys >= 1 then
		local macroKeys = {}
		for i, keyName in ipairs(customTiltResetKeys) do
			for _, ek in pairs(Enum.KeyCode:GetEnumItems()) do
				if ek.Name == keyName then macroKeys[i] = ek; break end
			end
		end
		if #macroKeys > 0 then
			local lastKey = macroKeys[#macroKeys]
			ContextActionService:BindActionAtPriority("FreecamKeyboardTiltReset", function(action, state, input)
				if state == Enum.UserInputState.Begin and input.KeyCode == lastKey then
					local allDown = true
					for i = 1, #macroKeys - 1 do
						if not UserInputService:IsKeyDown(macroKeys[i]) then allDown = false; break end
					end
					if allDown then
						cameraRot = Vector3.new(cameraRot.x, cameraRot.y, 0)
						rollSpring:Reset(0)
					end
				end
				return Enum.ContextActionResult.Pass
			end, false, INPUT_PRIORITY, lastKey)
		end
	end
end
ui:AddKeybind(keybindTab, { text = "Toggle Freecam", default = customToggleKeys, onChange = function(keys)
	customToggleKeys = {}; for _, k in ipairs(keys) do table.insert(customToggleKeys, k.Name) end
	if #customToggleKeys == 0 then customToggleKeys = {"LeftShift", "P"} end
	rebuildToggleFreecamKeybind()
end })
ui:AddKeybind(keybindTab, { text = "Tilt Left", default = customTiltLeftKeys, onChange = function(keys)
	customTiltLeftKeys = {}; for _, k in ipairs(keys) do table.insert(customTiltLeftKeys, k.Name) end
	if #customTiltLeftKeys == 0 then customTiltLeftKeys = {"Z"} end
end })
ui:AddKeybind(keybindTab, { text = "Tilt Right", default = customTiltRightKeys, onChange = function(keys)
	customTiltRightKeys = {}; for _, k in ipairs(keys) do table.insert(customTiltRightKeys, k.Name) end
	if #customTiltRightKeys == 0 then customTiltRightKeys = {"C"} end
end })
ui:AddKeybind(keybindTab, { text = "Player Lock Toggle", default = customPlayerLockKeys, onChange = function(keys)
	customPlayerLockKeys = {}; for _, k in ipairs(keys) do table.insert(customPlayerLockKeys, k.Name) end
	if #customPlayerLockKeys == 0 then customPlayerLockKeys = {"Slash"} end
	rebuildPlayerLockKeybind()
end })
ui:AddKeybind(keybindTab, { text = "Player Lock Previous", default = customPlayerLockPrevKeys, onChange = function(keys)
	customPlayerLockPrevKeys = {}; for _, k in ipairs(keys) do table.insert(customPlayerLockPrevKeys, k.Name) end
	if #customPlayerLockPrevKeys == 0 then customPlayerLockPrevKeys = {"R"} end
	rebuildPlayerLockPrevKeybind()
end })
ui:AddKeybind(keybindTab, { text = "Player Lock Next", default = customPlayerLockNextKeys, onChange = function(keys)
	customPlayerLockNextKeys = {}; for _, k in ipairs(keys) do table.insert(customPlayerLockNextKeys, k.Name) end
	if #customPlayerLockNextKeys == 0 then customPlayerLockNextKeys = {"T"} end
	rebuildPlayerLockNextKeybind()
end })
ui:AddKeybind(keybindTab, { text = "Depth of Field Toggle", default = customDoFKeys, onChange = function(keys)
	customDoFKeys = {}; for _, k in ipairs(keys) do table.insert(customDoFKeys, k.Name) end
	if #customDoFKeys == 0 then customDoFKeys = {"BackSlash"} end
	rebuildDoFKeybind()
end })
ui:AddKeybind(keybindTab, { text = "Hide Freecam UI", default = customHideGuiKeys, onChange = function(keys)
	customHideGuiKeys = {}; for _, k in ipairs(keys) do table.insert(customHideGuiKeys, k.Name) end
	if #customHideGuiKeys == 0 then customHideGuiKeys = {"G"} end
	rebuildHideGuiKeybind()
end })
ui:AddKeybind(keybindTab, { text = "Hide Player GUI", default = customHidePlayerGuiKeys, onChange = function(keys)
	customHidePlayerGuiKeys = {}; for _, k in ipairs(keys) do table.insert(customHidePlayerGuiKeys, k.Name) end
	if #customHidePlayerGuiKeys == 0 then customHidePlayerGuiKeys = {"X"} end
	rebuildHidePlayerGuiKeybind()
end })
ui:AddKeybind(keybindTab, { text = "Toggle Leaderboard", default = customLeaderboardKeys, onChange = function(keys)
	customLeaderboardKeys = {}; for _, k in ipairs(keys) do table.insert(customLeaderboardKeys, k.Name) end
	if #customLeaderboardKeys == 0 then customLeaderboardKeys = {"L"} end
	rebuildLeaderboardKeybind()
end })
ui:AddKeybind(keybindTab, { text = "Tilt Reset", default = customTiltResetKeys, onChange = function(keys)
	customTiltResetKeys = {}; for _, k in ipairs(keys) do table.insert(customTiltResetKeys, k.Name) end
	if #customTiltResetKeys == 0 then customTiltResetKeys = {"Z", "C"} end
	rebuildTiltResetKeybind()
end })
ui:AddLabel(keybindTab, "Click any keybind button to record\na new key combination (max 3 keys)")
rebuildToggleFreecamKeybind()
rebuildPlayerLockKeybind()
rebuildPlayerLockPrevKeybind()
rebuildPlayerLockNextKeybind()
rebuildDoFKeybind()
rebuildHideGuiKeybind()
rebuildHidePlayerGuiKeybind()
rebuildLeaderboardKeybind()
rebuildTiltResetKeybind()
function StartFreecam()
	if FFlagUserFreecamPlayerLock then initializePlayerList() end
	if not FFlagUserFreecamGuiDestabilization then
		if FFlagUserShowGuiHideToggles then script:SetAttribute(FREECAM_ENABLED_ATTRIBUTE_NAME, true) end
	end
	local cameraCFrame = Camera.CFrame
	if FFlagUserFreecamTiltControl then
		cameraRot = Vector3.new(cameraCFrame:toEulerAnglesYXZ())
	else
		cameraRot = Vector2.new(cameraCFrame:toEulerAnglesYXZ())
	end
	cameraPos = cameraCFrame.p
	cameraFov = Camera.FieldOfView
	velSpring:Reset(Vector3.new())
	panSpring:Reset(Vector2.new())
	fovSpring:Reset(0)
	if FFlagUserFreecamTiltControl then rollSpring:Reset(0) end
	if FFlagUserFreecamCustomGui then
		if not touchGui then
			touchGui = CreateTouchControls()
		end
	end
	PlayerState.Push()
	if FFlagUserFreecamDepthOfFieldEffect then
		if not FreecamDepthOfField or not FreecamDepthOfField.Parent then
			FreecamDepthOfField = Instance.new("DepthOfFieldEffect")
			FreecamDepthOfField.Enabled = false
			FreecamDepthOfField.Name = "FreecamDepthOfField"
			FreecamDepthOfField.Parent = Camera
		end
	end
	RunService:BindToRenderStep("Freecam", Enum.RenderPriority.Camera.Value, StepFreecam)
	Input.StartCapture()
end
function DestroyAllUI()
	if touchGui then touchGui:Destroy() touchGui = nil end
	if customGui then customGui:Destroy() customGui = nil end
end
function StopFreecam()
	if not FFlagUserFreecamGuiDestabilization then
		if FFlagUserShowGuiHideToggles then script:SetAttribute(FREECAM_ENABLED_ATTRIBUTE_NAME, false) end
	end
	if FFlagUserFreecamPlayerLock then
		if playerAddedConnection then playerAddedConnection:Disconnect() playerAddedConnection = nil end
		if playerRemovingConnection then playerRemovingConnection:Disconnect() playerRemovingConnection = nil end
		playerLockEnabled = false
		currentTargetIndex = 1
		playerList = {}
	end
	if FFlagUserFreecamDepthOfFieldEffect then
		if FreecamDepthOfField and FreecamDepthOfField.Parent then
			if FreecamDepthOfField.Enabled then
				for _, effect in ipairs(postEffects) do if effect.Parent then effect.Enabled = true end end
				if cameraConnection then cameraConnection:Disconnect() cameraConnection = nil end
				if lightingConnection then lightingConnection:Disconnect() lightingConnection = nil end
				postEffects = {}
			end
			FreecamDepthOfField.Enabled = false
		end
	end
	if FFlagUserFreecamCustomGui then
		DestroyAllUI()
		screenGuisEnabled = false
		leaderboardEnabled = false
	end
	Input.StopCapture()
	pcall(function() RunService:UnbindFromRenderStep("Freecam") end)
	PlayerState.Pop()
end
do
	local enabled = false
	function ToggleFreecam()
		if enabled then StopFreecam() else StartFreecam() end
		enabled = not enabled
		if FFlagUserFreecamGuiDestabilization then script:SetAttribute(FREECAM_ENABLED_ATTRIBUTE_NAME, enabled) end
		if freecamToggle and freecamToggle.SetState then
			freecamToggle:SetState(enabled)
		end
	end
	if FFlagUserFreecamGuiDestabilization or FFlagUserShowGuiHideToggles then
		script:SetAttribute(FREECAM_ENABLED_ATTRIBUTE_NAME, enabled)
		script:GetAttributeChangedSignal(FREECAM_ENABLED_ATTRIBUTE_NAME):Connect(function()
			local attributeValue = script:GetAttribute(FREECAM_ENABLED_ATTRIBUTE_NAME)
			if typeof(attributeValue) ~= "boolean" then
				script:SetAttribute(FREECAM_ENABLED_ATTRIBUTE_NAME, enabled)
				return
			end
			if attributeValue ~= enabled then
				if attributeValue then StartFreecam() enabled = true else StopFreecam() enabled = false end
				if freecamToggle and freecamToggle.SetState then
					freecamToggle:SetState(enabled)
				end
			end
		end)
	end
end
return {}

Features & Information

Feeecam ui made by official roblox and remaster by pnsdg how to use: Camera Positioning: WASD / UHJK / Thumbstick1 to move camera forward, left, backward, right MouseButton2 / Thumbstick2 to rotate camera (full 360 degree rotation in all axes) Q,E / Y,I / L2,R2 to to move camera up, down Z,C / L1,R1 to tilt camera left, right (double-tap Z,C,L1,R1 to reset to 0 degree tilt) Down,Up / DpadDown,DpadUp to adjust movement speed Left,Right / DpadLeft,DpadRight to adjust zoom speed , and . to adjust tilt speed DO NOT USE Keybind settngs it's really buggy MOBILE CONTROLS: Left thumbstick (bottom-left) - Move camera forward/back/strafe Right side touch & drag - Look around (rotate) - Works simultaneously with joystick Pinch to zoom on right side - Zoom in/out (pinch out = zoom in, pinch in = zoom out) 🔽 button - Fly down (LEFT side) 🔼 button - Fly up (RIGHT side) Arrow circle is for tilt camera left, right

Community Comments (2)

styearstyearApr 24, 2026

This is cool but this is not GPT right?

Replying to @styear
ItzPnsdgItzPnsdgApr 25, 2026

@Styear it's roblox studio source code i have to rework

More Scripts for Universal Scripts