Efficient way of making shotgun spread realistic

Hello. I look for an efficient way of making a shotgun spread. I made a shotgun for a commission that I work on, but the spread looks unrealistic AT ALL:
https://streamable.com/7hgd5r
Here is the code that I use:

while wait(info.Rate) do
	if isShooting and not script.Parent.Configuration.IsOnSafe.Value and not script.Parent.Configuration.IsReloading.Value and script.Parent.Ammo.Value ~= 0 then
		local damage = 0;
		local humanoid = nil;
		function create_Bullet()
			local rayCastParams = RaycastParams.new();
			local wl = {workspace.Baseplate, workspace:FindFirstChild("NPC")}
			for _, v in ipairs(game.Players:GetChildren()) do
				if v ~= game.Players.LocalPlayer then
					table.insert(wl, #wl + 1, v.Character)
				end
			end
			rayCastParams.FilterDescendantsInstances = wl
			rayCastParams.FilterType = Enum.RaycastFilterType.Whitelist
			local bulletClone = script.Bullet:Clone();
			bulletClone.Parent = workspace
			bulletClone.CFrame = CFrame.new(script.Parent.ShootPart.Position, mouse.Hit.p.Unit * info.Range)
			bulletClone.Transparency = 0
			wait(.03)
			local sPosition = bulletClone.Position;
			local bodyVelocity = Instance.new("BodyVelocity");
			local x = math.random(-info.Spread, info.Spread)
			local y = math.random(-info.Spread, info.Spread)
			local z = 0
			local rayUnit = ((mouse.Hit.p + Vector3.new(x, y / 2, z)) - bulletClone.Position).Unit * 250
			bodyVelocity.Velocity = rayUnit
			local corout = coroutine.create(function()
				local startPos = bulletClone.Position;
				repeat wait() until (bulletClone.Position - sPosition).Magnitude < (mouse.Hit.p.Unit * info.Range - bulletClone.Position).Magnitude
				bulletClone:Destroy()
			end)
			coroutine.resume(corout)
			bulletClone.Anchored = false
			bodyVelocity.Parent = bulletClone
			local result = workspace:Raycast(bulletClone.Position,  ((mouse.Hit.p.Unit * info.Range + Vector3.new(x, y / 2, z))  - sPosition).Unit * info.Range, rayCastParams);
			
			if result and result.Instance then
				if result.Instance.Parent:FindFirstChildOfClass("Humanoid") or result.Instance.Parent.Parent:FindFirstChildOfClass("Humanoid") then
					damage = damage + info.Damage;
					humanoid = result.Instance.Parent:FindFirstChildOfClass("Humanoid") or result.Instance.Parent.Parent:FindFirstChildOfClass("Humanoid")
				end
			end
		end
		
		local bulletsCreated = 1;
		
		local c = coroutine.create(function()
			repeat game:GetService("RunService").Stepped:Wait()
				bulletsCreated = bulletsCreated + 1
				create_Bullet()
			until bulletsCreated >= info.BulletsInOneShot

			if damage > 0 then
				if humanoid ~= nil then
					if info.TeamKill then
						damageRemoteEvent:FireServer(humanoid, damage)
					else
						if game.Players:GetPlayerFromCharacter(humanoid.Parent) then
							if game.Players:GetPlayerFromCharacter(humanoid.Parent).Team ~= game.Players.LocalPlayer.Team then
								damageRemoteEvent:FireServer(humanoid, damage)
							else
							end
						else
							damageRemoteEvent:FireServer(humanoid, damage)
						end
					end
				end
			end
		end)
		
		coroutine.resume(c)
	elseif script.Parent.Ammo.Value == 0 and isShooting then
		if not script.Parent.Configuration.IsReloading.Value and script.Parent.Ammo.Value ~= script.Parent.MaxAmmo.Value then
			reloadRemote:FireServer()
			game:GetService("Players").LocalPlayer:WaitForChild("PlayerGui"):WaitForChild("Weapon").Main.Disabled = true
			game:GetService("Players").LocalPlayer:WaitForChild("PlayerGui"):WaitForChild("Weapon").WeaponName.Text = "Reloading..."
			wait(2 / script.Parent.ReloadSpeed.Value + .5)
			game:GetService("Players").LocalPlayer:WaitForChild("PlayerGui"):WaitForChild("Weapon").Main.Disabled = false
		
       end
	end
end

I believed that the wait(.03) in the create_Bullet() function is the cause of unrealistic bullet creation. Perhaps try using spawn(function() instead that way you can create new thread instantly instead of having to wait 0.03 seconds before creating another bullet.

while wait(info.Rate) do
	if isShooting and not script.Parent.Configuration.IsOnSafe.Value and not script.Parent.Configuration.IsReloading.Value and script.Parent.Ammo.Value ~= 0 then
		local damage = 0;
		local humanoid = nil;
		function create_Bullet()
			local rayCastParams = RaycastParams.new();
			local wl = {workspace.Baseplate, workspace:FindFirstChild("NPC")}
			for _, v in ipairs(game.Players:GetChildren()) do
				if v ~= game.Players.LocalPlayer then
					table.insert(wl, #wl + 1, v.Character)
				end
			end
			rayCastParams.FilterDescendantsInstances = wl
			rayCastParams.FilterType = Enum.RaycastFilterType.Whitelist
			local bulletClone = script.Bullet:Clone();
			bulletClone.Parent = workspace
			bulletClone.CFrame = CFrame.new(script.Parent.ShootPart.Position, mouse.Hit.p.Unit * info.Range)
			bulletClone.Transparency = 0
			wait(.03)
			local sPosition = bulletClone.Position;
			local bodyVelocity = Instance.new("BodyVelocity");
			local x = math.random(-info.Spread, info.Spread)
			local y = math.random(-info.Spread, info.Spread)
			local z = 0
			local rayUnit = ((mouse.Hit.p + Vector3.new(x, y / 2, z)) - bulletClone.Position).Unit * 250
			bodyVelocity.Velocity = rayUnit
			local corout = coroutine.create(function()
				local startPos = bulletClone.Position;
				repeat wait() until (bulletClone.Position - sPosition).Magnitude < (mouse.Hit.p.Unit * info.Range - bulletClone.Position).Magnitude
				bulletClone:Destroy()
			end)
			coroutine.resume(corout)
			bulletClone.Anchored = false
			bodyVelocity.Parent = bulletClone
			local result = workspace:Raycast(bulletClone.Position,  ((mouse.Hit.p.Unit * info.Range + Vector3.new(x, y / 2, z))  - sPosition).Unit * info.Range, rayCastParams);

			if result and result.Instance then
				if result.Instance.Parent:FindFirstChildOfClass("Humanoid") or result.Instance.Parent.Parent:FindFirstChildOfClass("Humanoid") then
					damage = damage + info.Damage;
					humanoid = result.Instance.Parent:FindFirstChildOfClass("Humanoid") or result.Instance.Parent.Parent:FindFirstChildOfClass("Humanoid")
				end
			end
		end

		for i = 1, #create_Bullet do
			spawn(function()
				create_Bullet()
			end)
		end

		if damage > 0 then
			if humanoid ~= nil then
				if info.TeamKill then
					damageRemoteEvent:FireServer(humanoid, damage)
				else
					if game.Players:GetPlayerFromCharacter(humanoid.Parent) then
						if game.Players:GetPlayerFromCharacter(humanoid.Parent).Team ~= game.Players.LocalPlayer.Team then
							damageRemoteEvent:FireServer(humanoid, damage)
						else
						end
					else
						damageRemoteEvent:FireServer(humanoid, damage)
					end
				end
			end
		end

	elseif script.Parent.Ammo.Value == 0 and isShooting then
		if not script.Parent.Configuration.IsReloading.Value and script.Parent.Ammo.Value ~= script.Parent.MaxAmmo.Value then
			reloadRemote:FireServer()
			game:GetService("Players").LocalPlayer:WaitForChild("PlayerGui"):WaitForChild("Weapon").Main.Disabled = true
			game:GetService("Players").LocalPlayer:WaitForChild("PlayerGui"):WaitForChild("Weapon").WeaponName.Text = "Reloading..."
			wait(2 / script.Parent.ReloadSpeed.Value + .5)
			game:GetService("Players").LocalPlayer:WaitForChild("PlayerGui"):WaitForChild("Weapon").Main.Disabled = false

		end
	end
end

The spread is unrealistic, do you find any issue with how I do the spread?