Controllers
The Steam Frame Controllers have the same capabilities as your standard console gaming controllers plus touch states, grip buttons, capacitive finger tracking, and 6-DOF spatial tracking. This gives you many more options on how to let players interact with your titles. It also allows users to play non-VR content on the headset without a separate device.

VR Compatibility
While you will always get a better user experience by tailoring your title to the Frame Controllers specifically, our controller compatibility modes have become quite robust. SteamVR will always use a Frame Controller binding by default but in the case one is not available it will first look for a
OpenXR Generic Controller binding, then an
Oculus Touch binding, and remap those to the Frame Controller layout.
- The menu button on Oculus Touch will map to the View button at the top of the Steam Frame Controller.
- The top button of the Oculus Touch controllers (B / Y) will map to all three top buttons of the Steam Frame Controllers (Dpad Left, Dpad Up, Dpad Right / X, Y, B).
Non-VR Compatibility
When playing Non-VR titles locally or via streaming the Frame Controllers take full advantage of
Steam Input. Depending on the game and the configured bindings it will show up as a generic gamepad or keyboard/mouse.
OpenXR Interaction Profile
You'll find an OpenXR Interaction Profile included in each Game Engine plugin (or in the case of Godot in the engine itself). If you have your own custom engine you can add support via our extension detailed on the
steamframe/engines/custom documentation page.
To enable the interaction profile you must request to use the extension:
XR_VALVE_frame_controller_interaction
Then you can suggest bindings for the interaction profile path:
/interaction_profiles/valve/frame_controller_valve
You can use the following paths:
"/user/hand/right/input/a/touch", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/right/input/a/click", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/right/input/b/touch", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/right/input/b/click", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/right/input/x/touch", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/right/input/x/click", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/right/input/y/touch", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/right/input/y/click", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/right/input/menu/touch", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/right/input/menu/click", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/right/input/system/touch", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/right/input/system/click", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/right/input/bumper/touch", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/right/input/bumper/click", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/right/input/squeeze/touch", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/right/input/squeeze/click", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/right/input/squeeze/value", XR_ACTION_TYPE_FLOAT_INPUT
"/user/hand/right/input/trigger/touch", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/right/input/trigger/click", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/right/input/trigger/value", XR_ACTION_TYPE_FLOAT_INPUT
"/user/hand/right/input/thumbstick/touch", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/right/input/thumbstick/click", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/right/input/thumbstick/x", XR_ACTION_TYPE_FLOAT_INPUT
"/user/hand/right/input/thumbstick/y", XR_ACTION_TYPE_FLOAT_INPUT
"/user/hand/right/input/thumbstick", XR_ACTION_TYPE_VECTOR2F_INPUT
"/user/hand/right/input/grip/pose", XR_ACTION_TYPE_POSE_INPUT
"/user/hand/right/input/aim/pose", XR_ACTION_TYPE_POSE_INPUT
"/user/hand/right/output/haptic", XR_ACTION_TYPE_VIBRATION_OUTPUT
"/user/hand/left/input/dpad_up/touch", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/left/input/dpad_up/click", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/left/input/dpad_left/touch", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/left/input/dpad_left/click", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/left/input/dpad_down/touch", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/left/input/dpad_down/click", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/left/input/dpad_right/touch", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/left/input/dpad_right/click", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/left/input/view/touch", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/left/input/view/click", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/left/input/system/touch", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/left/input/system/click", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/left/input/bumper/touch", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/left/input/bumper/click", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/left/input/squeeze/touch", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/left/input/squeeze/click", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/left/input/squeeze/value", XR_ACTION_TYPE_FLOAT_INPUT
"/user/hand/left/input/trigger/touch", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/left/input/trigger/click", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/left/input/trigger/value", XR_ACTION_TYPE_FLOAT_INPUT
"/user/hand/left/input/thumbstick/touch", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/left/input/thumbstick/click", XR_ACTION_TYPE_BOOLEAN_INPUT
"/user/hand/left/input/thumbstick/x", XR_ACTION_TYPE_FLOAT_INPUT
"/user/hand/left/input/thumbstick/y", XR_ACTION_TYPE_FLOAT_INPUT
"/user/hand/left/input/thumbstick", XR_ACTION_TYPE_VECTOR2F_INPUT
"/user/hand/left/input/grip/pose", XR_ACTION_TYPE_POSE_INPUT
"/user/hand/left/input/aim/pose", XR_ACTION_TYPE_POSE_INPUT
"/user/hand/left/output/haptic", XR_ACTION_TYPE_VIBRATION_OUTPUT
OpenVR Bindings
You can bind to the Steam Frame Controllers like you would any other OpenVR controller type. Its
controller_type is
"frame_controller". They support finger tracking via the
Skeletal Input API and all of the controls listed above. You can create bindings for it in the SteamVR Binding UI.
Left Controller
"/user/hand/left/input/dpad_up": click, touch
"/user/hand/left/input/dpad_down": click, touch
"/user/hand/left/input/dpad_left": click, touch
"/user/hand/left/input/dpad_right": click, touch
"/user/hand/left/input/view": click, touch
"/user/hand/left/input/bumper": click, touch
"/user/hand/left/input/trigger": click, touch, value 1d
"/user/hand/left/input/grip": click, touch, value 1d
"/user/hand/left/input/thumbstick": click, touch, value 2d
"/user/hand/left/input/system": click, touch
Right Controller
"/user/hand/right/input/a": click, touch
"/user/hand/right/input/b": click, touch
"/user/hand/right/input/x": click, touch
"/user/hand/right/input/y": click, touch
"/user/hand/right/input/menu": click, touch
"/user/hand/right/input/bumper": click, touch
"/user/hand/right/input/trigger": click, touch, value 1d
"/user/hand/right/input/grip": click, touch, value 1d
"/user/hand/right/input/thumbstick": click, touch, value 2d
"/user/hand/right/input/system": click, touch
HMD
It is unlikely that you will have a reason to need the hmd name, but if you do, its
controller_type is
"frame_hmd". Be sure NOT to use this for determining the type of controllers the user currently has. Many users choose to mix and match hmds and controllers.
Eye Tracking
OpenXR
Our current support for eye tracking uses the
XR_EXT_eye_gaze_interaction OpenXR extension. You can find details about that on the
OpenXR spec site here.
OpenVR
OpenVR has two functions in IVRInput
GetEyeTrackingDataRelativeToNow and
GetEyeTrackingDataForNextFrame. To use those you will need to create a new action of type
"eyetracking" to pass into the functions.