Dragon rolls left and right only when descending

I have a physics based dragon flight that behaves correctly except when it descends. While diving or slowing the dragon performs a fast left and right roll that looks like a flip flop. updatePhysics runs on RunService.Heartbeat and updateOrientation runs on RunService.PreRender. Increasing angular damping and lowering responsiveness reduced the amplitude but did not remove the oscillation.

function DragonFlightModule:updateOrientation(camera: Camera)
	if not self.attachment or self.state ~= 3 then return end

	local camCF = camera.CFrame
	local lookDir = camCF.LookVector

	local angularVel = self.rootPart.AssemblyAngularVelocity
	local turnRate = angularVel:Dot(Vector3.yAxis)

	local bankAngle = math.clamp(turnRate * BANKING_FACTOR, -math.rad(45), math.rad(45))

	local upVec = camCF.UpVector
	local targetCF = CFrame.lookAt(Vector3.zero, lookDir, upVec) * CFrame.Angles(0, 0, bankAngle)

	self.alignOrientation.CFrame = targetCF
	self.orientationCFrame = targetCF

	local speed = self.rootPart.AssemblyLinearVelocity.Magnitude
	local speedFactor = math.clamp(speed / self.config.FLY_SPEED, 0.5, 1.5)
	self.alignOrientation.Responsiveness = ORIENTATION_RESPONSIVENESS * speedFactor
end
function DragonFlightModule:updatePhysics(deltaTime: number)
	if self.state ~= 3 or not self.vectorForce then return end
	deltaTime = math.clamp(deltaTime or 0.016, 0.001, 0.1)

	local physics = self.config.PHYSICS
	local vel = self.rootPart.AssemblyLinearVelocity
	local speed = vel.Magnitude
	local lookDir = self.rootPart.CFrame.LookVector

	local baseSpeed = self.config.FLY_SPEED

	if self.sprintTakeoffMode then
		self.sprintTakeoffTimer = self.sprintTakeoffTimer + deltaTime
		local progress = math.min(self.sprintTakeoffTimer / (physics.SPRINT_TAKEOFF_TIME or 1), 1)
		baseSpeed = self.config.FLY_SPEED + (self.config.FAST_FLY_SPEED - self.config.FLY_SPEED) * progress
	elseif self.sprinting then
		baseSpeed = self.config.FAST_FLY_SPEED
	elseif self.gliding then
		baseSpeed = self.config.GLIDE_SPEED
	elseif self.slowing then
		if physics.CAN_HOVER then
			baseSpeed = physics.HOVER_SPEED or 10
		else
			baseSpeed = math.max(self.config.MIN_FLY_SPEED * 0.25, 5)
		end
	end

	local pitchFactor = lookDir.Y
	local speedMultiplier = 1.0

	if pitchFactor < -0.3 then
		speedMultiplier = 1 + math.abs(pitchFactor)
	elseif pitchFactor > 0.3 then
		speedMultiplier = 1 - pitchFactor * 0.2
	end

	local targetSpeed = math.clamp(
		baseSpeed * speedMultiplier,
		self.config.MIN_FLY_SPEED,
		self.config.MAX_DIVE_SPEED
	)

	if self.slowing and physics.CAN_HOVER then
		-- hover: keep near-zero velocity (slight momentum preservation)
		self.targetVelocity = self.velocitySmooth * (physics.MOMENTUM_PRESERVATION or 0.75) * 0.1
		self.targetVelocity = Vector3.new(self.targetVelocity.X, 0, self.targetVelocity.Z)
	else
		self.targetVelocity = lookDir * targetSpeed
	end

	if pitchFactor > 0.1 then
		local verticalBoost = Vector3.yAxis * pitchFactor * targetSpeed * VERTICAL_THRUST_MULTIPLIER * 0.3
		self.targetVelocity = self.targetVelocity + verticalBoost
	end

	if self.slowing and not physics.CAN_HOVER then
		self.targetVelocity = (lookDir * targetSpeed) + Vector3.new(0, - (physics.HOVER_SPEED or 10) * 0.5, 0)
	end

	self.velocitySmooth = self.velocitySmooth:Lerp(
		self.targetVelocity,
		math.clamp(VELOCITY_SMOOTHING * deltaTime, 0, 1)
	)

	local accelRate = physics.ACCELERATION
	if self.sprintTakeoffMode or self.sprinting then
		accelRate = physics.FAST_ACCELERATION
	elseif self.slowing then
		if speed > physics.QUICK_STOP_THRESHOLD then
			accelRate = physics.EMERGENCY_STOP_DECELERATION
		else
			accelRate = physics.QUICK_STOP_DECELERATION
		end
	end

	local velError = self.velocitySmooth - vel
	local desiredAccel = velError * accelRate * 10

	local maxAccel = physics.MAX_ACCEL
	if desiredAccel.Magnitude > maxAccel then
		desiredAccel = desiredAccel.Unit * maxAccel
	end

	local accelForce = desiredAccel * self.mass

	local liftEff = physics.LIFT_EFFICIENCY
	local speedRatio = math.clamp(speed / baseSpeed, 0, 1.5)
	local pitchLiftFactor = 1 + math.max(0, -pitchFactor * 0.3)
	local dynamicLift = self.mass * GRAVITY_FORCE * liftEff * speedRatio * pitchLiftFactor

	local liftDir = Vector3.yAxis
	if speed > 1 then
		local velNorm = vel.Unit
		liftDir = (Vector3.yAxis - velNorm * velNorm:Dot(Vector3.yAxis)).Unit
	end
	local lift = liftDir * dynamicLift

	local dragC = physics.DRAG_COEFFICIENT
	local drag = Vector3.zero
	if speed > 0.1 then
		drag = -vel * (dragC * speed * self.mass)
	end

	local forwardVel = lookDir * vel:Dot(lookDir)
	local lateralVel = vel - forwardVel
	local lateralDamping = -lateralVel * self.mass * physics.TURN_RADIUS_MULTIPLIER * 2

	local angVel = self.rootPart.AssemblyAngularVelocity
	local turnPenalty = 0
	if angVel.Magnitude > 0.5 then
		turnPenalty = angVel.Magnitude * physics.TURN_SPEED_PENALTY
		local turnDrag = -vel * (turnPenalty * self.mass * 0.02)
		drag = drag + turnDrag
	end

	local stability = (self.targetVelocity - vel) * self.mass * 0.5

	local totalForce = accelForce + lift + drag + lateralDamping + stability

	local maxForceMag = self.mass * physics.MAX_FORCE_MAG
	if totalForce.Magnitude > maxForceMag then
		totalForce = totalForce.Unit * maxForceMag
	end

	if totalForce ~= totalForce or totalForce.Magnitude > 1e8 then
		totalForce = Vector3.new(0, self.mass * GRAVITY_FORCE, 0)
	end

	self.vectorForce.Force = totalForce

	local angDamp = physics.ANGULAR_DAMPING
	local alpha = math.clamp(angDamp * deltaTime, 0, 0.5)
	self.rootPart.AssemblyAngularVelocity = angVel:Lerp(Vector3.zero, alpha)

	local sinceTakeoff = tick() - (self._lastTakeoffTime or 0)
	if sinceTakeoff > (TUNABLES.TAKEOFF_GRACE_TIME or 3) then
		local origin = self.rootPart.Position
		local direction = Vector3.new(0, -1, 0) * (TUNABLES.RAYCAST_DISTANCE or 8)

		local params = RaycastParams.new()
		params.FilterDescendantsInstances = { self.dragon }
		params.FilterType = Enum.RaycastFilterType.Exclude

		local result = Workspace:Raycast(origin, direction, params)
		if result and result.Instance then
			self:stopFlight()
		end
	end

end

First forum post🙃

Still having this issue. Would appreciate any help!