inputpro

Visual input binding editor for Roblox's Input Action System. Configure keyboard, gamepad, and touch bindings in Studio.

Free Runtime Studio Plugin Keyboard + Gamepad + Touch

How It Works

InputPro ships as two packages: a paid Studio plugin for visual editing, and a free open-source runtime for your game code.

1. Create in Studio

Use the visual editor to create contexts, actions, and bindings. Link your existing touch buttons to actions. Listen for keys in real time.

2. Export

One click exports your config as Roblox instances to StarterGui/InputPro/. A bootstrap script is auto-created.

3. Use in Code

The runtime reads your exported config and provides a simple API: BindPressed, SetContext, CreatePromptHint, and more.

The runtime is free and open-source. You can use it without the plugin by building configs manually in code or by creating the instance hierarchy in Studio.

Plugin Guide Paid

The InputPro plugin adds a visual editor to Roblox Studio for creating input bindings without writing code.

Installation

  1. Purchase and install the plugin from the Creator Store
  2. Open Studio — click the InputPro button in the toolbar
  3. The editor widget opens as a dockable panel

Quick Start

  1. The editor starts with a default "Gameplay" context
  2. Click "+ Action" to create your first action (e.g. "Jump", type Bool)
  3. Expand the action row, then click "+ Keyboard"
  4. The plugin enters listen mode — press a key (e.g. Space) and it's bound immediately
  5. Go to File > Export Config, name it, and click Save
  6. Press Play to test!
Main editor view with actions and bindings

Contexts

Contexts group related actions and control when they're active. A game might have "Gameplay", "UI", and "Vehicle" contexts.

Manage contexts via Context > Manage Contexts in the menu bar, or use the context dropdown in the top bar.

Context Manager popup

Bindings

Keyboard & Gamepad

Expand an action row and click "+ Keyboard" or "+ Gamepad" to add a binding. The plugin enters Listen Mode — press a key or button and it's bound immediately. You can also add bindings manually from a dropdown.

Reserved Keys: Some keys (F1-F12, Escape, etc.) are intercepted by Studio and cannot be bound via listen mode. Check Help > Reserved Keys for the full list.

Touch

InputPro doesn't generate touch UI — you design your own buttons in StarterGui, then link them to actions in the plugin. This gives you full control over your mobile UI design.

How to link a touch button

  1. Create a TextButton or ImageButton in your ScreenGui (StarterGui)
  2. Select an action in the plugin, then click + Touch
  3. Select the button in the Explorer — the plugin validates it's a GuiButton
  4. The link appears as a touch pill on the action row, showing the button name and path

On export, each linked button is stored as attributes (TouchButtonPath, TouchButtonName) on the InputAction instance. At runtime, InputProService resolves the path in PlayerGui, creates an InputBinding with its UIButton property set to your button, and Roblox automatically wires tap events to the action's Pressed/Released signals.

Note: One action can have keyboard, gamepad, and touch bindings simultaneously. Players on mobile tap the button; players on keyboard/gamepad use the bound keys — all firing the same action callbacks.
Action row with touch pill

Analog Triggers

R2/L2 gamepad triggers support custom pressed/released thresholds. Click the binding's edit button to configure when the trigger registers as pressed or released (0.0–1.0 range).

Export & Import

File > Export Config creates the full instance hierarchy in StarterGui/InputPro/<name> and:

File > Import Config reads an existing export back into the editor for modification.

Export dialog

Code Generation

InputPro can generate boilerplate code for your actions and insert it directly into your scripts.

How to use

  1. Select a LocalScript in the Explorer
  2. Go to Edit > Insert Code (or click the code icon on an action row)
  3. Choose an action from the dropdown
  4. Select which events to generate: Pressed, Released, StateChanged
  5. Preview the code, then click Insert

The generated code includes:

-- Example generated code for a "Jump" action:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local InputProService = require(ReplicatedStorage
    :WaitForChild("InputProRuntime")
    :WaitForChild("InputProService")
)

InputProService.BindPressed("Jump", function()
    -- Handle Jump pressed
end)

InputProService.BindReleased("Jump", function()
    -- Handle Jump released
end)
Tip: For non-Bool action types (Direction1D/2D/3D, ViewportPosition), only StateChanged is available — Pressed and Released are automatically disabled in the UI.
Insert Code popup with code preview

Troubleshooting

ProblemSolution
F key won't bind in listen mode Studio intercepts F when Explorer has a selection. The plugin auto-clears selection on focus, but if it still happens, click inside the plugin widget first.
Gamepad not detected Connect your gamepad before opening Studio. Ensure the viewport is in focus when using listen mode for gamepad — the plugin shows a reminder when you click "+ Gamepad".
Export not appearing Check StarterGui/InputPro/ in the Explorer. Make sure you haven't filtered instance types.
Bootstrap not running Verify StarterPlayerScripts/InputProBootstrap exists and its ConfigName attribute matches your export name.

Runtime API Free

InputProService is a client-side module that reads exported configs and provides a clean API for input handling.

Installation

If you're using the plugin, the runtime is auto-installed on export. For manual installation:

  1. Download InputProService.luau from the GitHub repository
  2. Place it in ReplicatedStorage/InputProRuntime/InputProService
  3. Require it from a LocalScript
-- In a LocalScript (e.g. StarterPlayerScripts)
local InputProService = require(
    game:GetService("ReplicatedStorage")
    :WaitForChild("InputProRuntime")
    :WaitForChild("InputProService")
)

-- Initialize from an exported config
InputProService.Init("MyGameInput")

-- Or initialize for manual/code-only setup
InputProService.InitManual()

Initialization

InputProService.Init(configName: string)
Loads an exported config from PlayerGui/InputPro/<configName>. Caches all contexts and actions, wires touch bindings, and auto-enables the default context if one was set in the plugin. Must be called once before other API calls (or they yield until ready).
InputProService.InitManual()
Marks the service as initialized without loading a config folder. Use this when building input mappings entirely from code via the Manual Creation API.

Actions

InputProService.GetAction(name: string) -> InputAction?
Returns the InputAction instance by name. Supports both simple names ("Jump") and qualified names ("Gameplay/Jump"). Returns nil if not found.
InputProService.BindPressed(actionName: string, callback: () -> ()) -> Connection
Connects to the Pressed event of a Bool action. Fires when the action transitions from false to true (key pressed). Returns a Connection with a :Disconnect() method. Yields until Init completes.
InputProService.BindReleased(actionName: string, callback: () -> ()) -> Connection
Connects to the Released event of a Bool action. Fires when the action transitions from true to false (key released). Returns a Connection with a :Disconnect() method.
InputProService.BindStateChanged(actionName: string, callback: (value: any) -> ()) -> Connection
Connects to the StateChanged event of any action type. The callback receives the new state value: boolean for Bool, number for Direction1D, Vector2 for Direction2D, Vector3 for Direction3D.
-- Bool action: Jump
InputProService.BindPressed("Jump", function()
    character:FindFirstChildWhichIsA("Humanoid"):ChangeState(Enum.HumanoidStateType.Jumping)
end)

-- Direction2D action: Move (returns Vector2)
InputProService.BindStateChanged("Move", function(value)
    -- value is a Vector2 with X and Y components (-1 to 1)
    moveDirection = Vector3.new(value.X, 0, value.Y)
end)

Contexts

InputProService.GetContext(name: string) -> InputContext?
Returns the InputContext instance by name, or nil if not found.
InputProService.SetContext(contextName: string, exclusive: boolean?)
Enables a context. If exclusive is true, disables all other contexts first. Fires ContextChanged.
exclusive = true
Only this context is active (e.g. switching to a menu)
exclusive = false
Additive — enable alongside existing contexts (e.g. adding a vehicle overlay)
InputProService.DisableContext(contextName: string)
Disables a specific context. Fires ContextChanged.
InputProService.ContextChanged: Signal(contextName: string, enabled: boolean)
Event that fires whenever a context is enabled or disabled. Connect with :Connect(callback).
-- Exclusive switch: only Gameplay is active
InputProService.SetContext("Gameplay", true)

-- Additive: enable UI alongside Gameplay
InputProService.SetContext("UI", false)

-- Disable a specific context
InputProService.DisableContext("UI")

-- Listen for context changes
InputProService.ContextChanged:Connect(function(name, enabled)
    print(name, enabled and "ON" or "OFF")
end)

Device Detection

InputProService.GetActiveDevice() -> "Keyboard" | "Gamepad" | "Touch"
Returns the current active input device category. Updates automatically based on the player's last input type.
InputProService.ActiveDeviceChanged: Signal(device: string)
Fires when the player switches between input devices (e.g. from keyboard to gamepad). Use this to update UI prompts.

Input Images

InputProService.GetInputImage(actionName: string, device: string?) -> (imageId?, keyName?)
Returns the display info for an action's binding on the current (or specified) device.
  • Gamepad: returns (assetId, keyCodeName) — use the asset for button glyphs
  • Keyboard: returns (nil, displayName) — e.g. "E", "Space", "LShift"
  • Touch: returns (nil, nil)

Prompt Hints

InputProService.CreatePromptHint(actionName, parent, config?) -> PromptHint
Creates an auto-updating key badge on a UI element. Shows keyboard key name or gamepad button glyph. Automatically hides on touch devices. Updates when the player switches input device.
parent: GuiObject
The UI element to attach the badge to
config.Corner: string?
"TopLeft", "TopRight" (default), "BottomLeft", "BottomRight", "Center"
config.Size: number?
Badge size in pixels (default 32)
config.Offset: Vector2?
Position offset from the corner
-- Add a key hint badge to an inventory slot
local hint = InputProService.CreatePromptHint("OpenInventory", inventoryButton, {
    Corner = "BottomRight",
    Size = 28,
    Offset = Vector2.new(4, 4),
})

-- hint.Frame   — the UI frame instance
-- hint.Update() — force refresh
-- hint.Destroy() — cleanup
Custom template: If a PromptHintTemplate Frame exists in your exported config folder, it will be cloned instead of the default badge. The template must contain children named GlyphImage (ImageLabel) and KeyText (TextLabel).

Manual Setup No Plugin

You can use InputProService without the plugin by creating contexts, actions, and bindings in code.

Manual Creation API

InputProService.CreateContext(name, priority, sink) -> InputContext
Creates a new InputContext at runtime. Higher priority contexts process input first. If sink is true, input doesn't pass to lower-priority contexts.
InputProService.CreateAction(contextName, actionName, actionType) -> InputAction?
Creates a new InputAction under an existing context. Action types: Enum.InputActionType.Bool, Direction1D, Direction2D, Direction3D, ViewportPosition.
InputProService.CreateBinding(actionName, keyCode?, inputType?, config?) -> InputBinding?
Creates a new InputBinding under an existing action. Provide a KeyCode for keyboard/gamepad buttons. The optional config table accepts PressedThreshold and ReleasedThreshold for analog triggers (R2/L2 auto-default to 0.1).

Complete Example

-- ManualSetup.client.luau (place in StarterPlayerScripts)
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local InputProService = require(
    ReplicatedStorage:WaitForChild("InputProRuntime"):WaitForChild("InputProService")
)

-- Initialize without a config file
InputProService.InitManual()

-- Create contexts
InputProService.CreateContext("Gameplay", 1, false)
InputProService.CreateContext("UI", 2, true) -- higher priority, sinks input

-- Create actions
InputProService.CreateAction("Gameplay", "Jump", Enum.InputActionType.Bool)
InputProService.CreateAction("Gameplay", "Sprint", Enum.InputActionType.Bool)
InputProService.CreateAction("Gameplay", "Move", Enum.InputActionType.Direction2D)
InputProService.CreateAction("UI", "CloseMenu", Enum.InputActionType.Bool)

-- Bind keys
InputProService.CreateBinding("Jump", Enum.KeyCode.Space)
InputProService.CreateBinding("Jump", Enum.KeyCode.ButtonA)          -- Gamepad A
InputProService.CreateBinding("Sprint", Enum.KeyCode.LeftShift)
InputProService.CreateBinding("Sprint", Enum.KeyCode.ButtonL2, nil, {
    PressedThreshold = 0.3,
    ReleasedThreshold = 0.2,
})
InputProService.CreateBinding("Move", Enum.KeyCode.W)                -- Forward
InputProService.CreateBinding("Move", Enum.KeyCode.S)                -- Backward
InputProService.CreateBinding("Move", Enum.KeyCode.A)                -- Left
InputProService.CreateBinding("Move", Enum.KeyCode.D)                -- Right
InputProService.CreateBinding("Move", Enum.KeyCode.Thumbstick1)      -- Gamepad stick
InputProService.CreateBinding("CloseMenu", Enum.KeyCode.Escape)
InputProService.CreateBinding("CloseMenu", Enum.KeyCode.ButtonB)

-- Enable the default context
InputProService.SetContext("Gameplay", true)

-- Connect events
InputProService.BindPressed("Jump", function()
    print("Jump!")
end)

InputProService.BindPressed("Sprint", function()
    print("Sprint started")
end)

InputProService.BindReleased("Sprint", function()
    print("Sprint ended")
end)

InputProService.BindStateChanged("Move", function(value)
    print("Move direction:", value)
end)

-- Context switching example
InputProService.BindPressed("CloseMenu", function()
    InputProService.SetContext("Gameplay", true) -- exclusive: back to gameplay
end)

-- Device detection
InputProService.ActiveDeviceChanged:Connect(function(device)
    print("Switched to:", device) -- "Keyboard", "Gamepad", or "Touch"
end)

Action Types

Action types determine what kind of value an InputAction produces. Choose the type that matches your gameplay need.

Type Value Use Case Events
Bool boolean Jump, shoot, sprint, interact — any on/off action Pressed, Released, StateChanged
Direction1D number Throttle, zoom level, single-axis analog input StateChanged
Direction2D Vector2 Character movement, camera rotation, 2D stick input StateChanged
Direction3D Vector3 Flight controls, 6DOF movement (up/down + left/right + forward/back) StateChanged
ViewportPosition Vector2 Mouse cursor position, touch point, screen-space raycasting StateChanged
Bool is the only type that fires Pressed and Released events. For all other types, use BindStateChanged to receive continuous value updates.