i made a funny flashlight
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.
I love funny things, and I love flashlights.
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
Please put a laugh warning.
I laughed so hard I vomited then ate my pet hamster’s dog.
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 ![]()
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.
flashlight so funny i died of laughter
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.
Thats impressive ngl, its quite cool how it looks as well
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.
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
Yours looks significantly cleaner than mine. (although it also probably does less, so maybe that’s why lol)
i didn’t find it very funny, but it is a neat effect so thank you for sharing either way ![]()
Okay! I have made it better and rewritten parts of it (4 or 3 year old code) ![]()
--!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
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.
