Countdown breaking when spammed

What I want to happen :
The countdown resets to 60 immediately after I leave the circle and the loop stops running in order for the timer to completely reset.

What happens:

Code (Runs every time a specified part is touched):

local normCD = 60

local function startNormCountDown()
	if normMinQueue == normPlayersQueued then
		local countdown = normDisplay.SurfaceGui.Frame.Countdown
		for i = 60,0,-1 do
			if normPlayersQueued >= normMinQueue then
				normCD = i
				countdown.Text = normCD
			else
				normCD = 60
				countdown.Text = normCD
				break
			end
			task.wait(1)
		end
	end
end

Do you cancel the previous countdown when they leave? Try testing it on the console and when you get it to work apply it to the part

At the end, I see you staying on the circle, which is the problem; Add a Touched and ToucheEnded event, so it won’t glitch and only fire when the Touched event gets registered.

if I were you, rather than running a function every time the function is called. I would use GetTouchingParts every second or few seconds.
https://developer.roblox.com/en-us/api-reference/function/BasePart/GetTouchingParts
As you currently have it, spamming it can cause the same function to be running in synchronous, which would allow one to try to cancel it, and then an instant later another one continues the count, which is just problematic.

I won’t write a whole program example, but here’s an explaination:
Have a loop which runs the same code every second or few seconds. Within that create a variable, for example touchingParts and set it to the return of :GetTouchingParts on the part which can be touched (if that makes sense).

Then, create a table which you will fill with player objects. Once that is done, iterate through the list of parts and if part.Parent and Players:GetPlayerFromCharacter(part.Parent), then add that player to the table. Do this for every part. If the part is the child of a player’s character, then before adding the player to the table, use .find to see if that player is in the table already (will return nil if they aren’t). At that point, you’ll have a list of players which you can use for your playerlist Gui. You can then also have the same code from the function you used. That code works, the problem is that it runs multiple times as a result of the event. This option avoids the event.

The “.Touched” and “.TouchEnded” events are quite inaccurate (they will fire repetitively even if it appears as if the touching parts never stopped touching). Consider using “GetPartsInPart()” or “GetTouchingParts()” instead.

Here’s an example script.

local Run = game:GetService("RunService")
local Players = game:GetService("Players")
local Part = script.Parent

local Timer = 60

local function OnStepped(Time, Step)
	local TouchingParts = workspace:GetPartsInPart(Part)
	for _, TouchingPart in ipairs(TouchingParts) do
		local TouchingModel = TouchingPart:FindFirstAncestorOfClass("Model")
		if TouchingModel then
			local TouchingPlayer = Players:GetPlayerFromCharacter(TouchingModel)
			if TouchingPlayer then
				Timer -= Step
				if Timer <= 0 then
					Timer = 60
					return
				end
			else
				print(Timer)
				Timer = 60
			end
		else
			print(Timer)
			Timer = 60
		end
	end
end

Run.Stepped:Connect(OnStepped)