Make it if there are to many surfels or lights in an area they don't spawn. Also dynamic objects are extremely laggy. Finally just give me a full complete implementation that works to the best of your ability.

Here is what I have so far

ModuleScript: -- ReplicatedStorage -> GISettings
local GISettings = {}

-- General Settings
GISettings.Enabled = true
GISettings.UpdateInterval = 0.1 -- Time in seconds between updates
GISettings.MaxBounces = 2 -- Number of light bounces to simulate

-- Surfel Settings
GISettings.MaxSurfelCount = 100 -- Maximum number of surfels
GISettings.SurfelSize = 0.5
GISettings.SurfelLifetime = 5 -- Time in seconds before a surfel is respawned

-- Lighting Settings
GISettings.LightIntensity = 1/GISettings.MaxSurfelCount
GISettings.LightRange = 10
GISettings.ColorBleedIntensity = 0.5/GISettings.MaxSurfelCount

-- View Settings
GISettings.SpawnDistance = 100 -- Distance from the camera to spawn surfels
GISettings.ViewExpansionAngle = 50 -- Degrees to expand the camera's field of view
GISettings.CameraShiftThreshold = 0.01 -- Threshold for detecting camera rotation

-- Movement Detection
GISettings.ObjectMovementDetectionEnabled = true
GISettings.MovementUpdateInterval = 0.2
GISettings.MovementRangeMultiplier = 1.5 -- Multiplier for point light range to determine affected surfels

-- Debug Settings
GISettings.ShowSurfels = false -- Set to true to visualize surfels

return GISettings

Localscript SurfelManager: -- StarterPlayerScripts -> SurfelManager
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local CollectionService = game:GetService("CollectionService")
local GISettings = require(game.ReplicatedStorage.GISettings)

local player = Players.LocalPlayer
local camera = workspace.CurrentCamera
local surfelsFolder = workspace:FindFirstChild("SurfelsFolder") or Instance.new("Folder", workspace)
surfelsFolder.Name = "SurfelsFolder"

local surfels = {}
local lastCameraCFrame = camera.CFrame
local lastCameraLookVector = camera.CFrame.LookVector

-- Table to keep track of moving objects
local movingObjects = {}

-- Function to monitor objects in the workspace
function MonitorMovingObjects()
	while GISettings.ObjectMovementDetectionEnabled do
		for _, object in ipairs(workspace:GetDescendants()) do
			if object:IsA("BasePart") and object:GetAttribute("IsDynamic") then
				local lastPosition = object:GetAttribute("LastPosition")
				if lastPosition then
					if (object.Position - lastPosition).Magnitude > 0 then
						table.insert(movingObjects, object)
					end
				end
				object:SetAttribute("LastPosition", object.Position)
			end
		end
		wait(GISettings.MovementUpdateInterval)
	end
end

-- Function to update surfels affected by moving objects
function UpdateSurfelsForMovingObjects()
	for _, movingObject in ipairs(movingObjects) do
		local affectedSurfels = GetSurfelsInRange(movingObject.Position, GISettings.LightRange * GISettings.MovementRangeMultiplier)
		for _, surfel in ipairs(affectedSurfels) do
			UpdateSurfel(surfel)
		end
	end
	movingObjects = {}
end

-- Get surfels within a certain range
function GetSurfelsInRange(position, range)
	local nearbySurfels = {}
	for _, surfel in pairs(surfelsFolder:GetChildren()) do
		if surfel:IsA("Part") and (surfel.Position - position).Magnitude <= range then
			table.insert(nearbySurfels, surfel)
		end
	end
	return nearbySurfels
end

-- Update an individual surfel
function UpdateSurfel(surfel)
	local origin = surfel.Position + surfel.CFrame.LookVector * 0.01
	local direction = -surfel.CFrame.LookVector * 0.1

	local raycastParams = RaycastParams.new()
	raycastParams.FilterDescendantsInstances = {surfelsFolder, player.Character}
	raycastParams.FilterType = Enum.RaycastFilterType.Blacklist

	local result = workspace:Raycast(origin, direction, raycastParams)

	if result then
		local hitPart = result.Instance
		surfel.Color = hitPart.Color
		local pointLight = surfel:FindFirstChildOfClass("PointLight")
		if pointLight then
			pointLight.Color = hitPart.Color
		end
	end
end

-- Generate surfels within the expanded camera view
function GenerateSurfelsInView()
	if not GISettings.Enabled then return end

	local fov = math.rad(camera.FieldOfView + GISettings.ViewExpansionAngle)
	local aspectRatio = camera.ViewportSize.X / camera.ViewportSize.Y
	local depth = GISettings.SpawnDistance

	local height = 2 * math.tan(fov / 2) * depth
	local width = height * aspectRatio

	local tl = camera.CFrame.LookVector * depth
	- camera.CFrame.RightVector * (width / 2)
		+ camera.CFrame.UpVector * (height / 2)
	local tr = camera.CFrame.LookVector * depth
		+ camera.CFrame.RightVector * (width / 2)
		+ camera.CFrame.UpVector * (height / 2)
	local bl = camera.CFrame.LookVector * depth
	- camera.CFrame.RightVector * (width / 2)
	- camera.CFrame.UpVector * (height / 2)
	local br = camera.CFrame.LookVector * depth
		+ camera.CFrame.RightVector * (width / 2)
	- camera.CFrame.UpVector * (height / 2)

	local raycastParams = RaycastParams.new()
	raycastParams.FilterDescendantsInstances = {surfelsFolder, player.Character}
	raycastParams.FilterType = Enum.RaycastFilterType.Blacklist

	local surfelCount = 0
	while surfelCount < GISettings.MaxSurfelCount do
		local u = math.random()
		local v = math.random()

		local dir1 = tl:Lerp(tr, u)
		local dir2 = bl:Lerp(br, u)
		local direction = dir1:Lerp(dir2, v).Unit * depth

		local origin = camera.CFrame.Position

		local result = workspace:Raycast(origin, direction, raycastParams)

		if result then
			local hitPart = result.Instance
			local surfelKey = tostring(result.Position)
			if not surfels[surfelKey] then
				local surfel = CreateSurfel(result.Position, result.Normal, hitPart)
				surfels[surfelKey] = surfel
				surfelCount = surfelCount + 1
			end
		else
			surfelCount = surfelCount + 1
		end
	end
end

function CreateSurfel(position, normal, hitPart)
	local surfel = Instance.new("Part")
	surfel.Size = Vector3.new(GISettings.SurfelSize, GISettings.SurfelSize, GISettings.SurfelSize)
	surfel.Shape = Enum.PartType.Ball
	surfel.Anchored = true
	surfel.CanCollide = false
	surfel.Transparency = GISettings.ShowSurfels and 0.5 or 1
	surfel.Material = Enum.Material.Neon
	surfel.Name = "Surfel"
	surfel.Color = hitPart.Color
	surfel.Position = position + normal * 0.01 -- Offset to avoid z-fighting
	surfel.CFrame = CFrame.new(surfel.Position, surfel.Position + normal) -- Orient surfel to normal
	surfel.Parent = surfelsFolder

	-- Add a point light
	local pointLight = Instance.new("PointLight")
	pointLight.Brightness = GISettings.LightIntensity
	pointLight.Range = GISettings.LightRange
	pointLight.Color = hitPart.Color
	pointLight.Parent = surfel

	return surfel
end

function UpdateSurfels()
	while true do
		-- Remove surfels outside the expanded camera view
		for key, surfel in pairs(surfels) do
			local screenPoint, onScreen = camera:WorldToViewportPoint(surfel.Position)
			local relativeCameraVector = surfel.Position - camera.CFrame.Position
			local angle = math.deg(math.acos(camera.CFrame.LookVector:Dot(relativeCameraVector.Unit)))

			if angle > (camera.FieldOfView / 2 + GISettings.ViewExpansionAngle) then
				surfel:Destroy()
				surfels[key] = nil
			end
		end

		-- Generate new surfels if the camera has moved or rotated
		local cameraMoved = (camera.CFrame.Position - lastCameraCFrame.Position).Magnitude > 1
		local cameraRotated = (camera.CFrame.LookVector - lastCameraLookVector).Magnitude > GISettings.CameraShiftThreshold

		if cameraMoved or cameraRotated then
			GenerateSurfelsInView()
			lastCameraCFrame = camera.CFrame
			lastCameraLookVector = camera.CFrame.LookVector
		end

		-- Update surfels affected by moving objects
		if GISettings.ObjectMovementDetectionEnabled then
			UpdateSurfelsForMovingObjects()
		end

		wait(GISettings.UpdateInterval)
	end
end

-- Initialize
GenerateSurfelsInView()
spawn(UpdateSurfels)
spawn(MonitorMovingObjects)

LocalScript LightManager: -- StarterPlayerScripts -> LightBallGenerator
local GISettings = require(game.ReplicatedStorage.GISettings)
local surfelsFolder = workspace:WaitForChild("SurfelsFolder")

function GenerateLightBalls()
	while true do
		for _, surfel in pairs(surfelsFolder:GetChildren()) do
			if surfel:IsA("Part") and surfel.Name == "Surfel" then
				-- Update existing light ball or create a new one
				local lightBall = surfel:FindFirstChild("LightBall")
				if not lightBall then
					lightBall = Instance.new("Part")
					lightBall.Name = "LightBall"
					lightBall.Shape = Enum.PartType.Ball
					lightBall.Size = Vector3.new(0.2, 0.2, 0.2)
					lightBall.Anchored = true
					lightBall.CanCollide = false
					lightBall.Position = surfel.Position
					lightBall.Color = surfel.Color
					lightBall.Material = Enum.Material.Neon
					lightBall.Parent = surfel

					-- Add light emission
					local pointLight = Instance.new("PointLight")
					pointLight.Brightness = GISettings.LightIntensity
					pointLight.Range = GISettings.LightRange
					pointLight.Color = surfel.Color
					pointLight.Parent = lightBall
				else
					-- Update light ball's color and position
					lightBall.Color = surfel.Color
					lightBall.Position = surfel.Position
					local pointLight = lightBall:FindFirstChildOfClass("PointLight")
					if pointLight then
						pointLight.Color = surfel.Color
					end
				end
			end
		end
		wait(GISettings.UpdateInterval)
	end
end

spawn(GenerateLightBalls)