Instead, upon activation on mouse, start the loop and the loop will exit as soon a state changes where the mouse has stopped its primary button down. This way would avoid having an unnecessary loop running forever. RunService would be in place of this, since they usually house the fastest waiting time and responds as accordingly without interfering with the renderer(frames aren’t suffering too much, except from code expense per loop). To run this timer down, simply try something with os.clock().
local ongoingTick = os.clock()
repeat -- optionally a while loop can work
until os.clock() - ongoingTick >= timeBeforeNewIteration
You can have the loop run as a connected function to the mouse event, which would run in conjunction with a debounce variable (along with the button up event). For example, depending on your code, you’ll probably have both of them in a function connected to your event of the tool. Which would work for nearly any problem of a similar nature, be it related to mouse events or not. Like this:
-- You can have the mouse button events wherever you want eitherway, depending on your code
local Firing = false
-- The event gets Mouse as the parameter
if not Firing then
Firing = true --Setting it to true so it won't have 2 different loops or more
while Firing do
-- Any other code you want
if Firing then
Firing = false
Notice: If you were to get the mouse object without having it already as a parameter, you’d use the GetMouse() function on the LocalPlayer from the Players service.