Creating a "Laser" on raycast

Hi, I’ve been having a problem creating a “Laser Beam” upon shooting my raycast gun.

The problem is that I’m creating the Laser Beam, and I made it rezise using MaxRange.

Here is what’s happening:

How could I make the beam stop on the Hit position?

-- Server
local function CreateVisual(Start, End, direction)
	local Center = Start + direction/2 
	local Distance = direction.Magnitude

	local visual ="Part", game.Workspace.Visuals)
	visual.Name = "Visual"
	visual.Anchored = true
	visual.CanCollide = false
	visual.BrickColor ="Neon orange")
	visual.Material = "Neon"

	visual.CFrame =, Start)
	visual.Size =, .1, direction.Magnitude)
	TweenService:Create(visual,, {Size =, .05, Distance), Transparency = 1}):Play()
	game:GetService("Debris"):AddItem(visual, VisualTime)

RemoteEvent.OnServerEvent:Connect(function(plr, hit, Damage, HSMEnabled, HSM, Start, End, Direction, MaxRange)

	if hit then
		if hit:GetAttribute("Health") then

			local NewHealth = hit:GetAttribute("Health") - Damage
			hit:SetAttribute("Health", NewHealth)

		elseif hit.Parent:GetAttribute("Health") then

			local NewHealth = hit.Parent:GetAttribute("Health") - Damage
			hit.Parent:SetAttribute("Health", NewHealth)


		local hum = hit.Parent:FindFirstChild("Humanoid")

		if hum then
			if hit.Name == "Head" then
				RemoteEvent:FireClient(PLR, hit, true)

				if HSMEnabled then
					hum:TakeDamage(Damage * HSM)
			elseif hit.Name == "Torso" or hit.Name == "HumanoidRootPart" then
				RemoteEvent:FireClient(PLR, hit, false)
			elseif hit.Name == "Left Arm" or hit.Name == "Right Arm" then
				RemoteEvent:FireClient(PLR, hit, false)
				hum:TakeDamage(Damage / 1.2)
			elseif hit.Name == "Left Leg" or hit.Name == "Right Leg" then
				RemoteEvent:FireClient(PLR, hit, false)
				hum:TakeDamage(Damage / 1.4)
			TagHumanoid(hum, plr)
			local HitSound = Handle.Hit:Clone()
			HitSound.Parent = hit
			game.Debris:AddItem(HitSound, 1)

	CreateVisual(Start, End, Direction.LookVector * MaxRange)
-- Local

local MaxRange = 250
local Damage = 12
local HeadshotMultiplierEnabled = true
local HeadshotMultiplier = 1.2
local pelletCount = 1 --The number of pellets you want being shot
local maximumOffset = 0.5 --The maximum number of studs that the bullets will offset at
local filterTable = {}
local params =

local function CastRay(Start, End)
	local Dir =, End)*CFrame.Angles(math.rad(math.random(-maximumOffset,maximumOffset)),math.rad(math.random(-maximumOffset,maximumOffset)),0)

	local raycast = game.Workspace:Raycast(Start, Dir.LookVector * MaxRange, params)
	if raycast then
		RemoteEvent:FireServer(raycast.Instance, Damage, HeadshotMultiplierEnabled, HeadshotMultiplier, Start, End, Dir, MaxRange)
		RemoteEvent:FireServer(nil, Damage, HeadshotMultiplierEnabled, HeadshotMultiplier, Start, End, Dir, MaxRange)
local width = 0.1
local function VisualizeRaycast(Origin,Position)
	local RayPart ="Part")
	RayPart.Parent = -- Your parent
	RayPart.Anchored = true
	RayPart.CanCollide = false
	RayPart.CFrame =,0.5),Position)
	RayPart.Size =,width,(Origin-Position).Magnitude)
	RayPart.Color = -- Color
	RayPart.Material = -- Material
local RayCast = game.Workspace:Raycast(Origin,Direction)

Here we use a function to visualize our raycast that being a laser, First we make a part, set its basic properties then we use cframe to make the part face our end position and positioning it at the midpoint of the 2 vectors, then we scale it so that its the length between the 2 vectors, its thickness you can change using the variable on top.

For your script it would be:

local function CreateVisual(Start, End)
	local Distance = (Start-End).Magnitude
	local visual ="Part", game.Workspace.Visuals)
	visual.Name = "Visual"
	visual.Anchored = true
	visual.CanCollide = false
	visual.BrickColor ="Neon orange")
	visual.Material = "Neon"

	visual.CFrame =,0.5), Start)
	visual.Size =, .1, Distance)
	TweenService:Create(visual,, {Size =, .05, Distance), Transparency = 1}):Play()
	game:GetService("Debris"):AddItem(visual, VisualTime)

What your doing wrong is your not using the end to be the point where mouse is that being the point where our ray hits a object.


You see, what I’m trying to do is add a Bullet Spread to my gun.

If you look in the Local script, you can see Dir
which is the bullet spread cframe

1 Like

I see then what you can do is add a little bit of offset in the end position of the raycast. Lets say 0.1-0.15 or more.

1 Like

Can you tell me how could I make this math?

1 Like
Put this where end is first defined in the local script that being 
after CastRay function in local 
local SpreadIntensity = 1 -- Determines the amount of spread you want
local Spread =,math.random(),math.random())*SpreadIntensity
End = End+Spread

For Range do:

local MinSpread,MaxSpread = 10,15
local Dampining = 100
local Spread =,MaxSpread),math.random(MinSpread,MaxSpread),math.random(MinSpread,MaxSpread))/Dampining

Your problem with the laser stems from the fact that you’re sizing it based on the full Raycast distance - not just the length of the ray that hits.

You might recall that subtracting a number from another number gives you the difference between those numbers (5 - 3 = 2; 3 + 2 = 5), and that applies in multiple dimensions as well. First, subtract the coordinates of your start point from your terminal point - this will give you a vector between the two points. The length of this vector is the distance between the start point and terminal point. In your CreateVisual() function, this would be written as (End - Start).Magnitude

As for a better spread function…

function module.randomCone(axis:Vector3, degrees:number):Vector3
   local angle = math.rad(degrees)
   local cosAngle = math.cos(angle)
   local z, phi
   z = 1 - math.random()*(1 - cosAngle)
   phi = math.random()*math.pi*2
   local r = math.sqrt(1 - z*z)
   local x = r * math.cos(phi)
   local y = r * math.sin(phi)
   local vec =, y, z)
   if axis.Z > 0.9999 then
   	return vec
   elseif axis.Z < -0.9999 then
   	return -vec
   local orth = Vector3.zAxis:Cross(axis)
   local rot = math.acos(axis:Dot(Vector3.zAxis))
   return CFrame.fromAxisAngle(orth, rot) * vec

Plug your directional vector and the maximum spread angle (in degrees) into the function. The value returned by the function should be used as your Raycast direction. The math is explained well by this post on StackExchange.

Also you really, really shouldn’t be handling hit detection and damage on the client. It’ll be trivial for an exploiter to fire the remote rapidly and kill everyone in the server. You should only be sending the server a directional vector to tell the server where the player is aiming.

1 Like

Heads up, randomly jittering the end point won’t have an effect on anything aside from where the laser’s visual is. The actual instance and position hit by the Raycast is still the same.

Thank you guys for trying to help.

I couldn’t figure out how to get all of this together to make it work, I’ll try to remake the hole scripts again.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.