Look Straight Up/Down Without Jitter (Roblox CameraModule)

Summary:
If you raise Roblox’s camera pitch limits to ±90°, the default camera starts jittering when you look straight up or down. This post shows a small, safe change to the PlayerModule that lets the player look almost straight up/down (±~89°) with no jitter, by avoiding an internal singularity in CFrame.lookAt math.


Why this happens

Roblox’s lookAt "face toward vector” math becomes unstable when your look vector gets too close to the ±Y axis (straight up/down). The stock module clamps pitch to ~±80° to avoid that. If you set it to ±90°, the camera can wander into that singular zone → jitter/snap.

Fix: Build the camera’s orientation using Euler angles (CFrame.fromOrientation) and clamp with a tiny epsilon just shy of 90°. This preserves the feeling of “full vertical look” but never crosses the singularity.

Steps (using the latest PlayerModule)

Doing it this way keeps you on the newest Roblox PlayerModule and only changes one function.

  1. Play the game in Studio (Run or Play test).
  2. In Explorer, expand StarterPlayer > StarterPlayerScripts. You’ll see PlayerModule appear (injected at runtime).
  3. Right-click → Copy the PlayerModule.
  4. Stop the play session.
  5. Paste the copied PlayerModule into StarterPlayer > StarterPlayerScripts (so it persists).
  6. Open PlayerModule > CameraModule > BaseCamera.
  7. (Optional but recommended) Set these near the top to express your intended limits:
local MIN_Y = math.rad(-90)
local MAX_Y = math.rad(90)

These are still referenced in a few places; we’ll guard the singularity in the function itself.

  1. Replace the function BaseCamera:CalculateNewLookCFrameFromArg with the version below.

Drop-in Replacement Function

Put this inside BaseCamera (replace the original CalculateNewLookCFrameFromArg):

-- near the top of BaseCamera (add if not present)
local Y_LIMIT   = math.rad(89.0)   -- target “almost straight up/down”
local Y_EPSILON = math.rad(0.25)   -- small guard so we never hit the singularity exactly

function BaseCamera:CalculateNewLookCFrameFromArg(suppliedLookVector: Vector3?, rotateInput: Vector2): CFrame
	-- get current camera orientation (pitch, yaw, roll)
	local cam = workspace.CurrentCamera
	local cx, cy, cz = cam.CFrame:ToOrientation()  -- x=pitch, y=yaw, z=roll

	-- apply input (note the signs match the original code’s conventions)
	local targetPitch = cx - rotateInput.Y
	local targetYaw   = cy - rotateInput.X

	-- soft clamp away from the singularity
	targetPitch = math.clamp(targetPitch, -Y_LIMIT + Y_EPSILON, Y_LIMIT - Y_EPSILON)

	-- rebuild without using lookAt; zero out roll
	return CFrame.fromOrientation(targetPitch, targetYaw, 0)
end

That’s it—no more jitter, and players can look virtually straight up/down.


Before / After

  • Before Attempt [Default Code]: Only lets user look at a ±80° pitch up and down.
  • After Attempt: Jitters/snaps when looking near ±90° pitch up and down.
  • After Fix: Smooth aiming up/down with no jitter; feels like full 90°, technically ~89°.
8 Likes

I found out they updated PlayerModule when I was trying to achieve this on the older version from around 2024 or 2023. I wonder why. They must have fixed a bug or something, because your solution only works on the latest version of PlayerModule.