Gamepad

W3C Editor's Draft

More details about this document
This version:
https://w3c.github.io/gamepad/
Latest published version:
https://www.w3.org/TR/gamepad/
Latest editor's draft:
https://w3c.github.io/gamepad/
History:
https://www.w3.org/standards/history/gamepad/
Commit history
Test suite:
https://wpt.live/gamepad/
Implementation report:
https://wpt.fyi/results/gamepad
Editors:
Steve Agoston (Sony)
Matt Reynolds (Google)
Former editors:
James Hollyer (Google)
Brandon Jones (Google)
Scott Graham (Google)
Ted Mielczarek (Mozilla)
Feedback:
GitHub w3c/gamepad (pull requests, new issue, open issues)
Browser support:
caniuse.com

Abstract

The Gamepad specification defines a low-level interface that represents gamepad devices.

Status of This Document

This is a preview

Do not attempt to implement this version of the specification. Do not reference this version as authoritative in any way. Instead, see https://w3c.github.io/gamepad/ for the Editor's draft.

This section describes the status of this document at the time of its publication. A list of current W3C publications and the latest revision of this technical report can be found in the W3C standards and drafts index at https://www.w3.org/TR/.

This is a work in progress.

This document was published by the Web Applications Working Group as an Editor's Draft.

Publication as an Editor's Draft does not imply endorsement by W3C and its Members.

This is a draft document and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.

This document was produced by a group operating under the W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.

This document is governed by the 03 November 2023 W3C Process Document.

1. Introduction

This section is non-normative.

Some user agents have connected gamepad devices. These devices are desirable and suited to input for gaming applications, and for "10 foot" user interfaces (presentations, media viewers).

Currently, the only way for a gamepad to be used as input would be to emulate mouse or keyboard events, however this would lose information and require additional software outside of the user agent to accomplish emulation.

Meanwhile, native applications are capable of accessing these devices via system APIs.

The Gamepad API provides a solution to this problem by specifying interfaces that allow web applications to directly act on gamepad data.

2. Scope

Interfacing with external devices designed to control games has the potential to become large and intractable if approached in full generality. In this specification we explicitly choose to narrow scope to provide a useful subset of functionality that can be widely implemented and broadly useful.

Specifically, we choose to only support the functionality required to support gamepads. Support for gamepads requires two input types: buttons and axes. Both buttons and axes are reported as analog values, buttons ranging from [0 .. 1], and axes ranging from [-1 .. 1].

While the primary goal is support for gamepad devices, supporting these two types of analog inputs allows support for other similar devices common to current gaming systems including joysticks, driving wheels, pedals, and accelerometers. As such, the name "gamepad" is exemplary rather than trying to be a generic name for the entire set of devices addressed by this specification.

We specifically exclude support for more complex devices that may also be used in some gaming contexts, including those that that do motion sensing, depth sensing, video analysis, gesture recognition, and so on.

3. Model

A gamepad is a collection of input controls and output controls. An input control has a collection of input values that may update over time. Input controls include the buttons, triggers, joysticks, thumbsticks, and touch surfaces of the gamepad. An output control is a feature that changes the behavior of the gamepad to provide feedback to the user interacting with the gamepad. Output controls include the haptic actuators of the gamepad. A gamepad is available if the user agent can read the current state of its input controls. A gamepad that is not available is unavailable. The input controls and output controls for a gamepad cannot change while the gamepad is available.

The user agent is responsible for:

A gamepad has a gamepad identifier string, a human-readable string that identifies the brand or style of gamepad. The content is decided by the user agent.

3.1 Input control layout

A gamepad may have an input control layout that describes the position, orientation and type of each input control on the gamepad. The user agent is responsible for recognizing when a gamepad corresponds with a standard layout, meaning the gamepad has an input control layout that enables it to be used interchangeably with other gamepads that correspond with the same standard layout. The user agent SHOULD consider a layout to correspond with a standard layout if its input controls have approximately the same relative positions and orientations as input controls described in the standard layout.

The user agent typically cannot directly inspect the input control layout for a gamepad and MAY use heuristics to decide the layout. The user agent SHOULD consider the device identifiers when deciding whether a gamepad corresponds with a standard layout. If the system assigns a label to each input control and the labels imply a particular layout then the user agent SHOULD consider the gamepad to have that layout. When there is a standard model and an accessible model with the same input controls, the user agent SHOULD consider the accessible model to have the same input control layout as the standard model.

Note
An accessible gamepad model is a gamepad where the manufacturer's intent is to provide a swap-in replacement for a gamepad with a standard layout. For example, Xbox Adaptive Controller and PlayStation Access Controller are accessible gamepad models. Xbox Wireless Controller and DualSense are the corresponding standard models.

3.2 Input values

Each input control has one or more associated input values, which are numerical values that represent the current state of the control. Input values can update at any time. The user agent is responsible for detecting when input values have updated and SHOULD try to minimize the delay between the update and when the updated values are read.

Reading an input value returns its logical value, an unscaled numerical representation of the current state. An input value also has a logical minimum and logical maximum which define the minimum and maximum in-bounds logical values.

An input value may have an associated HID usage identifier, a 32-bit value that identifies the type of data represented by the input value. A HID usage does not precisely describe the input control layout, but by convention many gamepads with similar layouts use similar usages. The user agent SHOULD rely on conventions around HID usage identifiers when deciding the input control layout.

3.3 Axes

A gamepad may have axis inputs. An axis is an input value that represents the current displacement of the control from a reference position.

A gamepad has an axis list which is a list containing all the axis inputs for the gamepad in some order determined by the user agent.

An input control may be designed to automatically return an axis to a center position when the user stops interacting with the input control. If so, the axis has a preferred axis state. An axis with a preferred axis state may also have an additional input value, the center position value, which is the logical value when the axis is centered.

3.4 Buttons

A gamepad may have button inputs. A button is an input control that can be pressed to activate. A gamepad has a button list which is a list containing all the button inputs for the gamepad in some order determined by the user agent.

An input control may be designed to automatically return a button to an unpressed state when the user stops interacting with the input control. If so, the button has a preferred button state.

A button may have a digital switch to indicate when the button is activated. If so, the button has an additional input value, the digital button value, that is true when the button is activated and false otherwise.

A button may have an analog sensor enabling the button to report the degree to which the button is activated. If so, the button has:

A button may be capable of detecting touch. If so, the button has an additional input value, the button touch value, that is true when the button is touched and false otherwise.

3.5 Touch surfaces

A gamepad may have touch surfaces. A touch surface is an input control that provides 2D position data representing points of contact. A gamepad has a touch surface list which is a list containing the touch surfaces for the gamepad. The list is ordered such that touch surfaces closer to the left side of the gamepad appear closer to the start of the list.

A touch surface has an active touch point list input value, a list of zero or more touch points representing the points of contact currently detected by the sensor. A touch point represents a single point of contact at a single point in time. A touch point has a touch x coordinate and a touch y coordinate representing the position in the touch surface's coordinate system. If a touch surface is on the top, bottom, front, or back side of the gamepad then the touch x coordinate is measured along the left-right axis, otherwise it is measured along the top-bottom axis. The touch y coordinate is measured along the perpendicular axis.

If a touch surface is on the left or right side of a gamepad then neither of its dimensions will align with the horizontal axis.

A touch surface may have surface dimension input values. The surface width and surface height input values are the dimensions of the touch surface in the same units as the touch x coordinate and touch y coordinate. A touch surface has both dimension values or neither value.

A touch point may be a new contact point or a continuation of an earlier contact. A touch point is part of an existing active touch point if the user agent identifies that it is a continuation of a touch point represented by an earlier GamepadTouch. The active touch point id for a touch point that is part of an existing active touch point is the touchId of the earlier GamepadTouch.

3.6 Output controls

A gamepad may have haptic actuators. A haptic actuator is an output control capable of moving the gamepad in a way that can be felt by the user. A haptic actuator can be used to generate a haptic effect that provides feedback to the user. Vibrations from multiple actuators are combined to generate more complex effects. The user agent is responsible for commanding haptic actuators to play and stop haptic effects on available gamepads.

A gamepad may have a vibration actuator which is a haptic actuator capable of playing a haptic effect to vibrate the whole gamepad.

A haptic actuator has a list of supported effect types containing one or more GamepadHapticEffectType values, which cannot change while the gamepad is available.

4. Gamepad Interface

This interface represents a gamepad.

WebIDL[Exposed=Window]
interface Gamepad {
  readonly attribute DOMString id;
  readonly attribute long index;
  readonly attribute boolean connected;
  readonly attribute DOMHighResTimeStamp timestamp;
  readonly attribute GamepadMappingType mapping;
  readonly attribute FrozenArray<double> axes;
  readonly attribute FrozenArray<GamepadButton> buttons;
  readonly attribute FrozenArray<GamepadTouch> touches;
  [SameObject] readonly attribute GamepadHapticActuator vibrationActuator;
};

The algorithms used to communicate with the system typically complete asynchronously, queuing work on the gamepad task source.

Instances of Gamepad are created with the internal slots described in the following table:

Internal slot Initial value Description (non-normative)
[[connected]] true A flag indicating that the gamepad is available
[[timestamp]] undefined The last time data for this Gamepad was updated
[[axes]] An empty sequence A sequence of double values representing the current state of each axis
[[buttons]] An empty sequence A sequence of GamepadButton objects representing the current state of each button
[[exposed]] false A flag indicating that the Gamepad object has been exposed to script
[[axisMapping]] An empty ordered map Mapping from indices in the axis list to indices in Gamepad.axes
[[buttonMapping]] An empty ordered map Mapping from indices in the button list to indices in Gamepad.buttons
[[touches]] An empty list Holds the list of GamepadTouch objects representing all active touch points for the gamepad. If the gamepad has no touch surfaces, then the list will remain empty.
[[nextTouchId]] 0 touchId value to use for the next touch point that is not part of an existing active touch point
[[vibrationActuator]] undefined The gamepad's vibration actuator
id attribute

The gamepad's gamepad identifier string.

The exact format of the id string is left unspecified. It is RECOMMENDED that the user agent select a string that identifies the product without uniquely identifying the device. For example, a USB gamepad may be identified by its idVendor and idProduct values. Unique identifiers like serial numbers or Bluetooth device addresses MUST NOT be included in the id string.

index attribute
The index of the Gamepad object in Navigator.[[gamepads]].
connected attribute

Indicates whether the gamepad is available.

The connected getter steps are:

  1. Return this.[[connected]].
timestamp attribute

The timestamp allows the author to determine when the input values for this Gamepad updated.

Warning

User agents SHOULD set a minimum resolution of the timestamp attribute to 5 microseconds, following [HR-TIME]'s clock resolution recommendation.

The timestamp getter steps are:

  1. Return this.[[timestamp]].
mapping attribute

Indicates whether inputs in the axis list and button list are reordered when setting Gamepad.axes and Gamepad.buttons. If the user agent has knowledge of the input control layout, then it SHOULD indicate that a mapping is in use by setting mapping to the corresponding GamepadMappingType value.

To select a mapping for a gamepad, run the following steps:

  1. If the input control layout corresponds with the Standard Gamepad layout, then return "standard".
  2. Return "".
axes attribute

Array of values for all axis inputs of the gamepad normalized to the range [-1 .. 1]. If the gamepad is perpendicular to the ground with the thumbsticks pointing up, -1 SHOULD correspond to "forward" or "left", and 1 SHOULD correspond to "backward" or "right". Axis inputs with input values from a thumbstick, joystick, or another input control that reports X and Y values SHOULD appear next to each other in the axes array, X then Y. It is RECOMMENDED that axis inputs appear in decreasing order of importance, such that element 0 and 1 typically represent the X and Y axes of a thumbstick or joystick. The same object MUST be returned until the user agent needs to return different values (or values in a different order).

The axes getter steps are:

  1. Return this.[[axes]].
buttons attribute

Array of button states for all buttons of the gamepad. It is RECOMMENDED that buttons appear in decreasing importance such that the primary button, secondary button, tertiary button, and so on appear as elements 0, 1, 2, ... in the buttons array. The same object MUST be returned until the user agent needs to return different values (or values in a different order).

The buttons getter steps are:

  1. Return this.[[buttons]].
touches attribute

A list of GamepadTouch objects representing the current contact points across all touch surfaces.

The touches getter steps are:

  1. Return this.[[touches]].
vibrationActuator attribute

A GamepadHapticActuator object that represents the gamepad's vibration actuator.

The vibrationActuator getter steps are:

  1. Return this.[[vibrationActuator]].

4.1 Receiving inputs

When an available gamepad has updated input values, run the following steps for each top-level traversable traversable of the user agent's top-level traversable set:

  1. For each navigable of traversable's active document's inclusive descendant navigables:
    1. Let window be navigable's active window.
    2. Let gamepad be the Gamepad in window.navigator.[[gamepads]] representing the gamepad that has updated input values.
    3. Queue a global task on the gamepad task source given window to update gamepad state for gamepad.

To update gamepad state for gamepad, run the following steps:

  1. If gamepad's relevant global object is not a Window, abort these steps.
  2. Let window be gamepad's relevant global object.
  3. Let now be the current high resolution time given window.
  4. Set gamepad.[[timestamp]] to now.
  5. Run the steps to map and normalize axes for gamepad.
  6. Run the steps to map and normalize buttons for gamepad.
  7. Run the steps to record touches for gamepad.
  8. If window.navigator.[[hasGamepadGesture]] is false and gamepad contains a gamepad user gesture:
    1. Set window.navigator.[[hasGamepadGesture]] to true.
    2. For each connectedGamepad of window.navigator.[[gamepads]]:
      1. If connectedGamepad is not equal to null:
        1. Set connectedGamepad.[[exposed]] to true.
        2. Set connectedGamepad.[[timestamp]] to now.
        3. Let document be window's associated Document.
        4. If document is fully active, then queue a global task on the gamepad task source given window to fire an event named gamepadconnected at window using GamepadEvent with its gamepad attribute initialized to connectedGamepad.

To map and normalize axes for gamepad, run the following steps:

  1. Let rawGamepad be the gamepad represented by gamepad.
  2. For each rawAxisIndex of the indices of the axis list of rawGamepad:
    1. Let rawAxis be the axis at index rawAxisIndex of the axis list.
    2. Let mappedIndex be gamepad.[[axisMapping]][rawAxisIndex].
    3. Set gamepad.[[axes]][mappedIndex] to the current normalized axis value for rawAxis.

To compute the normalized axis value for an axis rawAxis:

  1. Let logicalValue be the current logical value for rawAxis.
  2. Let logicalMinimum be the logical minimum for rawAxis.
  3. Let logicalMaximum be the logical maximum for rawAxis.
  4. If logicalValue is less than logicalMinimum, set logicalValue to logicalMinimum.
  5. If logicalValue is greater than logicalMaximum, set logicalValue to logicalMaximum.
  6. If rawAxis has a center position value:

    1. Let center be the center position value for rawAxis.
    2. If logicalValue is greater than center, return (logicalValuecenter) / (logicalMaximumcenter).
    3. Return (logicalValuecenter) / (centerlogicalMinimum).
  7. Return 2 (logicalValuelogicalMinimum) / (logicalMaximumlogicalMinimum) − 1.

To map and normalize buttons for gamepad, run the following steps:

  1. Let rawGamepad be the gamepad represented by gamepad.
  2. For each rawButtonIndex of the indices of the rawGamepad's button list:
    1. Let rawButton be the button at index rawButtonIndex of rawGamepad's button list.
    2. Let mappedIndex be gamepad.[[buttonMapping]][rawButtonIndex].
    3. Set gamepad.[[buttons]][mappedIndex].[[value]] to the current normalized button value for rawButton.
    4. If rawButton has a digital button value, set button.[[pressed]] to the digital button value.

      Otherwise, set button.[[pressed]] to true if the current normalized button value for rawButton is above the button press threshold or false if it is not above the threshold.

    5. If rawButton has a button touch value, set button.[[touched]] to rawButton's button touch value.

      Otherwise, set button.[[touched]] to button.[[pressed]].

To compute the normalized button value for a button rawButton:

  1. If rawButton has an analog button value:
    1. Let logicalMinimum be the logical minimum for rawButton.
    2. Let logicalMaximum be the logical maximum for rawButton.
    3. Let logicalValue be the current analog button value for rawButton.
    4. If logicalValue is less than logicalMinimum, set logicalValue to logicalMinimum.
    5. If logicalValue is greater than logicalMaximum, set logicalValue to logicalMaximum.
    6. Return (logicalValuelogicalMinimum) / (logicalMaximumlogicalMinimum).
  2. If rawButton has a digital button value and its digital button value is true, return 1.
  3. Return 0.

To record touches for gamepad, run the following steps:

  1. Empty gamepad.[[touches]].
  2. For each surfaceId of the indices of the touch surface list of the gamepad represented by gamepad:
    1. Let surface be the touch surface at index surfaceId in the touch surface list.
    2. If surface has surface dimensions, then set touch.surfaceDimensions to a DOMRectReadOnly with width initialized to the surface width and height initialized to the surface height.
    3. For each point in surface's active touch point list:
      1. Let touch be a newly created GamepadTouch object.
      2. Set touch.surfaceId to be surfaceId.
      3. If point is part of an existing active touch point, set touch.touchId to the active touch point id.

        Otherwise, set touch.touchId to gamepad.[[nextTouchId]] and increment gamepad.[[nextTouchId]].

        Note: Active touch point ids are relative to the Gamepad

        If the Gamepad has multiple touch surfaces the touchId will be unique across surfaces.

      4. Set touch.position to a new DOMPointReadOnly with x initialized to the touch x coordinate normalized to [-1 .. 1] where -1 is the leftmost coordinate and 1 is the rightmost coordinate and y initialized to the touch y coordinate normalized to [-1 .. 1] where -1 is the topmost coordinate and 1 is the bottommost coordinate.
        Note: Possible implementation (if surfaceDimensions are available)

        x = (2.0 * touchData.x / surfaceDimensions.width) - 1
        y = (2.0 * touchData.y / surfaceDimensions.height) - 1

      5. Append touch to gamepad.[[touches]].

4.2 Constructing a Gamepad

A new Gamepad representing an available gamepad for a window is constructed by performing the following steps:

  1. Let gamepad be a newly created Gamepad instance:
    1. Initialize gamepad's id attribute to the gamepad identifier string for the gamepad.
    2. Initialize gamepad's index attribute to the result of selecting an unused gamepad index given window.
    3. Initialize gamepad's mapping attribute to the result of selecting a mapping for the gamepad.
  2. Set gamepad.[[timestamp]] to the current high resolution time given window.
  3. Set gamepad.[[axes]] to the result of initializing axes for gamepad.
  4. Set gamepad.[[buttons]] to the result of initializing buttons for gamepad.
  5. If the gamepad has a vibration actuator, set gamepad.[[vibrationActuator]] to the result of constructing a GamepadHapticActuator for the gamepad's vibration actuator.
  6. Return gamepad.

To select an unused gamepad index given window, run the following steps:

  1. For each gamepadIndex of the indices of window.navigator.[[gamepads]]:
    1. If window.navigator.[[gamepads]][gamepadIndex] is null, then return gamepadIndex.
  2. Append null to window.navigator.[[gamepads]].
  3. Return the size of window.navigator.[[gamepads]] − 1.

To initialize axes for gamepad, run the following steps:

  1. For each rawInputIndex of the indices of the axis list of the gamepad represented by gamepad:
    1. Let rawAxis be the axis at index rawInputIndex of the axis list.
    2. If rawAxis represents a Standard Gamepad axis:
      1. Let canonicalIndex be the canonical index for rawAxis.
      2. If mappedIndexList contains canonicalIndex, then append rawInputIndex to unmappedInputList.

        Otherwise:

        1. Set gamepad.[[axisMapping]][rawInputIndex] to canonicalIndex.
        2. Append canonicalIndex to mappedIndexList.
        3. If canonicalIndex + 1 is greater than axesSize, then set axesSize to canonicalIndex + 1.

      Otherwise, append rawInputIndex to unmappedInputList.

  2. Let axisIndex be 0.
  3. For each rawInputIndex of unmappedInputList:
    1. While mappedIndexList contains axisIndex:
      1. Increment axisIndex.
    2. Set gamepad.[[axisMapping]][rawInputIndex] to axisIndex.
    3. Append axisIndex to mappedIndexList.
    4. If axisIndex + 1 is greater than axesSize, then set axesSize to axisIndex + 1.
  4. Let axes be an empty list.
  5. For each axisIndex of the range from 0 to axesSize − 1, append 0 to axes.
  6. Return axes.

To initialize buttons for gamepad, run the following steps:

  1. For each rawInputIndex of the indices of the button list of the gamepad represented by gamepad:
    1. Let rawButton be the button at index rawInputIndex in the button list.
    2. If rawButton represents a Standard Gamepad button:
      1. Let canonicalIndex be the canonical index for rawButton.
      2. If mappedIndexList contains canonicalIndex, then append rawInputIndex to unmappedInputList.

        Otherwise:

        1. Set gamepad.[[buttonMapping]][rawInputIndex] to canonicalIndex.
        2. Append canonicalIndex to mappedIndexList.
        3. If canonicalIndex + 1 is greater than buttonsSize, then set buttonsSize to canonicalIndex + 1.

      Otherwise, append rawInputIndex to unmappedInputList.

    3. Increment rawInputIndex.
  2. Let buttonIndex be 0.
  3. For each rawInputIndex of unmappedInputList:
    1. While mappedIndexList contains buttonIndex:
      1. Increment buttonIndex.
    2. Set gamepad.[[buttonMapping]][rawInputIndex] to buttonIndex.
    3. Append buttonIndex to mappedIndexList.
    4. If buttonIndex + 1 is greater than buttonsSize, then set buttonsSize to buttonIndex + 1.
  4. Let buttons be an empty list.
  5. For each buttonIndex of the range from 0 to buttonsSize − 1, append a new GamepadButton to buttons.
  6. Return buttons.

5. GamepadButton Interface

This interface defines the state of an individual button on a gamepad at a single point in time.

WebIDL[Exposed=Window]
interface GamepadButton {
  readonly attribute boolean pressed;
  readonly attribute boolean touched;
  readonly attribute double value;
};

Instances of GamepadButton are created with the internal slots described in the following table:

Internal slot Initial value Description (non-normative)
[[pressed]] false A flag indicating that the button is pressed
[[touched]] false A flag indicating that the button is touched
[[value]] 0 A double in the range [0 .. 1] representing the degree to which the button is activated
pressed attribute

Indicates whether the button is pressed. This property MUST be true if the button is currently pressed, and false if it is not pressed. For buttons which do not have a digital switch to indicate a pure pressed or released state, the user agent MUST choose a button press threshold to indicate the button as pressed when its value is above a certain amount. If the platform API gives a recommended value, the user agent SHOULD use that. In other cases, the user agent SHOULD choose some other reasonable value.

The pressed getter steps are:

  1. Return this.[[pressed]].
touched attribute

Indicates whether the button is detecting any interaction, including interactions that do not cause the button to activate. If the button has a button touch value, touched is the button touch value. If the button does not have a button touch value but has an analog button value, touched is true if the current analog button value is greater than the logical minimum, otherwise false. If the button does not have a button touch value or an analog button value, touched mirrors pressed.

The touched getter steps are:

  1. Return this.[[touched]].
value attribute

A value in the range [0 .. 1] representing the degree to which the button is activated. For buttons that have an analog button value, this property MUST be the analog button value clamped to the logical minimum and logical maximum and then linearly normalized to the range [0 .. 1]. 0 MUST mean fully unpressed, and 1 MUST mean fully pressed. For buttons without an analog button value, only the values 0 and 1 for fully unpressed and fully pressed respectively, MUST be provided.

The value getter steps are:

  1. Return this.[[value]].

6. GamepadTouch Interface

This interface represents a touch point on a touch surface. The object consists of a touchId that uniquely identifies the touch point from the time the input medium (e.g. finger, stylus, etc) makes contact with the touch surface, up to the time the input medium is no longer making contact with the surface.

WebIDLdictionary GamepadTouch {
  unsigned long touchId;
  octet surfaceId;
  DOMPointReadOnly position;
  DOMRectReadOnly? surfaceDimensions;
};
touchId attribute
Identifier shared by touch points that are part of an existing active touch point. Range is [0 .. 4294967295].
surfaceId attribute
Identifier of the touch surface that generated the touch point.
position attribute
A DOMPointReadOnly which holds the normalized touch x coordinate and touch y coordinate of the touch point as x and y. The z and w values are currently unused. The range of each coordinate is normalized to [-1 .. 1]. Along the x-axis, -1 references the leftmost coordinate and 1 references the rightmost coordinate. Along the y-axis, -1 references the topmost coordinate and 1 references the bottommost coordinate.
surfaceDimensions attribute
A DOMRectReadOnly initialized with the surface width and surface height of the touch surface as width and height, if the touch surface has surface dimensions. Otherwise, null.

7. GamepadMappingType Enum

This enum defines the mapping types for mapping from a gamepad to a Gamepad.

WebIDLenum GamepadMappingType {
  "",
  "standard",
  "xr-standard",
};
""
The empty string indicates that the gamepad does not have an input control layout, or the user agent does not know the gamepad's input control layout, or the layout corresponds with none of the standard layouts. The axes and buttons arrays are not reordered.
"standard"
The gamepad's input control layout corresponds with the Standard Gamepad layout. The axes and buttons arrays are reordered.
"xr-standard"
The gamepad's controls have been mapped to the "xr-standard" gamepad mapping. This mapping is reserved for use by the WebXR Gamepads Module - Level 1. Gamepad objects returned by getGamepads() MUST NOT report a mapping of "xr-standard".

8. GamepadHapticActuator Interface

This interface represents a haptic actuator capable of playing haptic effects that move the gamepad in a way that can be felt by the user.

WebIDL[Exposed=Window]
interface GamepadHapticActuator {
  [SameObject] readonly attribute FrozenArray<GamepadHapticEffectType> effects;
  Promise<GamepadHapticsResult> playEffect(
      GamepadHapticEffectType type,
      optional GamepadEffectParameters params = {}
  );
  Promise<GamepadHapticsResult> reset();
};

Instances of GamepadHapticActuator are created with the internal slots described in the following table:

Internal slot Initial value Description
[[effects]] An empty list of GamepadHapticEffectType. Represents the effects supported by the actuator.
[[playingEffectPromise]] null The Promise to play some effect, or null if no effect is playing.
effects attribute

Array of GamepadHapticEffectType values representing all the types of haptic effects that the GamepadHapticActuator supports. This property lists the GamepadHapticEffectType values that the actuator supports, unless the user agent does not support playing effects of that type.

The effects getter steps are:

  1. Return this.[[effects]].
playEffect() method

The playEffect() method steps, called with GamepadHapticEffectType type and GamepadEffectParameters params, are:

  1. If params does not describe a valid effect of type type, return a promise rejected with a TypeError.
  2. If the current global object is not a Window, return a promise rejected with an "InvalidStateError" DOMException.
  3. Let window be the current global object.
  4. Let document be the window's associated Document.
  5. If document is not fully active or document's visibility state is "hidden", return a promise rejected with an "InvalidStateError" DOMException.
  6. If this.[[playingEffectPromise]] is not null:
    1. Let effectPromise be this.[[playingEffectPromise]].
    2. Set this.[[playingEffectPromise]] to null.
    3. Queue a global task on the gamepad task source with window to resolve effectPromise with "preempted".
  7. If this GamepadHapticActuator cannot play effects with type type, return a promise rejected with reason NotSupportedError.
  8. Let [[playingEffectPromise]] be a new promise.
  9. Let playEffectTimestamp be the current high resolution time given window.
  10. Do the following steps in parallel:
    1. Play a haptic effect on the haptic actuator represented by this with type, params, and the playEffectTimestamp.
    2. When the effect completes, if this.[[playingEffectPromise]] is not null, queue a global task on the gamepad task source with window to run the following steps:
      1. If this.[[playingEffectPromise]] is null, abort these steps.
      2. Resolve this.[[playingEffectPromise]] with "complete".
      3. Set this.[[playingEffectPromise]] to null.
  11. Return [[playingEffectPromise]].
reset() method

The reset() method steps are:

  1. If the current global object is not a Window, return a promise rejected with an "InvalidStateError" DOMException.
  2. Let window be the current global object.
  3. Let document be window's associated Document.
  4. If document is not fully active or document's visibility state is "hidden", return a promise rejected with an "InvalidStateError" DOMException.
  5. Let resetResultPromise be a new promise.
  6. If this.[[playingEffectPromise]] is not null, do the following steps in parallel:
    1. Let effectPromise be this.[[playingEffectPromise]].
    2. Stop haptic effects on the haptic actuator represented by this GamepadHapticActuator.
    3. If the effect has been successfully stopped, do:
      1. If effectPromise and this.[[playingEffectPromise]] are still the same, set this.[[playingEffectPromise]] to null.
      2. Queue a global task on gamepad task source with window to resolve effectPromise with "preempted".
    4. Resolve resetResultPromise with "complete"
  7. Return resetResultPromise.

A GamepadHapticActuator can play effects with type type if type can be found in the [[effects]] list.

To check if an effect with GamepadHapticEffectType type and GamepadEffectParameters params describes a valid effect, run the following steps:

  1. Given the value of GamepadHapticEffectType type, switch on:
    "dual-rumble"
    If params does not describe a valid dual-rumble effect, return false.
    "trigger-rumble"
    If params does not describe a valid trigger-rumble effect, return false.
  2. Return true

To play a haptic effect on a haptic actuator, the user agent MUST command the haptic actuator to render a haptic effect of type and try to make it use the provided params. The user agent SHOULD use the provided playEffectTimestamp for more precise playback timing when params.startDelay is not 0. The user agent MAY modify the effect to increase compatibility. For example, an effect intended for a rumble motor may be transformed into a waveform-based effect for a device that supports waveform haptics but lacks rumble motors.

To stop haptic effects on a haptic actuator, the user agent MUST command the haptic actuator to abort any effects currently being played on that actuator. If a haptic effect was interrupted, the actuator SHOULD return to a motionless state as quickly as possible.

8.1 Handling visibility change

When the document's visibility state becomes "hidden", run these steps:

  1. Let window be document's relevant global object.
  2. For each gamepad of window.navigator.[[gamepads]]:
    1. If gamepad is null, continue.
    2. If gamepad.vibrationActuator is null, continue.
    3. If gamepad.vibrationActuator.[[playingEffectPromise]] is null, continue.
    4. Queue a global task on the gamepad task source with window to run the following steps:
      1. If gamepad.vibrationActuator.[[playingEffectPromise]] is null, abort these steps.
      2. Resolve gamepad.vibrationActuator.[[playingEffectPromise]] with "preempted".
      3. Set gamepad.vibrationActuator.[[playingEffectPromise]] to null.
    5. Stop haptic effects on the haptic actuator represented by gamepad.vibrationActuator.

8.2 Constructing a GamepadHapticActuator

A new gamepadHapticActuator representing a gamepad's vibration actuator is constructed by performing the following steps:

  1. Let gamepadHapticActuator be a newly created GamepadHapticActuator instance.
  2. Set gamepadHapticActuator.[[effects]] to the supported effect types for the vibration actuator.
  3. Return gamepadHapticActuator.
  4. Return gamepadHapticActuator.

9. GamepadHapticsResult Enum

WebIDLenum GamepadHapticsResult {
  "complete",
  "preempted"
};
complete

The haptic effect completed playing.

preempted

The current haptic effect was stopped or replaced (i.e., "preempted") by another effect.

10. GamepadHapticEffectType Enum

Each value represents a different style of haptic effect. The effect type defines how to generate a haptic effect from the GamepadEffectParameters.

WebIDLenum GamepadHapticEffectType {
  "dual-rumble",
  "trigger-rumble"
};
"dual-rumble" effect type

"dual-rumble" describes a haptic configuration with an eccentric rotating mass (ERM) vibration motor in each handle of a gamepad. In this configuration, either motor is capable of vibrating the whole gamepad. The vibration effects created by each motor are unequal so that the effects of each can be combined to create more complex haptic effects.

A "dual-rumble" effect is a fixed-duration, constant-intensity vibration effect intended for an actuator of this type. "dual-rumble" effects are defined by startDelay, duration, strongMagnitude, and weakMagnitude, none of which are required because they default to 0.

strongMagnitude and weakMagnitude set the intensity levels for the low-frequency and high-frequency vibrations, normalized to the range [0 .. 1], defaulting to 0.

Given GamepadEffectParameters params, a valid dual-rumble effect must have a valid duration, a valid startDelay, and both the strongMagnitude and the weakMagnitude must be in the range [0 .. 1].

"trigger-rumble" effect type

"trigger-rumble" describes a haptics configuration with a vibration motor in each of the bottom front buttons of a Standard Gamepad (buttons with canonical indices 6 and 7) in addition to the two handle motors used for "dual-rumble". These buttons most commonly take the form of spring-loaded triggers. In this configuration, either motor is capable of providing localized haptic feedback on the button's surface.

A "trigger-rumble" effect is a fixed-duration, constant-intensity vibration effect intended for an actuator of this type. "trigger-rumble" effects are defined by startDelay, duration, strongMagnitude, weakMagnitude, leftTrigger, and rightTrigger, none of which are required because they default to 0.

startDelay, duration, strongMagnitude, weakMagnitude share the same definition with "dual-rumble". leftTrigger and rightTrigger, respectively, set the intensity levels for the left and right bottom front buttons vibrations, normalized to the range [0 .. 1], defaulting to 0.

Given GamepadEffectParameters params, a valid trigger-rumble effect must have a valid duration, a valid startDelay, and the strongMagnitude, weakMagnitude, leftTrigger, and rightTrigger must be in the range [0 .. 1].

11. GamepadEffectParameters Dictionary

A GamepadEffectParameters dictionary contains keys for parameters used by haptic effects. The meaning of each key is defined by the haptic effect type, and some keys may be unused.

To mitigate unwanted long-running effects, the user agent MAY limit the total effect duration for a valid effect to some maximum duration. It is RECOMMENDED that the user agent use a maximum of 5 seconds.

WebIDLdictionary GamepadEffectParameters {
    unsigned long long duration = 0;
    unsigned long long startDelay = 0;
    double strongMagnitude = 0.0;
    double weakMagnitude = 0.0;
    double leftTrigger = 0.0;
    double rightTrigger = 0.0;
};
duration member
duration sets the duration of the vibration effect in milliseconds.
startDelay member
startDelay sets the duration of the delay after playEffect() is called until vibration is started, in milliseconds. During the delay interval, the actuator SHOULD NOT vibrate.
strongMagnitude member
The vibration magnitude for the low frequency rumble in a "dual-rumble" or "trigger-rumble" effect.
weakMagnitude member
The vibration magnitude for the high frequency rumble in a "dual-rumble" or "trigger-rumble" effect.
leftTrigger member
The vibration magnitude for the bottom left front button (canonical index 6) rumble in a "trigger-rumble" effect.
rightTrigger member
The vibration magnitude for the bottom right front button (canonical index 7) rumble in a "trigger-rumble" effect.

12. Extensions to the Navigator interface

WebIDL[Exposed=Window]
partial interface Navigator {
  sequence<Gamepad?> getGamepads();
};

Instances of Navigator are created with the internal slots described in the following table:

Internal slot Initial value Description (non-normative)
[[hasGamepadGesture]] false A flag indicating that a gamepad user gesture has been observed
[[gamepads]] A empty sequence of Gamepad? objects Each Gamepad present at the index specified by its index attribute, or null for unassigned indices.

12.1 getGamepads() method

Note

The gamepad state returned from getGamepads() does not reflect disconnection or connection until after the gamepaddisconnected or gamepadconnected events have fired.

Note

To mitigate fingerprinting, getGamepads() returns an empty list before a gamepad user gesture has been seen. [FINGERPRINTING-GUIDANCE]

The getGamepads() method steps are:

  1. Let window be the current global object.
  2. Let document be window's associated Document.
  3. If document is not fully active, return an empty list.
  4. If document is not allowed to use the "gamepad" permission, then throw a "SecurityError" DOMException and abort these steps.
  5. If this.[[hasGamepadGesture]] is false, then return an empty list.
  6. Let now be the current high resolution time given window.
  7. Let gamepads be an empty list.
  8. For each gamepad of this.[[gamepads]]:
    1. If gamepad is not null and gamepad.[[exposed]] is false:
      1. Set gamepad.[[exposed]] to true.
      2. Set gamepad.[[timestamp]] to now.
    2. Append gamepad to gamepads.
  9. Return gamepads.

A gamepad contains a gamepad user gesture if the current input state indicates that the user is currently interacting with the gamepad. The user agent MUST provide an algorithm to check if the input state contains a gamepad user gesture. For buttons that have a preferred button state and have reported a pressed value of false at least once, a pressed value of true SHOULD be considered interaction. If a button does not have a preferred button state (for example, a toggle switch), then a pressed value of true SHOULD NOT be considered interaction. If a button has never reported a pressed value of false then it SHOULD NOT be considered interaction. Axis movements SHOULD be considered interaction if the axis has a preferred axis state, the current displacement from the center position is greater than a threshold chosen by the user agent, and the axis has reported a value below the threshold at least once. If an axis does not have a preferred axis state (for example, an axis for a joystick that does not self-center), or an axis has never reported a value below the axis gesture threshold, then the axis SHOULD NOT be considered when checking for interaction. The axis gesture threshold SHOULD be large enough that random jitter is not considered interaction.

13. GamepadEvent Interface

WebIDL[Exposed=Window]

interface GamepadEvent: Event {
  constructor(DOMString type, GamepadEventInit eventInitDict);
  [SameObject] readonly attribute Gamepad gamepad;
};
gamepad attribute
The gamepad attribute provides access to the associated gamepad data for this event.

13.1 GamepadEventInit dictionary

WebIDLdictionary GamepadEventInit : EventInit {
  required Gamepad gamepad;
};
gamepad member
The Gamepad associated with this event.

14. Remapping

Each device manufacturer creates many different products and each has unique styles and input control layouts. It is intended that the user agent support as many of these as possible.

Additionally there are de facto standard layouts that have been made popular by game consoles. When the user agent recognizes the attached device, it is RECOMMENDED that it be remapped to a canonical ordering when possible. Devices that are not recognized should still be exposed in their raw form.

There is currently one canonical layout, the Standard Gamepad. When remapping, the indices in axes and buttons should correspond as closely as possible to the physical locations in the diagram below. Additionally, mapping SHOULD be set to "standard".

The Standard Gamepad buttons are laid out in a left cluster of four buttons, a right cluster of four buttons, a center cluster of three buttons, and a pair of front facing buttons on the left and right side of the gamepad. The four axis inputs of the "Standard Gamepad" are associated with a pair of analog sticks, one on the left and one on the right. The following table describes the input controls and their physical locations.

An axis input represents a Standard Gamepad axis if it reports the input values for a thumbstick axis, the thumbstick is located in approximately the same location as the corresponding Standard Gamepad thumbstick, and the orientation of the axis (up-down or left-right) matches the orientation of the Standard Gamepad axis. If there are multiple axis inputs that represent the same Standard Gamepad axis, then the user agent SHOULD select one to be the Standard Gamepad axis and assign a different index to the other axis.

A button input represents a Standard Gamepad button if it reports the input values for a button or trigger, and the button or trigger is located in approximately the same location as the corresponding Standard Gamepad button or trigger.

If an axis or button input represents a Standard Gamepad axis or button, then its canonical index is the index of the corresponding Standard Gamepad axis or button.

Type Index Location
Button 0 Bottom button in right cluster
1 Right button in right cluster
2 Left button in right cluster
3 Top button in right cluster
4 Top left front button
5 Top right front button
6 Bottom left front button
7 Bottom right front button
8 Left button in center cluster
9 Right button in center cluster
10 Left stick pressed button
11 Right stick pressed button
12 Top button in left cluster
13 Bottom button in left cluster
14 Left button in left cluster
15 Right button in left cluster
16 Center button in center cluster
axes 0 Horizontal axis for left stick (negative left/positive right)
1 Vertical axis for left stick (negative up/positive down)
2 Horizontal axis for right stick (negative left/positive right)
3 Vertical axis for right stick (negative up/positive down)
Figure 1 Visual representation of a Standard Gamepad layout.

14.1 Fingerprinting mitigation

Inspecting the capabilities of Gamepad objects can be used as a means of active fingerprinting. The user agent MAY alter the device information exposed through the API to reduce the fingerprinting surface. As an example, an implementation can require that a Gamepad object have exactly the number of button and axis inputs defined in the Standard Gamepad layout even if more or fewer inputs are present on the connected device. [FINGERPRINTING-GUIDANCE]

15. Usage Examples

This section is non-normative.

The example below demonstrates typical access to gamepads. Note the relationship with the requestAnimationFrame() method.

function runAnimation() {
    window.requestAnimationFrame(runAnimation);
    for (const pad of navigator.getGamepads()) {
      // todo; simple demo of displaying pad.axes and pad.buttons
      console.log(pad);
    }
}

window.requestAnimationFrame(runAnimation);
Best Practice 1: Coordination with requestAnimationFrame()

Interactive applications will typically be using the requestAnimationFrame() method to drive animation, and will want coordinate animation with user gamepad input. As such, the gamepad data should be polled as closely as possible to immediately before the animation callbacks are executed, and with frequency matching that of the animation. That is, if the animation callbacks are running at 60Hz, the gamepad inputs should also be sampled at that rate.

16. The gamepadconnected event

When a gamepad availableGamepad becomes available, run the following steps for each top-level traversable traversable of the user agent's top-level traversable set:

  1. For each navigable navigable of traversable's active document's inclusive descendant navigables:
    1. Let window be navigable's active window.
    2. Run the steps to add an available gamepad availableGamepad given window.

To add an available gamepad rawGamepad given window:

  1. If window's associated Document is not allowed to use the "gamepad" permission, then abort these steps.
  2. Queue a global task on the gamepad task source given window to perform the following steps:
    1. Let gamepad be a new Gamepad representing availableGamepad for window.
    2. Set window.navigator.[[gamepads]][gamepad.index] to gamepad.
    3. If window.navigator.[[hasGamepadGesture]] is true:
      1. Set gamepad.[[exposed]] to true.
      2. If document is not null and is fully active, then fire an event named gamepadconnected at window using GamepadEvent with its gamepad attribute initialized to gamepad.

User agents implementing this specification must provide a new DOM event, named gamepadconnected. The corresponding event MUST be of type GamepadEvent and MUST fire on the Window object.

A user agent MUST dispatch this event type to indicate a gamepad has become available. If a gamepad was already available when the page was loaded, the gamepadconnected event SHOULD be dispatched once any Gamepad contains a gamepad user gesture.

17. The gamepaddisconnected event

When a gamepad unavailableGamepad becomes unavailable, run the following steps for each top-level traversable traversable of the user agent's top-level traversable set:

  1. For each navigable navigable of traversable's active document's inclusive descendant navigables:
    1. Let window be navigable's active window.
    2. Run the steps to remove an unavailable gamepad unavailableGamepad given window.

To remove an unavailable gamepad rawGamepad given window:

  1. Let gamepad be the Gamepad in window.navigator.[[gamepads]] representing unavailableGamepad.
  2. Queue a global task on the gamepad task source given window to perform the following steps:
    1. Set gamepad.[[connected]] to false.
    2. Let document be window's associated Document.
    3. If gamepad.[[exposed]] is true and document is fully active, then fire an event named gamepaddisconnected at window using GamepadEvent with its gamepad attribute initialized to gamepad.
    4. Set window.navigator.[[gamepads]][gamepad.index] to null.
    5. While window.navigator.[[gamepads]] is not empty and the last item of window.navigator.[[gamepads]] is null, remove the last item of window.navigator.[[gamepads]].

User agents implementing this specification must provide a new DOM event, named gamepaddisconnected. The corresponding event MUST be of type GamepadEvent and MUST fire on the Window object.

When a gamepad becomes unavailable, if the user agent has previously dispatched a gamepadconnected event for that gamepad to a Window, a gamepaddisconnected event MUST be dispatched to that same Window.

18. Other events

More discussion needed, on whether to include or exclude axis and button changed events, and whether to roll them more together ("gamepadchanged"?), separate somewhat ("gamepadaxischanged"?), or separate by individual axis and button.

19. Extensions to the WindowEventHandlers Interface Mixin

This specification extends the WindowEventHandlers interface mixin from HTML to add event handler IDL attributes to facilitate the event handler registration.

WebIDLpartial interface mixin WindowEventHandlers {
  attribute EventHandler ongamepadconnected;
  attribute EventHandler ongamepaddisconnected;
};

20. Integration with Permissions Policy

This specification defines a policy-controlled feature identified by the string "gamepad". Its default allowlist is *.

Note

A document's permissions policy determines whether any content in that document is allowed to access getGamepads(). If disabled in any document, no content in the document will be allowed to use getGamepads(), nor will the gamepadconnected and gamepaddisconnected events fire.

21. Conformance

As well as sections marked as non-normative, all authoring guidelines, diagrams, examples, and notes in this specification are non-normative. Everything else in this specification is normative.

The key words MAY, MUST, MUST NOT, RECOMMENDED, SHOULD, and SHOULD NOT in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

A. Acknowledgements

This section is non-normative.

The following people contributed to the development of this document.

B. References

B.1 Normative references

[dom]
DOM Standard. Anne van Kesteren. WHATWG. Living Standard. URL: https://dom.spec.whatwg.org/
[FINGERPRINTING-GUIDANCE]
Mitigating Browser Fingerprinting in Web Specifications. Nick Doty; Tom Ritter. W3C. 21 March 2025. W3C Working Group Note. URL: https://www.w3.org/TR/fingerprinting-guidance/
[geometry-1]
Geometry Interfaces Module Level 1. Simon Pieters; Chris Harrelson. W3C. 4 December 2018. W3C Candidate Recommendation. URL: https://www.w3.org/TR/geometry-1/
[HR-TIME]
High Resolution Time. Yoav Weiss. W3C. 7 November 2024. W3C Working Draft. URL: https://www.w3.org/TR/hr-time-3/
[html]
HTML Standard. Anne van Kesteren; Domenic Denicola; Dominic Farolino; Ian Hickson; Philip Jägenstedt; Simon Pieters. WHATWG. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[infra]
Infra Standard. Anne van Kesteren; Domenic Denicola. WHATWG. Living Standard. URL: https://infra.spec.whatwg.org/
[permissions-policy]
Permissions Policy. Ian Clelland. W3C. 6 May 2025. W3C Working Draft. URL: https://www.w3.org/TR/permissions-policy-1/
[RFC2119]
Key words for use in RFCs to Indicate Requirement Levels. S. Bradner. IETF. March 1997. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc2119
[RFC8174]
Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words. B. Leiba. IETF. May 2017. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc8174
[WEBIDL]
Web IDL Standard. Edgar Chen; Timothy Gu. WHATWG. Living Standard. URL: https://webidl.spec.whatwg.org/
[webxr-gamepads-module-1]
WebXR Gamepads Module - Level 1. Brandon Jones; Manish Goregaokar; Rik Cabanier. W3C. 9 April 2024. W3C Working Draft. URL: https://www.w3.org/TR/webxr-gamepads-module-1/