Roblox ProximityPrompt Script: Triggered, Hold & Cooldown
Last updated:
Quick answer: Insert a ProximityPrompt into a part, then connect `prompt.Triggered` in a server Script. The callback receives the player who pressed the key, so you can give items, open doors or start events. Set `HoldDuration` for hold-to-use and flip `Enabled` to build cooldowns.
A ProximityPrompt is the built-in "press E to interact" button in Roblox. You parent one to a part, set its text and distance, and connect a function to its Triggered event. When a player gets close and presses the key, Roblox fires that event and hands you the Player who did it. It replaces the old ClickDetector for most interaction, and it works on keyboard, gamepad and touch without any extra code from you.
How do you create a ProximityPrompt?
You can add one in Studio (Part, then the plus icon, then ProximityPrompt) or create it from code. Doing it from code is useful when you spawn items at runtime:
local part = workspace.Door
local prompt = Instance.new("ProximityPrompt")
prompt.ActionText = "Open"
prompt.ObjectText = "Wooden Door"
prompt.KeyboardKeyCode = Enum.KeyCode.E
prompt.MaxActivationDistance = 8
prompt.HoldDuration = 0
prompt.RequiresLineOfSight = true
prompt.Parent = part
The properties you will touch most often:
- ActionText is the verb ("Open", "Pick up", "Talk").
- ObjectText is the smaller label above it, usually the thing's name.
- MaxActivationDistance is how close a player must be, in studs. The default is 10.
- HoldDuration is seconds the key must be held. Zero means a simple press.
- RequiresLineOfSight hides the prompt when something solid is between the player and the part.
- Enabled turns the prompt on and off without destroying it.
How do you detect when someone uses it?
Connect to Triggered. Put this in a Script (not a LocalScript) so the logic runs on the server:
local prompt = script.Parent.ProximityPrompt
prompt.Triggered:Connect(function(player: Player)
print(player.Name .. " used the prompt")
end)
Unlike the Touched event, you do not need to dig a player out of a body part. The argument already is the Player, so there is no GetPlayerFromCharacter step. If you want to compare it with how that works, the Touched event guide shows the extra work a physical trigger needs.
Can you make a hold-to-use prompt?
Set HoldDuration to a number of seconds. Roblox draws the progress ring and only fires Triggered once the hold completes. Two companion events let you react earlier:
prompt.HoldDuration = 2
prompt.ActionText = "Open chest"
prompt.PromptButtonHoldBegan:Connect(function(player)
print(player.Name .. " started holding")
end)
prompt.PromptButtonHoldEnded:Connect(function(player)
print(player.Name .. " let go early (or finished)")
end)
prompt.Triggered:Connect(function(player)
print(player.Name .. " finished the hold")
end)
Remember that PromptButtonHoldEnded fires whether the hold finished or was cancelled, so use Triggered for the real action.
How do you add a cooldown?
The easiest approach is to disable the prompt while the cooldown runs. Players see it vanish, so you also get free feedback:
local COOLDOWN = 5
local prompt = script.Parent.ProximityPrompt
prompt.Triggered:Connect(function(player)
prompt.Enabled = false
-- give the reward here
local leaderstats = player:FindFirstChild("leaderstats")
if leaderstats and leaderstats:FindFirstChild("Coins") then
leaderstats.Coins.Value += 10
end
task.wait(COOLDOWN)
prompt.Enabled = true
end)
If each player should have their own cooldown (for example a daily chest), disabling the prompt for everyone is the wrong tool. Track a timestamp per player in a table keyed by UserId and compare it with os.time() inside the callback instead.
How do you handle lots of prompts at once?
Copying the same script into forty doors does not scale. Tag the parts with CollectionService and use one script. ProximityPromptService also exposes a global PromptTriggered event that fires for any prompt, which is convenient for a central interaction system:
local ProximityPromptService = game:GetService("ProximityPromptService")
local CollectionService = game:GetService("CollectionService")
local handlers = {
Door = function(player, prompt)
print(player.Name .. " opened " .. prompt.Parent.Name)
end,
Chest = function(player, prompt)
print(player.Name .. " looted " .. prompt.Parent.Name)
end,
}
ProximityPromptService.PromptTriggered:Connect(function(prompt, player)
for tag, handler in handlers do
if CollectionService:HasTag(prompt.Parent, tag) then
handler(player, prompt)
return
end
end
end)
Put the handlers in a ModuleScript once the table grows; the ModuleScript guide shows how.
Can you customise how the prompt looks?
Set Style to Enum.ProximityPromptStyle.Custom and build your own UI listening to ProximityPromptService.PromptShown and PromptHidden in a LocalScript. That is a larger topic, and the GUI scripting basics cover the building blocks. For most games the default look is fine and a small tweak of UIOffset or ObjectText is enough.
Common mistakes
- Doing the real work in a LocalScript. Exploiters can fire client-side code at will. Keep rewards, doors and purchases in a server Script.
- Forgetting the prompt needs a valid parent. It only works under a BasePart or Attachment, and it needs to be in the workspace.
- Using
wait()for the cooldown. Usetask.wait. See task.wait vs wait. - Never disconnecting for removed items. If you destroy the part, the prompt goes with it, but connections you made in a table of your own should be cleaned up.
- Setting a huge MaxActivationDistance. Prompts stack on screen when many are in range, and players cannot tell which one they are about to use.
- Skipping a sanity check on valuable prompts. For anything worth cheating on, also compare the player's character position to the prompt's parent on the server before paying out.
Practice it
Interaction systems are a good first project because the loop is short: build the part, add the prompt, see the result. Try it in the Luablox lessons, follow with the theory lessons, and test yourself on the practice problems.
Related guides: Touched events, tool scripts, and server-side validation for keeping interactions honest.
Frequently asked questions
Should I handle ProximityPrompt.Triggered on the server or the client?
Handle anything that changes game state on the server, because the server version of Triggered cannot be faked by editing a LocalScript. Use a LocalScript with the same event only for visual effects such as sounds or camera moves that only that player needs to see.
Why does my ProximityPrompt not show up?
Check that the prompt is Enabled, that the player is within MaxActivationDistance, and that RequiresLineOfSight is not blocking it behind a wall. Also make sure the parent is a BasePart or Attachment that is actually in the workspace and not anchored far from where you are standing.
How do I change the key a ProximityPrompt uses?
Set KeyboardKeyCode, for example Enum.KeyCode.F, and GamepadKeyCode for controllers. Touch players always get an on-screen button, so you do not need to handle that separately.
How do I stop players spamming a prompt?
Set prompt.Enabled to false inside the Triggered callback, wait for your cooldown with task.wait, then set it back to true. Because Enabled is a property, the prompt disappears for everyone during the cooldown, which doubles as visual feedback.
Learn core Roblox scripting and Luau programming concepts, including: roblox proximityprompt script, proximityprompt triggered, roblox hold e to interact, proximityprompt holdduration, proximitypromptservice.