Funny flashlight

i made a funny flashlight

12 Likes

How does this work? Does the camera move more slowly than the flashlight?

Flashlight moves exactly how the camera normally would. Camera is smoothed. There’s also jittering on the flashlight to make it look like you’re actually holding it, and then there’s camera wobble.

And the lighting is done by abusing fog.

2 Likes

I love funny things, and I love flashlights.

4 Likes

I had my take on something like this i think like 4 years ago, how did you do it, i dropped that project because I couldn’t make it work for all fps’s, even with deltaTime, im curious on if its open source

1 Like

Please put a laugh warning.

I laughed so hard I vomited then ate my pet hamster’s dog.

2 Likes

It’s not, but I could totally make it open source. I don’t know what I’ll do with it, so it’s the least I could do!

I wrote it early while drinking coffee and then came back to it at the end of the day so it’s definitely not the best and I’ll probably have to touch it up a bit. It’s ~150 lines of code that can probably be optimized.

What exactly are you interested in? I wish could explain in great detail, but I’ve kind of already forgotten how I did it! So, I’ll try my best :sweat_smile:

1 Like

Here’s my garbage code that I wrote and immediately forgot how it works. I can try to dissect it and help!

--!strict

local Players          = game:GetService("Players")
local RunService       = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")

local player = Players.LocalPlayer     or error("Couldn't get player")
local camera = workspace.CurrentCamera or error("Couldn't get camera")

local character = player.Character or player.CharacterAdded:Wait()
local humanoidRoot = character:FindFirstChild("HumanoidRootPart")::Part or error("Couldn't get humanoid root part")

-- Settings
local smoothTime                = 0.1
local sensitivity               = 0.2

-- Flashlight control
local flashlightMaxAngle        = 30
local flashlightFollowFactor    = 0.5
local flashlightJitterMagnitude = 0.3
local flashlightJitterInterval  = 0.1
local flashlightJitterLerp      = 0.1

-- Camera wobble control
local cameraWobbleMagnitude     = 1
local cameraWobbleInterval      = 0.15
local cameraWobbleLerp          = 0.05
local idleWobbleMagnitude       = 0.2
local idleWobbleSpeed           = 1.5

-- Rotation state
local targetYaw    = 0
local targetPitch  = 0
local currentYaw   = 0
local currentPitch = 0

-- Flashlight jitter state
local jitterYaw         = 0
local jitterPitch       = 0
local targetJitterYaw   = 0
local targetJitterPitch = 0
local jitterTimer       = 0

-- Camera wobble state
local wobbleYaw         = 0
local wobblePitch       = 0
local targetWobbleYaw   = 0
local targetWobblePitch = 0
local wobbleTimer       = 0
local idleTimer         = 0

-- Update target rotation
UserInputService.InputChanged:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseMovement then
		targetYaw   = targetYaw - input.Delta.X * sensitivity
		targetPitch = math.clamp(targetPitch - input.Delta.Y * sensitivity, -80, 80)
	end
end)

-- Create flashlight
local flashlightPart        = Instance.new("Part")
flashlightPart.Size         = Vector3.new(0.2, 0.2, 0.2)
flashlightPart.Transparency = 1
flashlightPart.Anchored     = true
flashlightPart.CanCollide   = false
flashlightPart.Parent       = workspace

local flashlight      = Instance.new("SpotLight")
flashlight.Enabled    = true
flashlight.Brightness = 1
flashlight.Range      = 40
flashlight.Angle      = 60
flashlight.Shadows    = true
flashlight.Face       = Enum.NormalId.Front
flashlight.Parent     = flashlightPart

RunService.RenderStepped:Connect(function(deltaTime)
	local isMoving = humanoidRoot.AssemblyLinearVelocity.Magnitude > 0.1

	-- Smooth camera rotation
	currentYaw   = currentYaw + (targetYaw - currentYaw) * math.clamp(deltaTime / smoothTime, 0, 1)
	currentPitch = currentPitch + (targetPitch - currentPitch) * math.clamp(deltaTime / smoothTime, 0, 1)

	local cameraPos = camera.CFrame.Position

	-- --- CAMERA WOBBLE ---

	-- Idle sine wobble
	idleTimer = idleTimer + deltaTime * idleWobbleSpeed
	local idleYaw   = math.sin(idleTimer) * idleWobbleMagnitude
	local idlePitch = math.sin(idleTimer * 1.3) * idleWobbleMagnitude

	-- Walking wobble
	if isMoving then
		wobbleTimer = wobbleTimer + deltaTime
		if wobbleTimer >= cameraWobbleInterval then
			wobbleTimer       = 0
			targetWobbleYaw   = (math.random() * 2 - 1) * cameraWobbleMagnitude
			targetWobblePitch = (math.random() * 2 - 1) * cameraWobbleMagnitude
		end
		-- Lerp toward walking wobble target slowly
		wobbleYaw   = wobbleYaw + (targetWobbleYaw - wobbleYaw) * cameraWobbleLerp
		wobblePitch = wobblePitch + (targetWobblePitch - wobblePitch) * cameraWobbleLerp
	else
		-- When not moving, slowly decay walking wobble toward 0
		wobbleYaw   = wobbleYaw + (0 - wobbleYaw) * cameraWobbleLerp
		wobblePitch = wobblePitch + (0 - wobblePitch) * cameraWobbleLerp
	end

	-- Combine idle + walking wobble into a single target
	local targetWobbleYawTotal   = idleYaw + wobbleYaw
	local targetWobblePitchTotal = idlePitch + wobblePitch

	-- Smoothly interpolate final camera wobble toward combined target
	local finalYaw   = wobbleYaw   + (targetWobbleYawTotal - wobbleYaw) * 0.15
	local finalPitch = wobblePitch + (targetWobblePitchTotal - wobblePitch) * 0.15
	wobbleYaw   = finalYaw
	wobblePitch = finalPitch

	-- Apply camera rotation
	local camYawCFrame   = CFrame.Angles(0, math.rad(currentYaw + finalYaw), 0)
	local camPitchCFrame = CFrame.Angles(math.rad(currentPitch + finalPitch), 0, 0)
	camera.CFrame        = CFrame.new(cameraPos) * camYawCFrame * camPitchCFrame

	-- --- FLASHLIGHT ---
	-- Pick new jitter target occasionally
	jitterTimer = jitterTimer + deltaTime
	if jitterTimer >= flashlightJitterInterval then
		jitterTimer       = 0
		targetJitterYaw   = (math.random() * 2 - 1) * flashlightJitterMagnitude
		targetJitterPitch = (math.random() * 2 - 1) * flashlightJitterMagnitude
	end

	-- Interpolate jitter toward target
	jitterYaw   = jitterYaw + (targetJitterYaw - jitterYaw) * flashlightJitterLerp
	jitterPitch = jitterPitch + (targetJitterPitch - jitterPitch) * flashlightJitterLerp

	-- Rotation difference toward target
	local deltaYaw   = targetYaw - currentYaw
	local deltaPitch = targetPitch - currentPitch

	-- Apply follow factor
	deltaYaw   = deltaYaw * flashlightFollowFactor
	deltaPitch = deltaPitch * flashlightFollowFactor

	-- Clamp deviation
	deltaYaw   = math.clamp(deltaYaw, -flashlightMaxAngle, flashlightMaxAngle)
	deltaPitch = math.clamp(deltaPitch, -flashlightMaxAngle, flashlightMaxAngle)

	-- Apply flashlight rotation with subtle jitter
	local flashYawCFrame   = CFrame.Angles(0, math.rad(currentYaw + deltaYaw + jitterYaw), 0)
	local flashPitchCFrame = CFrame.Angles(math.rad(currentPitch + deltaPitch + jitterPitch), 0, 0)
	flashlightPart.CFrame  = CFrame.new(cameraPos) * flashYawCFrame * flashPitchCFrame
end)

This is in StarterCharacterScripts.

@N_ckD, lmk if you have any questions on it.

1 Like

flashlight so funny i died of laughter

2 Likes

Oh wow, thats an interesting approach actually, my script is only 80 lines,

But again, my issue with it is the FPS, higher the FPS = Slower the movement and sensitivity ive noticed

I’ll see if mine breaks on higher FPS

Doesn’t look like mine breaks on higher FPS. Or maybe I’m just blind.

1 Like

Thats impressive ngl, its quite cool how it looks as well

1 Like

I’m not sure why yours does that with FPS; maybe you’re applying delta time wrong? I’ve never had any issues with delta time like that.


I don’t really have “DeltaTime” cuz im using bindtorenderstep

1 Like

Okay, i fixed it and now its frame dependant, but my only problem is higher fps = stops quicker lower fps = stops slower but thats easy fix

1 Like

Yours looks significantly cleaner than mine. (although it also probably does less, so maybe that’s why lol)

1 Like

i didn’t find it very funny, but it is a neat effect so thank you for sharing either way :bubbles:

1 Like

Okay! I have made it better and rewritten parts of it (4 or 3 year old code) :sob:

--!strict
--!optimize 2

local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")

local MOUSE_SENSITIVITY: number = 0.15
local CAMERA_OFFSET: Vector3 = Vector3.new(0, 2, 0)
local SMOOTHING_FACTOR: number = 12
local MAX_VERTICAL_ANGLE: number = 80

local CAMERA_RENDER_PRIORITY: number = Enum.RenderPriority.Camera.Value + 1
local RENDER_STEP_NAME: string = "FirstPersonCamera"

local player = Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local rootPart = character:WaitForChild("HumanoidRootPart")
local humanoid = character:WaitForChild("Humanoid")
local camera = workspace.CurrentCamera

local flashlight: BasePart = ReplicatedStorage.Flashlight:Clone()
flashlight.Parent = character

local targetXAngle: number = 0
local targetYAngle: number = 0

local currentXAngle: number = 0
local currentYAngle: number = 0

local function smoothDamp(current: number, target: number, smoothing: number, deltaTime: number): number
	local factor = math.exp(-smoothing * deltaTime)
	return current + (target - current) * (1 - factor)
end

local function makeInvisible(object: Instance)
	if object:IsA("BasePart") then
		object.Transparency = 1
		if object.Name ~= "HumanoidRootPart" and object.Name ~= "Head" then
			object.CanCollide = false
		end
	elseif object:IsA("Decal") or object:IsA("Texture") then
		object.Transparency = 1
	elseif object:IsA("Accessory") then
		object:Destroy()
	end
end

UserInputService.InputChanged:Connect(function(input, gameProcessed)
	if gameProcessed then 
		return 
	end

	if input.UserInputType == Enum.UserInputType.MouseMovement then
		local deltaX = input.Delta.X * MOUSE_SENSITIVITY
		local deltaY = input.Delta.Y * MOUSE_SENSITIVITY

		targetYAngle = targetYAngle - deltaX
		targetXAngle = math.clamp(
			targetXAngle - deltaY,
			-MAX_VERTICAL_ANGLE,
			MAX_VERTICAL_ANGLE
		)
	end
end)

local function updateCamera(deltaTime: number)
	camera.CameraType = Enum.CameraType.Scriptable
	UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter

	currentXAngle = smoothDamp(currentXAngle, targetXAngle, SMOOTHING_FACTOR, deltaTime)
	currentYAngle = smoothDamp(currentYAngle, targetYAngle, SMOOTHING_FACTOR, deltaTime)

	local cameraPosition = rootPart.Position + CAMERA_OFFSET

	local cameraCFrame = CFrame.new(cameraPosition) 
		* CFrame.Angles(0, math.rad(currentYAngle), 0)
		* CFrame.Angles(math.rad(currentXAngle), 0, 0)

	camera.CFrame = cameraCFrame

	local flashlightCFrame = CFrame.new(cameraPosition)
		* CFrame.Angles(0, math.rad(targetYAngle), 0)
		* CFrame.Angles(math.rad(targetXAngle), 0, 0)

	flashlight.CFrame = flashlightCFrame
end

RunService:BindToRenderStep(RENDER_STEP_NAME, CAMERA_RENDER_PRIORITY, updateCamera)

for _, descendant in pairs(character:GetDescendants()) do
	makeInvisible(descendant)
end

character.DescendantAdded:Connect(makeInvisible)

humanoid.Died:Connect(function()
	RunService:UnbindFromRenderStep(RENDER_STEP_NAME)
	camera.CameraType = Enum.CameraType.Custom
	UserInputService.MouseBehavior = Enum.MouseBehavior.Default
end)

102 lines ;3

1 Like

Neat! Mine also does camera wobble and makes the flashlight shaky, which is probably overkill for most cases, so it’s nice to have one that just does the flashlight.

1 Like