How to Detect Key Presses in Roblox (UserInputService Guide)

Quick answer: To detect a key press in Roblox, connect to UserInputService.InputBegan inside a LocalScript and compare input.KeyCode with an Enum.KeyCode value such as Enum.KeyCode.E. Always check gameProcessedEvent first so typing in the chat box does not trigger your actions.

Detecting a key press in Roblox is done with the UserInputService.InputBegan event on the client. You put the code in a LocalScript, connect a function to InputBegan, and compare the incoming input's KeyCode against a value from the Enum.KeyCode table — for example, checking whether the player pressed E with input.KeyCode == Enum.KeyCode.E. If you have used older tutorials that mention keydown, note that the old mouse.KeyDown API was deprecated years ago; UserInputService (or ContextActionService) is the modern replacement.

Why must this run in a LocalScript?

Input only exists on one player's device, so it can never be read on the server. A server Script has no keyboard to listen to — every player's keystrokes happen inside their own client. That means your key-detection code belongs in a LocalScript, usually placed in StarterPlayerScripts or StarterCharacterScripts. If you need the server to know about the key press (for example, to validate an ability), detect the input locally and then fire a RemoteEvent to the server, as covered in the Roblox remote events guide.

Listening with UserInputService.InputBegan

The core pattern looks like this:

local UserInputService = game:GetService("UserInputService")

UserInputService.InputBegan:Connect(function(input, gameProcessedEvent)
	if gameProcessedEvent then
		return -- Roblox already used this input (chat, menus, etc.)
	end

	if input.KeyCode == Enum.KeyCode.E then
		print("E was pressed!")
	end
end)

InputBegan fires the moment a key goes down. Its companion event, InputEnded, fires when the key is released:

UserInputService.InputEnded:Connect(function(input, gameProcessedEvent)
	if input.KeyCode == Enum.KeyCode.E then
		print("E was released")
	end
end)

What is gameProcessedEvent and why does it matter?

The second argument passed to your callback tells you whether Roblox itself consumed the input before your code saw it. When a player is typing in the chat box, pressing E types the letter "e" into the chat — and gameProcessedEvent will be true. If you ignore this flag, your ability fires every time someone writes the word "tree" in chat.

The rule is simple: bail out early when gameProcessedEvent is true unless you deliberately want raw input (rare cases like custom chat systems). This single check prevents the most common bug in beginner input scripts. The same flag exists on other events too, including the Touched event pattern where you also guard against unwanted repeat triggers.

How do I match a specific key?

Every keyboard button maps to a member of the Enum.KeyCode enum. Compare directly:

if input.KeyCode == Enum.KeyCode.Q then ... end
if input.KeyCode == Enum.KeyCode.LeftShift then ... end
if input.KeyCode == Enum.KeyCode.Space then ... end

Some input is not a key at all — mouse clicks arrive as input.UserInputType == Enum.UserInputType.MouseButton1, and touch taps arrive as Enum.UserInputType.Touch. Checking input.UserInputType first keeps your code safe:

UserInputService.InputBegan:Connect(function(input, gameProcessedEvent)
	if gameProcessedEvent then return end

	if input.UserInputType == Enum.UserInputType.Keyboard
		and input.KeyCode == Enum.KeyCode.E then
		-- definitely the E key
	end
end)

How do I detect that a key is being held down?

A press and a hold are different things. To detect holding, set a flag on InputBegan and clear it on InputEnded, then read the flag wherever you need it (or run logic while it is true):

local UserInputService = game:GetService("UserInputService")

local sprinting = false

UserInputService.InputBegan:Connect(function(input, gameProcessedEvent)
	if gameProcessedEvent then return end
	if input.KeyCode == Enum.KeyCode.LeftShift then
		sprinting = true
	end
end)

UserInputService.InputEnded:Connect(function(input)
	if input.KeyCode == Enum.KeyCode.LeftShift then
		sprinting = false
	end
end)

-- somewhere else, e.g. in a RenderStepped loop:
-- if sprinting then character.Humanoid.WalkSpeed = 24 end

Do not try to poll the keyboard every frame; the begin/end pair is exactly what these two events were designed for.

Putting it together: a toggleable ability

Here is a complete example — pressing E toggles a simple speed boost on and off:

-- LocalScript in StarterPlayerScripts
local UserInputService = game:GetService("UserInputService")
local Players = game:GetService("Players")

local player = Players.LocalPlayer

local enabled = false

UserInputService.InputBegan:Connect(function(input, gameProcessedEvent)
	if gameProcessedEvent then return end

	if input.KeyCode == Enum.KeyCode.E then
		enabled = not enabled
		local character = player.Character
		local humanoid = character and character:FindFirstChildOfClass("Humanoid")
		if humanoid then
			humanoid.WalkSpeed = enabled and 28 or 16
		end
		print("Ability:", enabled and "ON" or "OFF")
	end
end)

Note the use of FindFirstChildOfClass instead of indexing directly — reaching into the character blindly can throw errors if the character has not spawned yet, which is the same class of mistake behind "attempt to index nil" errors.

What about mobile players and consoles?

Phones, tablets, and controllers often have no E key at all. If an action matters to gameplay, give every platform a way to trigger it. Two options:

local ContextActionService = game:GetService("ContextActionService")

ContextActionService:BindAction("Activate", function(_, state)
	if state == Enum.UserInputState.Begin then
		print("Activated by keyboard OR controller OR mobile button")
	end
	return Enum.ContextActionResult.Pass
end, true, Enum.KeyCode.E, Enum.KeyCode.ButtonX)

For UI-only needs, UserInputService remains perfectly fine; reach for ContextActionService when the same action must exist across devices.

If you are still getting comfortable with events, functions, and conditionals in Luau, the introduction to Luau covers those foundations first.

Learn Luau by doing

Reading about InputBegan is one thing; wiring it to real gameplay is another. On Luablox lessons you practice each concept interactively, drill the theory in /theory, and solve hands-on challenges in /problems — including input-driven ones.

Frequently asked questions

Why does my script fire when I type in the chat box?

Because you are ignoring the gameProcessedEvent parameter of InputBegan. When Roblox itself consumes an input — such as typing letters into the chat — gameProcessedEvent is true. Return early when it is true so chat text never triggers your key bindings.

Is UserInputService.KeyDown a real function?

No. The old Mouse.KeyDown API was deprecated long ago and UserInputService has no KeyDown method. The modern equivalents are the UserInputService.InputBegan and InputEnded events, or ContextActionService:BindAction for cross-platform actions.

How do I detect if a key is being held down?

Use InputBegan to set a boolean flag when the key goes down and InputEnded to clear it when the key goes up. While the flag is true, the key is being held. This works reliably for sprint keys, charge-up attacks, and similar mechanics.

Does key detection work on mobile and console?

Not directly — phones and many controllers lack a keyboard, so Enum.KeyCode checks only match hardware that has those keys. For gameplay-critical actions, use ContextActionService:BindAction, which supports multiple inputs per action and shows an on-screen button on touch devices.

Where should the key-detection script be placed?

In a LocalScript, typically under StarterPlayerScripts so it runs once per player on the client. Server Scripts cannot receive keyboard input because input only exists on the device the player is using.

Learn core Roblox scripting and Luau programming concepts, including: how to detect key press roblox, userinputservice keydown, roblox input began, roblox userinputservice inputbegan, enum keycode roblox.

Related pages