diff --git a/extensions.html b/extensions.html index a4448e1..af31e63 100644 --- a/extensions.html +++ b/extensions.html @@ -1,442 +1,442 @@ - - - - - Gamepad Extensions - - - - - - - - - -
- Extensions to the base Gamepad specification to enable access to more - advanced device capabilities. -
-
- If you have comments for this spec, please send them to public-webapps@w3.org with a Subject: - prefix of [gamepad]. See - Bugzilla for this specification's open bugs. -
-
-

- Introduction -

-

- The Gamepad API provides a tightly scoped interface to gamepad devices - and is focused on the most common elements of those devices, namely - axis and button inputs. It specifically excludes support for more - complex devices (e.g., those that do motion tracking or haptic - feedback). -

-

- However, some uses of gamepads (e.g., those paired with Virtual Reality - headsets) rely heavily on those more advanced features. This - supplemetary spec describes extensions to the base API to accommodate - those use cases. If they prove to be broadly useful, the hope is that - they will be eventually merged into the main spec. -

-
-
-
-

- GamepadHand Enum -

-

- This enum defines the set of possible hands a gamepad may be held by. -

-
-        enum GamepadHand {
-          "",  /* unknown, both hands, or not applicable */
-          "left",
-          "right"
-        };
-      
-
-
- "" (the empty string) -
-
- The empty string indicates the hand that is holding the gamepad is - unknown or not applicable (e.g., if the gamepad is held with two - hands). -
-
- left -
-
- Gamepad is held or is most likely to be held in the left hand. -
-
- right -
-
- Gamepad is held or is most likely to be held in the right hand. -
-
-
-
-

- GamepadPose Interface -

-

- This interface defines the gamepad's position, orientation, velocity, - and acceleration. -

-
-        [Exposed=Window]
-        interface GamepadPose {
-          readonly attribute boolean hasOrientation;
-          readonly attribute boolean hasPosition;
-
-          readonly attribute Float32Array? position;
-          readonly attribute Float32Array? linearVelocity;
-          readonly attribute Float32Array? linearAcceleration;
-          readonly attribute Float32Array? orientation;
-          readonly attribute Float32Array? angularVelocity;
-          readonly attribute Float32Array? angularAcceleration;
-        };
-      
-
-
- hasOrientation -
-
- The hasOrientation attribute MUST return whether the - gamepad is capable of tracking its orientation. -
-
- hasPosition -
-
- The hasPosition attribute MUST return whether the - gamepad is capable of tracking its position. -
-
- position -
-
-

- Position of the gamepad as a 3D vector, given in meters from an - origin point, which is determined by the gamepad hardware and MAY - be the position of the gamepad when first polled if no other - reference point is available. The coordinate system uses these axis - definitions, assuming the user is holding the gamepad in the - forward orientation: -

-
    -
  • Positive X is to the user's right. -
  • -
  • Positive Y is up. -
  • -
  • Positive Z is behind the user. -
  • -
-

- MUST be null if the gamepad is incapable of providing - positional data. When not null, MUST be a - three-element array. -

-
-
- linearVelocity -
-
- Linear velocity of the gamepad in meters per second. MUST be - null if the sensor is incapable of providing linear - velocity. When not null, MUST be a three-element array. -
-
- linearAcceleration -
-
- Linear acceleration of the gamepad in meters per second. MUST be - null if the sensor is incapable of providing linear - acceleration. When not null, MUST be a three-element - array. -
-
- orientation -
-
- Orientation of the gamepad as a quaternion. An orientation of - [0, 0, 0, 1] is considered to be forward. - The forward direction MUST be determined by the gamepad hardware. The - forward direction MAY be the orientation of the hardware when it was - first polled if no other reference orientation is available. If the - sensor is incapable of providing orientation data, the orientation - MUST be null. When not null, the - orientation MUST be a four-element array. -
-
- angularVelocity -
-
- Angular velocity of the gamepad in meters per second. MUST be - null if the sensor is incapable of providing angular - velocity. When not null, the - angularVelocity MUST be a three-element array. -
-
- angularAcceleration -
-
- Angular acceleration of the gamepad in meters per second. MUST be - null if the sensor is incapable of providing angular - acceleration. When not null, - angularAcceleration MUST be a three-element array. -
-
-
-
-

- Partial Gamepad Interface -

-

- This partial interface supplements the Gamepad interface described in - the main Gamepad spec. -

-
-        partial interface Gamepad {
-          readonly attribute GamepadHand hand;
-          readonly attribute FrozenArray<GamepadHapticActuator> hapticActuators;
-          readonly attribute GamepadPose? pose;
-        };
-      
-

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

- - - - - - - - - - - - - - - - - - - - - -
- Internal slot - - Initial value - - Description (non-normative) -
- [[\hand]] - - `undefined` - - Indicates the hand the controller is held in. -
- [[\hapticActuators]] - - `undefined` - - List of all the haptic actuators in the gamepad. -
- [[\pose]] - - `null` - - The current pose of the gamepad. -
-
-
- hand -
-
- An enumeration, {{GamepadHand}}, that indicates which hand the - controller is being held in or is most likely to be held in. -
-
- hapticActuators -
-
- A list of all the {{GamepadHapticActuator}}s in the gamepad. The same - object MUST be returned until the user agent needs to return - different values (or values in a different order). -
-
- pose -
-
- The current {{GamepadPose}} of the gamepad, if supported by the - hardware. Includes position, orientation, velocity, and acceleration. - If the hardware cannot supply any pose values, MUST be set to `null`. -
-
-
-

- Receiving inputs -

-

- This section supplements the Receiving - inputs section of the main Gamepad specification. -

-
-
-

- Constructing a `Gamepad` -

-

- This section supplements the Constructing - a `Gamepad` section of the main Gamepad specification. -

-
-
-
-

- Partial GamepadHapticActuator Interface -

-

- This partial interface supplements the {{GamepadHapticActuator}} - interface described in the main Gamepad spec. -

-
-        [Exposed=Window]
-        partial interface GamepadHapticActuator {
-          Promise<boolean> pulse(double value, double duration);
-        };
-      
-
-
- pulse() method -
-
-

- {{GamepadHapticActuator/pulse()}} applies a |value:double| to the - actuator for duration milliseconds. The value - passed to pulse() is clamped to limits defined by the - actuator type. The returned Promise will resolve true - once the pulse has completed. -

-

- Repeated calls to pulse() override the previous - values. -

-
-
-
- - + + + + + Gamepad Extensions + + + + + + + + + +
+ Extensions to the base Gamepad specification to enable access to more + advanced device capabilities. +
+
+ If you have comments for this spec, please send them to public-webapps@w3.org with a Subject: + prefix of [gamepad]. See + Bugzilla for this specification's open bugs. +
+
+

+ Introduction +

+

+ The Gamepad API provides a tightly scoped interface to gamepad devices + and is focused on the most common elements of those devices, namely + axis and button inputs. It specifically excludes support for more + complex devices (e.g., those that do motion tracking or haptic + feedback). +

+

+ However, some uses of gamepads (e.g., those paired with Virtual Reality + headsets) rely heavily on those more advanced features. This + supplemetary spec describes extensions to the base API to accommodate + those use cases. If they prove to be broadly useful, the hope is that + they will be eventually merged into the main spec. +

+
+
+
+

+ GamepadHand Enum +

+

+ This enum defines the set of possible hands a gamepad may be held by. +

+
+        enum GamepadHand {
+          "",  /* unknown, both hands, or not applicable */
+          "left",
+          "right"
+        };
+      
+
+
+ "" (the empty string) +
+
+ The empty string indicates the hand that is holding the gamepad is + unknown or not applicable (e.g., if the gamepad is held with two + hands). +
+
+ left +
+
+ Gamepad is held or is most likely to be held in the left hand. +
+
+ right +
+
+ Gamepad is held or is most likely to be held in the right hand. +
+
+
+
+

+ GamepadPose Interface +

+

+ This interface defines the gamepad's position, orientation, velocity, + and acceleration. +

+
+        [Exposed=Window]
+        interface GamepadPose {
+          readonly attribute boolean hasOrientation;
+          readonly attribute boolean hasPosition;
+
+          readonly attribute Float32Array? position;
+          readonly attribute Float32Array? linearVelocity;
+          readonly attribute Float32Array? linearAcceleration;
+          readonly attribute Float32Array? orientation;
+          readonly attribute Float32Array? angularVelocity;
+          readonly attribute Float32Array? angularAcceleration;
+        };
+      
+
+
+ hasOrientation +
+
+ The hasOrientation attribute MUST return whether the + gamepad is capable of tracking its orientation. +
+
+ hasPosition +
+
+ The hasPosition attribute MUST return whether the + gamepad is capable of tracking its position. +
+
+ position +
+
+

+ Position of the gamepad as a 3D vector, given in meters from an + origin point, which is determined by the gamepad hardware and MAY + be the position of the gamepad when first polled if no other + reference point is available. The coordinate system uses these axis + definitions, assuming the user is holding the gamepad in the + forward orientation: +

+
    +
  • Positive X is to the user's right. +
  • +
  • Positive Y is up. +
  • +
  • Positive Z is behind the user. +
  • +
+

+ MUST be null if the gamepad is incapable of providing + positional data. When not null, MUST be a + three-element array. +

+
+
+ linearVelocity +
+
+ Linear velocity of the gamepad in meters per second. MUST be + null if the sensor is incapable of providing linear + velocity. When not null, MUST be a three-element array. +
+
+ linearAcceleration +
+
+ Linear acceleration of the gamepad in meters per second. MUST be + null if the sensor is incapable of providing linear + acceleration. When not null, MUST be a three-element + array. +
+
+ orientation +
+
+ Orientation of the gamepad as a quaternion. An orientation of + [0, 0, 0, 1] is considered to be forward. + The forward direction MUST be determined by the gamepad hardware. The + forward direction MAY be the orientation of the hardware when it was + first polled if no other reference orientation is available. If the + sensor is incapable of providing orientation data, the orientation + MUST be null. When not null, the + orientation MUST be a four-element array. +
+
+ angularVelocity +
+
+ Angular velocity of the gamepad in meters per second. MUST be + null if the sensor is incapable of providing angular + velocity. When not null, the + angularVelocity MUST be a three-element array. +
+
+ angularAcceleration +
+
+ Angular acceleration of the gamepad in meters per second. MUST be + null if the sensor is incapable of providing angular + acceleration. When not null, + angularAcceleration MUST be a three-element array. +
+
+
+
+

+ Partial Gamepad Interface +

+

+ This partial interface supplements the Gamepad interface described in + the main Gamepad spec. +

+
+        partial interface Gamepad {
+          readonly attribute GamepadHand hand;
+          readonly attribute FrozenArray<GamepadHapticActuator> hapticActuators;
+          readonly attribute GamepadPose? pose;
+        };
+      
+

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

+ + + + + + + + + + + + + + + + + + + + + +
+ Internal slot + + Initial value + + Description (non-normative) +
+ [[\hand]] + + `undefined` + + Indicates the hand the controller is held in. +
+ [[\hapticActuators]] + + `undefined` + + List of all the haptic actuators in the gamepad. +
+ [[\pose]] + + `null` + + The current pose of the gamepad. +
+
+
+ hand +
+
+ An enumeration, {{GamepadHand}}, that indicates which hand the + controller is being held in or is most likely to be held in. +
+
+ hapticActuators +
+
+ A list of all the {{GamepadHapticActuator}}s in the gamepad. The same + object MUST be returned until the user agent needs to return + different values (or values in a different order). +
+
+ pose +
+
+ The current {{GamepadPose}} of the gamepad, if supported by the + hardware. Includes position, orientation, velocity, and acceleration. + If the hardware cannot supply any pose values, MUST be set to `null`. +
+
+
+

+ Receiving inputs +

+

+ This section supplements the Receiving + inputs section of the main Gamepad specification. +

+
+
+

+ Constructing a `Gamepad` +

+

+ This section supplements the Constructing + a `Gamepad` section of the main Gamepad specification. +

+
+
+
+

+ Partial GamepadHapticActuator Interface +

+

+ This partial interface supplements the {{GamepadHapticActuator}} + interface described in the main Gamepad spec. +

+
+        [Exposed=Window]
+        partial interface GamepadHapticActuator {
+          Promise<boolean> pulse(double value, double duration);
+        };
+      
+
+
+ pulse() method +
+
+

+ {{GamepadHapticActuator/pulse()}} applies a |value:double| to the + actuator for duration milliseconds. The value + passed to pulse() is clamped to limits defined by the + actuator type. The returned Promise will resolve true + once the pulse has completed. +

+

+ Repeated calls to pulse() override the previous + values. +

+
+
+
+ + diff --git a/index.html b/index.html index 034acd2..c6a65c3 100644 --- a/index.html +++ b/index.html @@ -1,2498 +1,2498 @@ - - - - - Gamepad - - - - - - - -
-

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

-
-
-

- This is a work in progress. -

-
-
-

- Introduction -

-

- Some [=user agent=]s 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. -

-
-
-

- 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. -

-
-
-

- Gamepad interface -

-

- This interface defines an individual gamepad device. -

-
-        [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>? touchEvents;
-          [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]] - - `false` - - A flag indicating that the device is connected to the system -
- [[\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 axes exposed by this device -
- [[\buttons]] - - An empty [=sequence=] - - A [=sequence=] of {{GamepadButton}} objects representing the - current state of buttons exposed by this device -
- [[\exposed]] - - `false` - - A flag indicating that the {{Gamepad}} object has been exposed to - script -
- [[\axisMapping]] - - An empty [=ordered map=] - - Mapping from unmapped axis index to an index in the - {{Gamepad/axes}} array -
- [[\axisMinimums]] - - An empty [=list=] - - A [=list=] containing the minimum logical value for each axis -
- [[\axisMaximums]] - - An empty [=list=] - - A [=list=] containing the maximum logical value for each axis -
- [[\buttonMapping]] - - An empty [=ordered map=] - - Mapping from unmapped button index to an index in the - {{Gamepad/buttons}} array -
- [[\buttonMinimums]] - - An empty [=list=] - - A [=list=] containing the minimum logical value for each button. -
- [[\buttonMaximums]] - - An empty [=list=] - - A [=list=] containing the maximum logical value for each button -
- [[\touchEvents]] - - `null` - - List of generated touch events. -
- [[\vibrationActuator]] - - undefined - - A {{GamepadHapticActuator}} object capable of generating a haptic - effect that vibrates the entire gamepad -
-
-
- id attribute -
-
-

- An identification string for the gamepad. This string identifies - the brand or style of connected gamepad device. -

-

- The exact format of the {{Gamepad/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 - {{Gamepad/id}} string. -

-
-
- index attribute -
-
- The index of the gamepad in the {{Navigator}}. When multiple gamepads - are connected to a [=user agent=], indices MUST be assigned on a - first-come, first-serve basis, starting at zero. If a gamepad is - disconnected, previously assigned indices MUST NOT be reassigned to - gamepads that continue to be connected. However, if a gamepad is - disconnected, and subsequently the same or a different gamepad is - then connected, the lowest previously used index MUST be reused. -
-
- connected attribute -
-
-

- Indicates whether the physical device represented by this object is - still connected to the system. When a gamepad becomes unavailable, - whether by being physically disconnected, powered off or otherwise - unusable, the {{Gamepad/connected}} attribute MUST be set to - `false`. -

-

- The {{Gamepad/connected}} getter steps are: -

-
    -
  1. Return [=this=].{{Gamepad/[[connected]]}}. -
  2. -
-
-
- timestamp attribute -
-
-

- The {{Gamepad/timestamp}} allows the author to determine the last - time the {{Gamepad/axes}} or {{Gamepad/buttons}} attribute for this - gamepad was updated. The value MUST be set to the [=current high - resolution time=] each time the system [=receives new button or - axis input values=] from the device. If no data has been received - from the hardware, {{Gamepad/timestamp}} MUST be the [=current high - resolution time=] at the time when the {{Gamepad}} was first made - available to script. -

-

- [=User agent=]s SHOULD set a minimum resolution of |gamepad|'s - {{Gamepad/timestamp}} attribute to 5 microseconds, following - [[HR-TIME]]'s clock resolution recommendation. -

-

- The {{Gamepad/timestamp}} getter steps are: -

-
    -
  1. Return [=this=].{{Gamepad/[[timestamp]]}}. -
  2. -
-
-
- mapping attribute -
-
-

- The mapping in use for this device. If the user agent has knowledge - of the layout of the device, then it SHOULD indicate that a mapping - is in use by setting {{Gamepad/mapping}} to the corresponding - {{GamepadMappingType}} value. -

-

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

-
    -
  1. If the button and axis layout of the gamepad device corresponds - with the [=Standard Gamepad=] layout, then return - {{GamepadMappingType/"standard"}}. -
  2. -
  3. Return {{GamepadMappingType/""}}. -
  4. -
-
-
- axes attribute -
-
-

- Array of values for all axes of the gamepad. All axis values MUST - be linearly normalized to the range [-1.0 .. 1.0]. If the - controller is perpendicular to the ground with the directional - stick pointing up, -1.0 SHOULD correspond to "forward" or "left", - and 1.0 SHOULD correspond to "backward" or "right". Axes that are - drawn from a 2D input device SHOULD appear next to each other in - the axes array, X then Y. It is RECOMMENDED that axes appear in - decreasing order of importance, such that element 0 and 1 typically - represent the X and Y axis of a directional stick. The same object - MUST be returned until the [=user agent=] needs to return different - values (or values in a different order). -

-

- The {{Gamepad/axes}} getter steps are: -

-
    -
  1. Return [=this=].{{Gamepad/[[axes]]}}. -
  2. -
-
-
- 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 {{Gamepad/buttons}} getter steps are: -

-
    -
  1. Return [=this=].{{Gamepad/[[buttons]]}}. -
  2. -
-
-
- touchEvents -
-
-

- A list of {{GamepadTouch}} events generated from all touch - surfaces. If the device does not support touch events, MUST be set - to `null`. -

-

- The {{Gamepad/touchEvents}} getter steps are: -

-
    -
  1. If [=this=].{{Gamepad/[[touchEvents]]}} not `null` and not - empty, return [=this=].{{Gamepad/[[touchEvents]]}}. -
  2. -
  3. Otherwise return `null`. -
  4. -
-
-
- vibrationActuator attribute -
-
-

- A {{GamepadHapticActuator}} object that represents the device's - primary vibration actuator. -

-

- The {{Gamepad/vibrationActuator}} getter steps are: -

-
    -
  1. Return [=this=].{{Gamepad/[[vibrationActuator]]}}. -
  2. -
-
-
-
-

- Receiving inputs -

-

- When the system receives new button or axis input values, - run the following steps: -

-
    -
  1. Let |gamepad:Gamepad| be the {{Gamepad}} object representing the - device that received new button or axis input values. -
  2. -
  3. [=Queue a task=] on the [=gamepad task source=] to [=update - gamepad state=] for |gamepad|. -
  4. -
-

- To update gamepad state for |gamepad:Gamepad|, run the - following steps: -

-
    -
  1. Let |now:DOMHighResTimeStamp| be the [=current high resolution - time=]. -
  2. -
  3. Set |gamepad|.{{Gamepad/[[timestamp]]}} to |now|. -
  4. -
  5. Run the steps to [=map and normalize axes=] for |gamepad|. -
  6. -
  7. Run the steps to [=map and normalize buttons=] for |gamepad|. -
  8. -
  9. Run the steps to [=update touchEvents=] for |gamepad|. -
  10. -
  11. Let |navigator:Navigator| be |gamepad|'s [=relevant global - object=]'s {{Navigator}} object. -
  12. -
  13. If |navigator|.{{Navigator/[[hasGamepadGesture]]}} is `false` and - |gamepad| [=contains a gamepad user gesture=]: -
      -
    1. Set |navigator|.{{Navigator/[[hasGamepadGesture]]}} to - `true`. -
    2. -
    3. [=list/For each=] |connectedGamepad:Gamepad?| of - |navigator|.{{Navigator/[[gamepads]]}}: -
        -
      1. If |connectedGamepad| is not equal to `null`: -
          -
        1. Set |connectedGamepad|.{{Gamepad/[[exposed]]}} to - `true`. -
        2. -
        3. Set |connectedGamepad|.{{Gamepad/[[timestamp]]}} to - |now|. -
        4. -
        5. Let |document:Document?| be |gamepad|'s [=relevant - global object=]'s [=associated `Document`=]; otherwise - `null`. -
        6. -
        7. If |document| is not `null` and is [=Document/fully - active=], then [=queue a task=] on the [=gamepad task - source=] to [=fire an event=] named {{gamepadconnected}} - at |gamepad|'s [=relevant global object=] using - {{GamepadEvent}} with its {{GamepadEvent/gamepad}} - attribute initialized to |connectedGamepad|. -
        8. -
        -
      2. -
      -
    4. -
    -
  14. -
-

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

-
    -
  1. Let |axisValues:list| be a [=list=] of {{unsigned long}} values - representing the most recent logical axis input values for each axis - input of the device represented by |gamepad|. -
  2. -
  3. Let |maxRawAxisIndex:long| be the [=list/size=] of |axisValues| − - 1. -
  4. -
  5. [=list/For each=] |rawAxisIndex:long| of [=the range=] from 0 to - |maxRawAxisIndex|: -
      -
    1. Let |mappedIndex:long| be - |gamepad|.{{Gamepad/[[axisMapping]]}}[|rawAxisIndex|]. -
    2. -
    3. Let |logicalValue:unsigned long| be - |axisValues|[|rawAxisIndex|]. -
    4. -
    5. Let |logicalMinimum:unsigned long| be - |gamepad|.{{Gamepad/[[axisMinimums]]}}[|rawAxisIndex|]. -
    6. -
    7. Let |logicalMaximum:unsigned long| be - |gamepad|.{{Gamepad/[[axisMaximums]]}}[|rawAxisIndex|]. -
    8. -
    9. Let |normalizedValue:double| be 2 (|logicalValue| − - |logicalMinimum|) / (|logicalMaximum| − |logicalMinimum|) − 1. -
    10. -
    11. Set |gamepad|.{{Gamepad/[[axes]]}}[|axisIndex|] to be - |normalizedValue|. -
    12. -
    -
  6. -
-

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

-
    -
  1. Let |buttonValues:list| be a [=list=] of {{unsigned long}} values - representing the most recent logical button input values for each - button input of the device represented by |gamepad|. -
  2. -
  3. Let |maxRawButtonIndex:long| be the [=list/size=] of - |buttonValues| − 1. -
  4. -
  5. [=list/For each=] |rawButtonIndex:long| of [=the range=] from 0 - to |maxRawButtonIndex|: -
      -
    1. Let |mappedIndex:long| be - |gamepad|.{{Gamepad/[[buttonMapping]]}}[|rawButtonIndex|]. -
    2. -
    3. Let |logicalValue:unsigned long| be - |buttonValues|[|rawButtonIndex|]. -
    4. -
    5. Let |logicalMinimum:unsigned long| be - |gamepad|.{{Gamepad/[[buttonMinimums]]}}[|rawButtonIndex|]. -
    6. -
    7. Let |logicalMaximum:unsigned long| be - |gamepad|.{{Gamepad/[[buttonMaximums]]}}[|rawButtonIndex|]. -
    8. -
    9. Let |normalizedValue:double| be (|logicalValue| − - |logicalMinimum|) / (|logicalMaximum| − |logicalMinimum|). -
    10. -
    11. Let |button:GamepadButton| be - |gamepad|.{{Gamepad/[[buttons]]}}[|mappedIndex|]. -
    12. -
    13. Set |button|.{{GamepadButton/[[value]]}} to - |normalizedValue|. -
    14. -
    15. -

      - If the button has a digital switch to indicate a pure pressed - or released state, set |button|.{{GamepadButton/[[pressed]]}} - to `true` if the button is pressed or `false` if it is not - pressed. -

      -

      - Otherwise, set |button|.{{GamepadButton/[[pressed]]}} to - `true` if the value is above the [=button press threshold=] - or `false` if it is not above the threshold. -

      -
    16. -
    17. -

      - If the button is capable of detecting touch, set - |button|.{{GamepadButton/[[touched]]}} to `true` if the - button is currently being touched. -

      -

      - Otherwise, set |button|.{{GamepadButton/[[touched]]}} to - |button|.{{GamepadButton/[[pressed]]}}. -

      -
    18. -
    -
  6. -
-

- When the user agent modifies the list of |active touch points| for a - touch surface by adding touch points, removing touch points, or - updating the values of existing touch points, update - touchEvents by running the following steps: -

-
    -
  1. Let |surfaceId:unsigned long| be 0. -
  2. -
  3. Remove any existing events from - {{Gamepad}}.{{Gamepad/[[touchEvents]]}}. -
  4. -
  5. Repeat the following steps for each [=touch surface=] on - |gamepad| in [=touch surface enumeration order=]: -
      -
    1. Let |surfaceDimensions| be an `null`. -
    2. -
    3. If the touch surface exposes maximum surface dimensions in - device units: -
        -
      1. Set |touchEvent|.{{GamepadTouch/surfaceDimensions}}[0] to - the maximum X dimension on the [=touch surface=] in device - units. -
      2. -
      3. Set |touchEvent|.{{GamepadTouch/surfaceDimensions}}[1] to - the maximum Y dimension on the [=touch surface=] in device - units. -
      4. -
      -
    4. -
    5. Repeat the following steps for each [=active touch point=] - reported by the |gamepad| for the current [=touch surface=]. -
        -
      1. Let |nextTouchId:unsigned long| be the next available - touchId for the |gamepad|. -
      2. -
      3. Let |touchEvent:GamepadTouch| be a {{GamepadTouch}}. -
      4. -
      5. Set |touchEvent|.{{GamepadTouch/surfaceId}} to be - |surfaceId|. -
      6. -
      7. If the touch data is part of an existing [=active touch - point=] tracked by the user agent: -
          -
        1. Set |touchEvent|.{{GamepadTouch/touchId}} to the - touchId of the [=active touch point=]. -
        2. -
        3. Otherwise, set touchId to nextTouchId and increment - nextTouchId. -

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

          -
        4. -
        -
      8. -
      9. Let |position| be a [=new=] {{Float32Array}} with length - 2. -
      10. -
      11. -

        - |position|[0] should be set to the device X coordinate - relative to the device [=touch surface=] and normalized - to [-1.0,1.0] where -1.0 is the leftmost coordinate and - 1.0 is the rightmost coordinate. -

        -

        - `position[0] = (2.0 * touchData.x / surfaceDimensions[0]) - - 1` -

        -
      12. -
      13. -

        - |position|[1] should be set to the device Y coordinate - relative to the device [=touch surface=] and normalized - to [-1.0,1.0] where -1.0 is the topmost coordinate and - 1.0 is the bottommost coordinate. -

        -

        - `position[1] = (2.0 * touchData.y / surfaceDimensions[1]) - - 1` -

        -
      14. -
      -
    6. -
    7. Set |touchEvent|.{{GamepadTouch/position}} to be - |position:Float32Array|. -
    8. -
    9. Add |touchEvent| to {{Gamepad}}.{{Gamepad/[[touchEvents]]}}. -
    10. -
    -
  6. -
  7. Increment |surfaceId| -
  8. -
  9. Increment |surfaceId| -
  10. -
-
-
-

- Constructing a `Gamepad` -

-

- A new `Gamepad` representing a connected gamepad device is - constructed by performing the following steps: -

-
    -
  1. Let |gamepad:Gamepad| be a newly created {{Gamepad}} instance: -
      -
    1. Initialize |gamepad|'s {{Gamepad/id}} attribute to an - identification string for the gamepad. -
    2. -
    3. Initialize |gamepad|'s {{Gamepad/index}} attribute to the - result of [=selecting an unused gamepad index=] for |gamepad|. -
    4. -
    5. Initialize |gamepad|'s {{Gamepad/mapping}} attribute to the - result of [=selecting a mapping=] for the gamepad device. -
    6. -
    7. Initialize |gamepad|.{{Gamepad/[[connected]]}} to `true`. -
    8. -
    9. Initialize |gamepad|.{{Gamepad/[[timestamp]]}} to the - [=current high resolution time=]. -
    10. -
    11. Initialize |gamepad|.{{Gamepad/[[axes]]}} to the result of - [=initializing axes=] for |gamepad|. -
    12. -
    13. Initialize |gamepad|.{{Gamepad/[[buttons]]}} to the result of - [=initializing buttons=] for |gamepad|. -
    14. -
    15. Initialize |gamepad|.{{Gamepad/[[touchEvents]]}} to the - result of [=initializing touchEvents=] for |gamepad|. -
    16. -
    17. Initialize |gamepad|.{{Gamepad/[[vibrationActuator]]}} - following the steps of [=constructing a GamepadHapticActuator=] - for |gamepad|. -
    18. -
    -
  2. -
  3. Return |gamepad|. -
  4. -
-

- To select an unused - gamepad index for |gamepad:Gamepad|, run the following steps: -

-
    -
  1. Let |navigator:Navigator| be |gamepad|'s [=relevant global - object=]'s {{Navigator}} object. -
  2. -
  3. Let |maxGamepadIndex:long| be the [=list/size=] of - |navigator|.{{Navigator/[[gamepads]]}} − 1. -
  4. -
  5. [=list/For each=] |gamepadIndex:long| of [=the range=] from 0 to - |maxGamepadIndex|: -
      -
    1. If |navigator|.{{Navigator/[[gamepads]]}}[|gamepadIndex|] is - `null`, then return |gamepadIndex|. -
    2. -
    -
  6. -
  7. [=list/Append=] `null` to |navigator|.{{Navigator/[[gamepads]]}}. -
  8. -
  9. Return the [=list/size=] of - |navigator|.{{Navigator/[[gamepads]]}} − 1. -
  10. -
-

- To initialize axes for - |gamepad:Gamepad|, run the following steps: -

-
    -
  1. Let |inputCount:long| be the number of axis inputs exposed by the - device represented by |gamepad|. -
  2. -
  3. Set |gamepad|.{{Gamepad/[[axisMinimums]]}} to a [=list=] of - {{unsigned long}} values with [=list/size=] equal to |inputCount| - containing minimum logical values for each of the axis inputs. -
  4. -
  5. Set |gamepad|.{{Gamepad/[[axisMaximums]]}} to a [=list=] of - {{unsigned long}} values with [=list/size=] equal to |inputCount| - containing maximum logical values for each of the axis inputs. -
  6. -
  7. Initialize |unmappedInputList| to be an empty [=list=]. -
  8. -
  9. Initialize |mappedIndexList| to be an empty [=list=]. -
  10. -
  11. Initialize |axesSize:long| to be 0. -
  12. -
  13. [=list/For each=] |rawInputIndex:long| of [=the range=] from 0 to - |inputCount| − 1: -
      -
    1. If the the gamepad axis at index |rawInputIndex| [=represents - a Standard Gamepad axis=]: -
        -
      1. Let |canonicalIndex:long| be the [=canonical index=] for - the axis. -
      2. -
      3. If |mappedIndexList| [=list/contain=]s |canonicalIndex|, - then append |rawInputIndex| to |unmappedInputList|. -

        - Otherwise: -

        -
          -
        1. Set - |gamepad|.{{Gamepad/[[axisMapping]]}}[|rawInputIndex|] to - |canonicalIndex|. -
        2. -
        3. [=list/Append=] |canonicalIndex| to - |mappedIndexList|. -
        4. -
        5. If |canonicalIndex| + 1 is greater than |axesSize|, - then set |axesSize| to |canonicalIndex| + 1. -
        6. -
        -
      4. -
      -

      - Otherwise, [=list/append=] |rawInputIndex| to - |unmappedInputList|. -

      -
    2. -
    -
  14. -
  15. Initialize |axisIndex:long| to be 0. -
  16. -
  17. [=list/For each=] |rawInputIndex:long| of |unmappedInputList|: -
      -
    1. While |mappedIndexList| [=list/contain=]s |axisIndex|: -
        -
      1. Increment |axisIndex|. -
      2. -
      -
    2. -
    3. Set |gamepad|.{{Gamepad/[[axisMapping]]}}[|rawInputIndex|] to - |axisIndex|. -
    4. -
    5. [=list/Append=] |axisIndex| to |mappedIndexList|. -
    6. -
    7. If |axisIndex| + 1 is greater than |axesSize|, then set - |axesSize| to |axisIndex| + 1. -
    8. -
    -
  18. -
  19. Initialize |axes| to be an empty [=list=]. -
  20. -
  21. [=list/For each=] |axisIndex:long| of [=the range=] from 0 to - |axesSize| − 1, [=list/append=] 0 to |axes|. -
  22. -
  23. Return |axes|. -
  24. -
-

- To initialize buttons for a - gamepad, run the following steps: -

-
    -
  1. Let |inputCount:long| be the number of button inputs exposed by - the device represented by |gamepad|. -
  2. -
  3. Set |gamepad|.{{Gamepad/[[buttonMinimums]]}} to be a [=list=] of - {{unsigned long}} values with [=list/size=] equal to |inputCount| - containing minimum logical values for each of the button inputs. -
  4. -
  5. Set |gamepad|.{{Gamepad/[[buttonMaximums]]}} to be a [=list=] of - {{unsigned long}} values with [=list/size=] equal to |inputCount| - containing maximum logical values for each of the button inputs. -
  6. -
  7. Initialize |unmappedInputList| to be an empty [=list=]. -
  8. -
  9. Initialize |mappedIndexList| to be an empty [=list=]. -
  10. -
  11. Initialize |buttonsSize:long| to be 0. -
  12. -
  13. [=list/For each=] |rawInputIndex:long| of [=the range=] from 0 to - |inputCount| − 1: -
      -
    1. If the the gamepad button at index |rawInputIndex| - [=represents a Standard Gamepad button=]: -
        -
      1. Let |canonicalIndex:long| be the [=canonical index=] for - the button. -
      2. -
      3. If |mappedIndexList| [=list/contain=]s |canonicalIndex|, - then append |rawInputIndex| to |unmappedInputList|. -

        - Otherwise: -

        -
          -
        1. Set - |gamepad|.{{Gamepad/[[buttonMapping]]}}[|rawInputIndex|] - to |canonicalIndex|. -
        2. -
        3. [=list/Append=] |canonicalIndex| to - |mappedIndexList|. -
        4. -
        5. If |canonicalIndex| + 1 is greater than - |buttonsSize|, then set |buttonsSize| to |canonicalIndex| - + 1. -
        6. -
        -
      4. -
      -

      - Otherwise, [=list/append=] |rawInputIndex| to - |unmappedInputList|. -

      -
    2. -
    3. Increment |rawInputIndex|. -
    4. -
    -
  14. -
  15. Initialize |buttonIndex:long| to be 0. -
  16. -
  17. [=list/For each=] |rawInputIndex:long| of |unmappedInputList|: -
      -
    1. While |mappedIndexList| [=list/contain=]s |buttonIndex|: -
        -
      1. Increment |buttonIndex|. -
      2. -
      -
    2. -
    3. Set |gamepad|.{{Gamepad/[[buttonMapping]]}}[|rawInputIndex|] - to |buttonIndex|. -
    4. -
    5. [=list/Append=] |buttonIndex| to |mappedIndexList|. -
    6. -
    7. If |buttonIndex| + 1 is greater than |buttonsSize|, then set - |buttonsSize| to |buttonIndex| + 1. -
    8. -
    -
  18. -
  19. Initialize |buttons| to be an empty [=list=]. -
  20. -
  21. [=list/For each=] |buttonIndex:long| of [=the range=] from 0 to - |buttonsSize| − 1, [=list/append=] a [=new=] {{GamepadButton}} to - |buttons|. -
  22. -
  23. Return |buttons|. -
  24. -
-

- To initialize - touchEvents for a gamepad, run the following steps: -

-
    -
  1. If the |gamepad| has touch surfaces, initialize |gamepad|'s - {{Gamepad/touchEvents}} to an empty [=list=]. -
  2. -
  3. Otherwise, initialize |gamepad|'s {{Gamepad/touchEvents}} - null. -
  4. -
-
-
-
-

- GamepadButton Interface -

-

- This interface defines the state of an individual button on a gamepad - device. -

-
-        [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.0 - - A {{double}} representing the button value scaled to the range [0.0 - .. 1.0] -
-
-
- pressed attribute -
-
-

- The pressed state of the button. 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 {{GamepadButton/pressed}} getter steps are: -

-
    -
  1. Return [=this=].{{GamepadButton/[[pressed]]}}. -
  2. -
-
-
- touched attribute -
-
-

- The touched state of the button. If the button is capable of - detecting touch, this property MUST be `true` if the button is - currently being touched, and `false` otherwise. If the button is - not capable of detecting touch and is capable of reporting an - analog value, this property MUST be `true` if the value property is - greater than 0, and `false` if the value is 0. If the button is not - capable of detecting touch and can only report a digital value, - this property MUST mirror the {{GamepadButton/pressed}} attribute. -

-

- The {{GamepadButton/touched}} getter steps are: -

-
    -
  1. Return [=this=].{{GamepadButton/[[touched]]}}. -
  2. -
-
-
- value attribute -
-
-

- For buttons that have an analog sensor, this property MUST - represent the amount which the button has been pressed. All button - values MUST be linearly normalized to the range [0.0 .. 1.0]. 0.0 - MUST mean fully unpressed, and 1.0 MUST mean fully pressed. For - buttons without an analog sensor, only the values 0.0 and 1.0 for - fully unpressed and fully pressed respectively, MUST be provided. -

-

- The {{GamepadButton/value}} getter steps are: -

-
    -
  1. Return [=this=].{{GamepadButton/[[value]]}}. -
  2. -
-
-
-
-
-

- GamepadTouch Interface -

-

- This interface defines a single touch event on a gamepad device that - supports input. The event consists of a touch id that uniquely - identifies the touch point from the time the input medium (e.g. finger, - stylus, etc) makes contact with the touch device, up to the time the - input medium is no longer making contact with the touch device. -

-
-        [Exposed=Window, SecureContext]
-        interface GamepadTouch {
-          readonly attribute unsigned long touchId;
-          readonly attribute octet surfaceId;
-          readonly attribute Float32Array position;
-          readonly attribute Uint32Array? surfaceDimensions;
-        };
-      
-
-
- touchId attribute -
-
- Unique id of the touch event. Range is [0, 4294967295]. -

- The user agent is responsible for: -

-
    -
  • Incrementing the id for each subsequent touch event based on - information provided by the device API. -
  • -
  • Maintaining the uniqueness of the id per origin to prevent - fingerprinting. -
  • -
{{GamepadTouch/touchId}} SHOULD be set to a default value of 0 - when a new {{Gamepad}} object is created. -
-
- surfaceId -
-
- Unique id of the surface that generated the touch event. -
-
- position -
-
- x, y coordinates of the touch event. Range of each coordinate is - normalized to [-1.0, 1.0]. Along the x-axis, -1.0 references the - leftmost coordinate and 1.0 references the rightmost coordinate. - Along the y-axis, -1.0 references the topmost coordinate and 1.0 - references the bottommost coordinate. MUST be a two-element array. -
-
- surfaceDimensions -
-
- Width and height of the touch surface in integer units. When not - null, MUST be a two-element array. -
-
-
-
-

- GamepadMappingType enum -

-

- This enum defines the set of known mappings for a Gamepad. -

-
-        enum GamepadMappingType {
-          "",
-          "standard",
-          "xr-standard",
-        };
-      
-
-
- "" -
-
- The empty string indicates that no mapping is in use for this - gamepad. -
-
- "standard" -
-
- The Gamepad's controls have been mapped to the [=Standard Gamepad=] - layout. -
-
- "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-1]]]. Gamepads returned by - {{Navigator/getGamepads()}} MUST NOT report a {{Gamepad/mapping}} of - {{GamepadMappingType/"xr-standard"}}. -
-
-
-
-

- GamepadHapticActuator Interface -

-

- A {{GamepadHapticActuator}} corresponds to a configuration of motors or - other actuators that can apply a force for the purposes of haptic - feedback. -

-
-        [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 {{Gamepad/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 {{Gamepad/GamepadHapticEffectType}} values representing - all the types of haptic effects that the actuator supports. This - property lists the {{Gamepad/GamepadHapticEffectType}} values that - the actuator supports, unless the [=user agent=] does not support - playing effects of that type. -

-

- The {{GamepadHapticActuator/effects}} getter steps are: -

-
    -
  1. Return [=this=].{{GamepadHapticActuator/[[effects]]}}. -
  2. -
-
-
- playEffect() method -
-
-

- The {{GamepadHapticActuator/playEffect()}} method steps, called - with {{GamepadHapticEffectType}} |type:GamepadHapticEffectType| and - {{GamepadEffectParameters}} |params:GamepadEffectParameters |, are: -

-
    -
  1. If |params:GamepadEffectParameters| does not describe a [=valid - effect=] of type |type:GamepadHapticEffectType|, return [=a promise - rejected with=] a {{TypeError}}. -
  2. -
  3. Let |document:Document?| be the [=current settings object=]'s - [=relevant global object=]'s [=associated `Document`=]. -
  4. -
  5. If |document| is `null` or |document| is not [=Document/fully - active=] or |document|'s [=Document/visibility state=] is - `"hidden"`, return [=a promise rejected with=] an - "{{InvalidStateError}}" {{DOMException}}. -
  6. -
  7. If [=this=].{{GamepadHapticActuator/[[playingEffectPromise]]}} - is not `null`: -
      -
    1. Let |effectPromise| be - [=this=].{{GamepadHapticActuator/[[playingEffectPromise]]}}. -
    2. -
    3. Set - [=this=].{{GamepadHapticActuator/[[playingEffectPromise]]}} to - `null`. -
    4. -
    5. [=Queue a global task=] on the [=relevant global object=] - of [=this=] using the [=gamepad task source=] to [=resolve=] - |effectPromise| with {{GamepadHapticsResult/"preempted"}}. -
    6. -
    -
  8. -
  9. If |this|'s gamepad's actuator cannot [=play effects with - type=] |type|, return [=a promise rejected with=] reason - {{NotSupportedError}}. -
  10. -
  11. Let {{GamepadHapticActuator/[[playingEffectPromise]]}} be [=a - new promise=]. -
  12. -
  13. Let |playEffectTimestamp:DOMHighResTimestamp| be the [=current - high resolution time=] given the |document|'s [=relevant global - object=]. -
  14. -
  15. Do the following steps [=in parallel=]: -
      -
    1. [=Issue a haptic effect=] to the actuator with |type|, - |params|, and the |playEffectTimestamp|. -
    2. -
    3. When the effect completes, if - [=this=].{{GamepadHapticActuator/[[playingEffectPromise]]}} is - not `null`, [=queue a global task=] on the [=relevant global - object=] of [=this=] using the [=gamepad task source=] to run - the following steps: -
        -
      1. If - [=this=].{{GamepadHapticActuator/[[playingEffectPromise]]}} - is `null`, abort these steps. -
      2. -
      3. [=Resolve=] - [=this=].{{GamepadHapticActuator/[[playingEffectPromise]]}} - with {{GamepadHapticsResult/"complete"}}. -
      4. -
      5. Set - [=this=].{{GamepadHapticActuator/[[playingEffectPromise]]}} - to `null`. -
      6. -
      -
    4. -
    -
  16. -
  17. Return {{GamepadHapticActuator/[[playingEffectPromise]]}}. -
  18. -
-
-
- reset() method -
-
-

- The {{GamepadHapticActuator/reset()}} method steps are: -

-
    -
  1. Let |document:Document?| be the [=current settings object=]'s - [=relevant global object=]'s [=associated `Document`=]. -
  2. -
  3. If |document| is `null` or |document| is not [=Document/fully - active=] or |document|'s [=Document/visibility state=] is - `"hidden"`, return [=a promise rejected with=] an - "{{InvalidStateError}}" {{DOMException}}. -
  4. -
  5. Let |resetResultPromise:Promise| be [=a new promise=]. -
  6. -
  7. If [=this=].{{GamepadHapticActuator/[[playingEffectPromise]]}} - is not `null`, do the following steps [=in parallel=]: -
      -
    1. Let |effectPromise| be - [=this=].{{GamepadHapticActuator/[[playingEffectPromise]]}}. -
    2. -
    3. [=Stop haptic effects=] on [=this=]'s gamepad's actuator. -
    4. -
    5. If the effect has been successfully stopped, do: -
        -
      1. If |effectPromise| and - [=this=].{{GamepadHapticActuator/[[playingEffectPromise]]}} - are still the same, set - [=this=].{{GamepadHapticActuator/[[playingEffectPromise]]}} - to `null`. -
      2. -
      3. [=Queue a global task=] on the [=relevant global - object=] of [=this=] using the [=gamepad task source=] to - [=resolve=] |effectPromise| with - {{GamepadHapticsResult/"preempted"}}. -
      4. -
      -
    6. -
    7. [=Resolve=] |resetResultPromise| with - {{GamepadHapticsResult/"complete"}} -
    8. -
    -
  8. -
  9. Return |resetResultPromise|. -
  10. -
-
-
-

- A {{GamepadHapticActuator}} can play effects with type - |type:GamepadHapticEffectType| if |type:GamepadHapticEffectType| can be - found in the {{GamepadHapticActuator/[[effects]]}} [=list=]. -

-

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

-
    -
  1. Given the value of {{GamepadHapticEffectType}} - |type:GamepadHapticEffectType|, switch on: -
    -
    - {{GamepadHapticEffectType/"dual-rumble"}} -
    -
    - If |params| does not describe a [=valid dual-rumble effect=], - return `false`. -
    -
    - {{GamepadHapticEffectType/"trigger-rumble"}} -
    -
    - If |params| does not describe a [=valid trigger-rumble effect=], - return `false`. -
    -
    -
  2. -
  3. Return `true` -
  4. -
-

- To issue a haptic effect on an actuator, the [=user agent=] - MUST send a command to the device to render an effect of - |type:GamepadHapticEffectType| and try to make it use the provided - |params:GamepadEffectParameters|. The [=user agent=] SHOULD use the - provided |playEffectTimestamp:DOMHighResTimestamp| for more precise - playback timing when |params|.{{GamepadEffectParameters/startDelay}} is - not `0.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 an actuator, the [=user agent=] - MUST send a command to the device to abort any effects currently being - played. If a haptic effect was interrupted, the actuator SHOULD return - to a motionless state as quickly as possible. -

-
-

- Handling visibility change -

-

- When the |document|'s [=Document/visibility state=] becomes - `"hidden"`, run these steps for each {{GamepadHapticActuator}} - |actuator:GamepadHapticActuator|: -

-
    -
  1. If |actuator|.{{GamepadHapticActuator/[[playingEffectPromise]]}} - is `null`, abort these steps. -
  2. -
  3. [=Queue a global task=] on the [=relevant global object=] of - |actuator| using the [=gamepad task source=] to run the following - steps: -
      -
    1. If - |actuator|.{{GamepadHapticActuator/[[playingEffectPromise]]}} is - `null`, abort these steps. -
    2. -
    3. [=Resolve=] - |actuator|.{{GamepadHapticActuator/[[playingEffectPromise]]}} - with {{GamepadHapticsResult/"preempted"}}. -
    4. -
    5. Set - |actuator|.{{GamepadHapticActuator/[[playingEffectPromise]]}} to - `null`. -
    6. -
    -
  4. -
  5. [=Stop haptic effects=] on |actuator|. -
  6. -
-
-
-

- Constructing a `GamepadHapticActuator` -

-

- A new - |gamepadHapticActuator:GamepadHapticActuator| representing a - {{Gamepad}}'s primary vibration actuator is constructed by performing - the following steps: -

-
    -
  1. Let |gamepadHapticActuator:GamepadHapticActuator| be a newly - created {{GamepadHapticActuator}} instance. -
  2. -
  3. Let `supportedEffectsList` be an empty list. -
  4. -
  5. For each enum value |type:GamepadHapticEffectType| of - {{GamepadHapticEffectType}}, if the [=user agent=] can send a command - to initiate effects of that type on that actuator, append |type| to - `supportedEffectsList`. -
  6. -
  7. Set |gamepadHapticActuator|.{{GamepadHapticActuator/[[effects]]}} - to `supportedEffectsList`. -
  8. -
-
-
-
-

- GamepadHapticsResult Enum -

-
-        enum GamepadHapticsResult {
-          "complete",
-          "preempted"
-        };
-      
-
-
- complete -
-
-

- The haptic effected completed playing. -

-
-
- preempted -
-
-

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

-
-
-
-
-

- GamepadHapticEffectType enum -

-

- The effect type defines how the effect parameters are interpreted by - the actuator. -

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

- {{GamepadHapticEffectType/"dual-rumble"}} describes a haptic - configuration with an eccentric rotating mass (ERM) vibration motor - in each handle of a standard 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 {{GamepadHapticEffectType/"dual-rumble"}} effect is a - fixed-duration, constant-intensity vibration effect intended for an - actuator of this type. {{GamepadHapticEffectType/"dual-rumble"}} - effects are defined by {{GamepadEffectParameters/startDelay}}, - {{GamepadEffectParameters/duration}}, - {{GamepadEffectParameters/strongMagnitude}}, and - {{GamepadEffectParameters/weakMagnitude}}, none of which are - required because they default to 0. -

-

- {{GamepadEffectParameters/strongMagnitude}} and - {{GamepadEffectParameters/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:GamepadEffectParameters|, - a valid dual-rumble effect must have a [=valid - effect|valid=] {{GamepadEffectParameters/duration}}, a [=valid - effect|valid=] {{GamepadEffectParameters/startDelay}}, and both the - {{GamepadEffectParameters/strongMagnitude}} and the - {{GamepadEffectParameters/weakMagnitude}} must be in the range - `[0,1]`. -

-
-
- "trigger-rumble" effect type -
-
-

- {{GamepadHapticEffectType/"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 - {{GamepadHapticEffectType/"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 {{GamepadHapticEffectType/"trigger-rumble"}} effect is a - fixed-duration, constant-intensity vibration effect intended for an - actuator of this type. {{GamepadHapticEffectType/"trigger-rumble"}} - effects are defined by {{GamepadEffectParameters/startDelay}}, - {{GamepadEffectParameters/duration}}, - {{GamepadEffectParameters/strongMagnitude}}, - {{GamepadEffectParameters/weakMagnitude}}, - {{GamepadEffectParameters/leftTrigger}}, and - {{GamepadEffectParameters/rightTrigger}}, none of which are - required because they default to 0. -

-

- {{GamepadEffectParameters/startDelay}}, - {{GamepadEffectParameters/duration}}, - {{GamepadEffectParameters/strongMagnitude}}, - {{GamepadEffectParameters/weakMagnitude}} share the same definition - with {{GamepadHapticEffectType/"dual-rumble"}}. - {{GamepadEffectParameters/leftTrigger}} and - {{GamepadEffectParameters/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:GamepadEffectParameters|, - a valid trigger-rumble effect must have a [=valid - effect|valid=] {{GamepadEffectParameters/duration}}, a [=valid - effect|valid=] {{GamepadEffectParameters/startDelay}}, and the - {{GamepadEffectParameters/strongMagnitude}}, - {{GamepadEffectParameters/weakMagnitude}}, - {{GamepadEffectParameters/leftTrigger}}, and - {{GamepadEffectParameters/rightTrigger}} must be in the range - `[0,1]`. -

-
-
-
-
-

- GamepadEffectParameters Dictionary -

-

- A GamepadEffectParameters dictionary contains keys for - parameters used by haptic effects. The meaning of each key is defined - by the haptic effect, 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. -

-
-        dictionary 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 -
-
- {{GamepadEffectParameters/duration}} sets the duration of the - vibration effect in milliseconds. -
-
- startDelay member -
-
- {{GamepadEffectParameters/startDelay}} sets the duration of the delay - after {{GamepadHapticActuator/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 - {{GamepadHapticEffectType/"dual-rumble"}} or - {{GamepadHapticEffectType/"trigger-rumble"}} effect. -
-
- weakMagnitude member -
-
- The vibration magnitude for the high frequency rumble in a - {{GamepadHapticEffectType/"dual-rumble"}} or - {{GamepadHapticEffectType/"trigger-rumble"}} effect. -
-
- leftTrigger member -
-
- The vibration magnitude for the bottom left front button ([=canonical - index=] 6) rumble in a {{GamepadHapticEffectType/"trigger-rumble"}} - effect. -
-
- rightTrigger member -
-
- The vibration magnitude for the bottom right front button - ([=canonical index=] 7) rumble in a - {{GamepadHapticEffectType/"trigger-rumble"}} effect. -
-
-
-
-

- Extensions to the `Navigator` interface -

-
-        [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 - {{Gamepad/index}} attribute, or `null` for unassigned indices. -
-
-

- getGamepads() method -

-

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

-

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

- -

- The {{Navigator/getGamepads()}} method steps are: -

-
    -
  1. Let |doc| be the [=current global object=]'s [=associated - `Document`=]. -
  2. -
  3. If |doc| is `null` or |doc| is not [=Document/fully active=], - then return an empty [=list=]. -
  4. -
  5. If |doc| is not [=allowed to use=] the `"gamepad"` permission, - then [=exception/throw=] a {{"SecurityError"}} {{DOMException}} and - abort these steps. -
  6. -
  7. If [=this=].{{Navigator/[[hasGamepadGesture]]}} is `false`, then - return an empty [=list=]. -
  8. -
  9. Let |now:DOMHighResTimeStamp| be the [=current high resolution - time=]. -
  10. -
  11. [=list/For each=] |gamepad:Gamepad| of - [=this=].{{Navigator/[[gamepads]]}}: -
      -
    1. If |gamepad| is not `null` and - |gamepad|.{{Gamepad/[[exposed]]}} is `false`: -
        -
      1. Set |gamepad|.{{Gamepad/[[exposed]]}} to `true`. -
      2. -
      3. Set |gamepad|.{{Gamepad/[[timestamp]]}} to |now|. -
      4. -
      -
    2. -
    -
  12. -
  13. Return |gamepads|. -
  14. -
-

- A |gamepad: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 support a neutral - default value and have reported a {{GamepadButton/pressed}} value of - `false` at least once, a {{GamepadButton/pressed}} value of `true` - SHOULD be considered interaction. If a button does not support a - neutral default value (for example, a toggle switch), then a - {{GamepadButton/pressed}} value of `true` SHOULD NOT be considered - interaction. If a button has never reported a - {{GamepadButton/pressed}} value of `false` then it SHOULD NOT be - considered interaction. Axis movements SHOULD be considered - interaction if the axis supports a neutral default value, the current - displacement from neutral 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 support a neutral default value - (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. -

-
-
-
-

- GamepadEvent Interface -

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

- GamepadEventInit dictionary -

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

- Remapping -

-

- Each device manufacturer creates many different products and each has - unique styles and layouts of buttons and axes. 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 {{Gamepad/axes}} and - {{Gamepad/buttons}} should correspond as closely as possible to the - physical locations in the diagram below. Additionally, - {{Gamepad/mapping}} SHOULD be set to {{GamepadMappingType/"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 axes 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 buttons/axes and their physical - locations. -

-

- An axis input represents a Standard Gamepad axis if it - reports the input value 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 axes 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 value for a button or trigger, and the button or - trigger is located in approximately the same location as the - corresponding [=Standard Gamepad=] button. -

-

- 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) -
-
- -
- Visual representation of a [=Standard Gamepad=] layout. -
-
-
-

- 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 buttons and axes - defined in the [=Standard Gamepad=] layout even if more or fewer - inputs are present on the connected device. - [[FINGERPRINTING-GUIDANCE]] -

-
-
-
-

- Usage Examples -

-

- The example below demonstrates typical access to gamepads. Note the - relationship with the - {{AnimationFrameProvider/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);
-      
-
- Coordination with - requestAnimationFrame() -

- Interactive applications will typically be using the - {{AnimationFrameProvider/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. -

-
-
-
-

- The gamepadconnected event -

-

- When a gamepad becomes available on the system, run the following - steps: -

-
    -
  1. Let |document:Document?| be the [=current global object=]'s - [=associated `Document`=]; otherwise `null`. -
  2. -
  3. If |document| is not `null` and is not [=allowed to use=] the - `"gamepad"` permission, then abort these steps. -
  4. -
  5. [=Queue a task=] on the [=gamepad task source=] to perform the - following steps: -
      -
    1. Let |gamepad:Gamepad| be [=a new `Gamepad`=] representing the - gamepad. -
    2. -
    3. Let |navigator:Navigator| be |gamepad|'s [=relevant global - object=]'s {{Navigator}} object. -
    4. -
    5. Set - |navigator|.{{Navigator/[[gamepads]]}}[|gamepad|.{{Gamepad/index}}] - to |gamepad|. -
    6. -
    7. If |navigator|.{{Navigator/[[hasGamepadGesture]]}} is `true`: -
        -
      1. Set |gamepad|.{{Gamepad/[[exposed]]}} to `true`. -
      2. -
      3. If |document| is not `null` and is [=Document/fully - active=], then [=fire an event=] named {{gamepadconnected}} at - |gamepad|'s [=relevant global object=] using {{GamepadEvent}} - with its {{GamepadEvent/gamepad}} attribute initialized to - |gamepad|. -
      4. -
      -
    8. -
    -
  6. -
-

- 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 the user has - connected a gamepad. If a gamepad was already connected when the page - was loaded, the {{gamepadconnected}} event SHOULD be dispatched when - the user presses a button or moves an axis. -

-
-
-

- The gamepaddisconnected event -

-

- When a gamepad becomes unavailable on the system, run the following - steps: -

-
    -
  1. Let |gamepad:Gamepad| be the {{Gamepad}} representing the - unavailable device. -
  2. -
  3. [=Queue a task=] on the [=gamepad task source=] to perform the - following steps: -
      -
    1. Set |gamepad|.{{Gamepad/[[connected]]}} to `false`. -
    2. -
    3. Let |document:Document?| be |gamepad|'s [=relevant global - object=]'s [=associated `Document`=]; otherwise `null`. -
    4. -
    5. If |gamepad|.{{Gamepad/[[exposed]]}} is `true` and |document| - is not `null` and is [=Document/fully active=], then [=fire an - event=] named {{gamepaddisconnected}} at |gamepad|'s [=relevant - global object=] using {{GamepadEvent}} with its - {{GamepadEvent/gamepad}} attribute initialized to |gamepad|. -
    6. -
    7. Let |navigator:Navigator| be |gamepad|'s [=relevant global - object=]'s {{Navigator}} object. -
    8. -
    9. Set - |navigator|.{{Navigator/[[gamepads]]}}[|gamepad|.{{Gamepad/index}}] - to `null`. -
    10. -
    11. While |navigator|.{{Navigator/[[gamepads]]}} [=list/is not - empty=] and the last [=list/item=] of - |navigator|.{{Navigator/[[gamepads]]}} is `null`, [=list/remove=] - the last [=list/item=] of |navigator|.{{Navigator/[[gamepads]]}}. -
    12. -
    -
  4. -
-

- 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 is disconnected from the [=user agent=], 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}}. -

-
-
-

- 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. -

-
-
-

- 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. -

-
-        partial interface mixin WindowEventHandlers {
-          attribute EventHandler ongamepadconnected;
-          attribute EventHandler ongamepaddisconnected;
-        };
-      
-
-
-

- Integration with Permissions Policy -

-

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

-
-

- A [=document=]’s [=Document/permissions policy=] determines whether - any content in that document is allowed to access - {{Navigator/getGamepads()}}. If disabled in any document, no content - in the document will be [=allowed to use=] - {{Navigator/getGamepads()}}, nor will the {{gamepadconnected}} and - {{gamepaddisconnected}} events fire. -

-
-
-
-

- Glossary -

-

- touch surface is a surface that can detect contact from a - users fingers and report where on the surface the contact is made. -

-

- touch surface enumeration order is an ordered listing of all - the surfaces in a |gamepad|. -

-

- active touch point is defined in the Touch - Events - Level 2 specification. -

-
-
- -
-
-

- Acknowledgements -

-

- The following people contributed to the development of this document. -

- -
- - + + + + + Gamepad + + + + + + + +
+

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

+
+
+

+ This is a work in progress. +

+
+
+

+ Introduction +

+

+ Some [=user agent=]s 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. +

+
+
+

+ 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. +

+
+
+

+ Gamepad interface +

+

+ This interface defines an individual gamepad device. +

+
+        [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>? touchEvents;
+          [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]] + + `false` + + A flag indicating that the device is connected to the system +
+ [[\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 axes exposed by this device +
+ [[\buttons]] + + An empty [=sequence=] + + A [=sequence=] of {{GamepadButton}} objects representing the + current state of buttons exposed by this device +
+ [[\exposed]] + + `false` + + A flag indicating that the {{Gamepad}} object has been exposed to + script +
+ [[\axisMapping]] + + An empty [=ordered map=] + + Mapping from unmapped axis index to an index in the + {{Gamepad/axes}} array +
+ [[\axisMinimums]] + + An empty [=list=] + + A [=list=] containing the minimum logical value for each axis +
+ [[\axisMaximums]] + + An empty [=list=] + + A [=list=] containing the maximum logical value for each axis +
+ [[\buttonMapping]] + + An empty [=ordered map=] + + Mapping from unmapped button index to an index in the + {{Gamepad/buttons}} array +
+ [[\buttonMinimums]] + + An empty [=list=] + + A [=list=] containing the minimum logical value for each button. +
+ [[\buttonMaximums]] + + An empty [=list=] + + A [=list=] containing the maximum logical value for each button +
+ [[\touchEvents]] + + `null` + + List of generated touch events. +
+ [[\vibrationActuator]] + + undefined + + A {{GamepadHapticActuator}} object capable of generating a haptic + effect that vibrates the entire gamepad +
+
+
+ id attribute +
+
+

+ An identification string for the gamepad. This string identifies + the brand or style of connected gamepad device. +

+

+ The exact format of the {{Gamepad/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 + {{Gamepad/id}} string. +

+
+
+ index attribute +
+
+ The index of the gamepad in the {{Navigator}}. When multiple gamepads + are connected to a [=user agent=], indices MUST be assigned on a + first-come, first-serve basis, starting at zero. If a gamepad is + disconnected, previously assigned indices MUST NOT be reassigned to + gamepads that continue to be connected. However, if a gamepad is + disconnected, and subsequently the same or a different gamepad is + then connected, the lowest previously used index MUST be reused. +
+
+ connected attribute +
+
+

+ Indicates whether the physical device represented by this object is + still connected to the system. When a gamepad becomes unavailable, + whether by being physically disconnected, powered off or otherwise + unusable, the {{Gamepad/connected}} attribute MUST be set to + `false`. +

+

+ The {{Gamepad/connected}} getter steps are: +

+
    +
  1. Return [=this=].{{Gamepad/[[connected]]}}. +
  2. +
+
+
+ timestamp attribute +
+
+

+ The {{Gamepad/timestamp}} allows the author to determine the last + time the {{Gamepad/axes}} or {{Gamepad/buttons}} attribute for this + gamepad was updated. The value MUST be set to the [=current high + resolution time=] each time the system [=receives new button or + axis input values=] from the device. If no data has been received + from the hardware, {{Gamepad/timestamp}} MUST be the [=current high + resolution time=] at the time when the {{Gamepad}} was first made + available to script. +

+

+ [=User agent=]s SHOULD set a minimum resolution of |gamepad|'s + {{Gamepad/timestamp}} attribute to 5 microseconds, following + [[HR-TIME]]'s clock resolution recommendation. +

+

+ The {{Gamepad/timestamp}} getter steps are: +

+
    +
  1. Return [=this=].{{Gamepad/[[timestamp]]}}. +
  2. +
+
+
+ mapping attribute +
+
+

+ The mapping in use for this device. If the user agent has knowledge + of the layout of the device, then it SHOULD indicate that a mapping + is in use by setting {{Gamepad/mapping}} to the corresponding + {{GamepadMappingType}} value. +

+

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

+
    +
  1. If the button and axis layout of the gamepad device corresponds + with the [=Standard Gamepad=] layout, then return + {{GamepadMappingType/"standard"}}. +
  2. +
  3. Return {{GamepadMappingType/""}}. +
  4. +
+
+
+ axes attribute +
+
+

+ Array of values for all axes of the gamepad. All axis values MUST + be linearly normalized to the range [-1.0 .. 1.0]. If the + controller is perpendicular to the ground with the directional + stick pointing up, -1.0 SHOULD correspond to "forward" or "left", + and 1.0 SHOULD correspond to "backward" or "right". Axes that are + drawn from a 2D input device SHOULD appear next to each other in + the axes array, X then Y. It is RECOMMENDED that axes appear in + decreasing order of importance, such that element 0 and 1 typically + represent the X and Y axis of a directional stick. The same object + MUST be returned until the [=user agent=] needs to return different + values (or values in a different order). +

+

+ The {{Gamepad/axes}} getter steps are: +

+
    +
  1. Return [=this=].{{Gamepad/[[axes]]}}. +
  2. +
+
+
+ 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 {{Gamepad/buttons}} getter steps are: +

+
    +
  1. Return [=this=].{{Gamepad/[[buttons]]}}. +
  2. +
+
+
+ touchEvents +
+
+

+ A list of {{GamepadTouch}} events generated from all touch + surfaces. If the device does not support touch events, MUST be set + to `null`. +

+

+ The {{Gamepad/touchEvents}} getter steps are: +

+
    +
  1. If [=this=].{{Gamepad/[[touchEvents]]}} not `null` and not + empty, return [=this=].{{Gamepad/[[touchEvents]]}}. +
  2. +
  3. Otherwise return `null`. +
  4. +
+
+
+ vibrationActuator attribute +
+
+

+ A {{GamepadHapticActuator}} object that represents the device's + primary vibration actuator. +

+

+ The {{Gamepad/vibrationActuator}} getter steps are: +

+
    +
  1. Return [=this=].{{Gamepad/[[vibrationActuator]]}}. +
  2. +
+
+
+
+

+ Receiving inputs +

+

+ When the system receives new button or axis input values, + run the following steps: +

+
    +
  1. Let |gamepad:Gamepad| be the {{Gamepad}} object representing the + device that received new button or axis input values. +
  2. +
  3. [=Queue a task=] on the [=gamepad task source=] to [=update + gamepad state=] for |gamepad|. +
  4. +
+

+ To update gamepad state for |gamepad:Gamepad|, run the + following steps: +

+
    +
  1. Let |now:DOMHighResTimeStamp| be the [=current high resolution + time=]. +
  2. +
  3. Set |gamepad|.{{Gamepad/[[timestamp]]}} to |now|. +
  4. +
  5. Run the steps to [=map and normalize axes=] for |gamepad|. +
  6. +
  7. Run the steps to [=map and normalize buttons=] for |gamepad|. +
  8. +
  9. Run the steps to [=update touchEvents=] for |gamepad|. +
  10. +
  11. Let |navigator:Navigator| be |gamepad|'s [=relevant global + object=]'s {{Navigator}} object. +
  12. +
  13. If |navigator|.{{Navigator/[[hasGamepadGesture]]}} is `false` and + |gamepad| [=contains a gamepad user gesture=]: +
      +
    1. Set |navigator|.{{Navigator/[[hasGamepadGesture]]}} to + `true`. +
    2. +
    3. [=list/For each=] |connectedGamepad:Gamepad?| of + |navigator|.{{Navigator/[[gamepads]]}}: +
        +
      1. If |connectedGamepad| is not equal to `null`: +
          +
        1. Set |connectedGamepad|.{{Gamepad/[[exposed]]}} to + `true`. +
        2. +
        3. Set |connectedGamepad|.{{Gamepad/[[timestamp]]}} to + |now|. +
        4. +
        5. Let |document:Document?| be |gamepad|'s [=relevant + global object=]'s [=associated `Document`=]; otherwise + `null`. +
        6. +
        7. If |document| is not `null` and is [=Document/fully + active=], then [=queue a task=] on the [=gamepad task + source=] to [=fire an event=] named {{gamepadconnected}} + at |gamepad|'s [=relevant global object=] using + {{GamepadEvent}} with its {{GamepadEvent/gamepad}} + attribute initialized to |connectedGamepad|. +
        8. +
        +
      2. +
      +
    4. +
    +
  14. +
+

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

+
    +
  1. Let |axisValues:list| be a [=list=] of {{unsigned long}} values + representing the most recent logical axis input values for each axis + input of the device represented by |gamepad|. +
  2. +
  3. Let |maxRawAxisIndex:long| be the [=list/size=] of |axisValues| − + 1. +
  4. +
  5. [=list/For each=] |rawAxisIndex:long| of [=the range=] from 0 to + |maxRawAxisIndex|: +
      +
    1. Let |mappedIndex:long| be + |gamepad|.{{Gamepad/[[axisMapping]]}}[|rawAxisIndex|]. +
    2. +
    3. Let |logicalValue:unsigned long| be + |axisValues|[|rawAxisIndex|]. +
    4. +
    5. Let |logicalMinimum:unsigned long| be + |gamepad|.{{Gamepad/[[axisMinimums]]}}[|rawAxisIndex|]. +
    6. +
    7. Let |logicalMaximum:unsigned long| be + |gamepad|.{{Gamepad/[[axisMaximums]]}}[|rawAxisIndex|]. +
    8. +
    9. Let |normalizedValue:double| be 2 (|logicalValue| − + |logicalMinimum|) / (|logicalMaximum| − |logicalMinimum|) − 1. +
    10. +
    11. Set |gamepad|.{{Gamepad/[[axes]]}}[|axisIndex|] to be + |normalizedValue|. +
    12. +
    +
  6. +
+

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

+
    +
  1. Let |buttonValues:list| be a [=list=] of {{unsigned long}} values + representing the most recent logical button input values for each + button input of the device represented by |gamepad|. +
  2. +
  3. Let |maxRawButtonIndex:long| be the [=list/size=] of + |buttonValues| − 1. +
  4. +
  5. [=list/For each=] |rawButtonIndex:long| of [=the range=] from 0 + to |maxRawButtonIndex|: +
      +
    1. Let |mappedIndex:long| be + |gamepad|.{{Gamepad/[[buttonMapping]]}}[|rawButtonIndex|]. +
    2. +
    3. Let |logicalValue:unsigned long| be + |buttonValues|[|rawButtonIndex|]. +
    4. +
    5. Let |logicalMinimum:unsigned long| be + |gamepad|.{{Gamepad/[[buttonMinimums]]}}[|rawButtonIndex|]. +
    6. +
    7. Let |logicalMaximum:unsigned long| be + |gamepad|.{{Gamepad/[[buttonMaximums]]}}[|rawButtonIndex|]. +
    8. +
    9. Let |normalizedValue:double| be (|logicalValue| − + |logicalMinimum|) / (|logicalMaximum| − |logicalMinimum|). +
    10. +
    11. Let |button:GamepadButton| be + |gamepad|.{{Gamepad/[[buttons]]}}[|mappedIndex|]. +
    12. +
    13. Set |button|.{{GamepadButton/[[value]]}} to + |normalizedValue|. +
    14. +
    15. +

      + If the button has a digital switch to indicate a pure pressed + or released state, set |button|.{{GamepadButton/[[pressed]]}} + to `true` if the button is pressed or `false` if it is not + pressed. +

      +

      + Otherwise, set |button|.{{GamepadButton/[[pressed]]}} to + `true` if the value is above the [=button press threshold=] + or `false` if it is not above the threshold. +

      +
    16. +
    17. +

      + If the button is capable of detecting touch, set + |button|.{{GamepadButton/[[touched]]}} to `true` if the + button is currently being touched. +

      +

      + Otherwise, set |button|.{{GamepadButton/[[touched]]}} to + |button|.{{GamepadButton/[[pressed]]}}. +

      +
    18. +
    +
  6. +
+

+ When the user agent modifies the list of |active touch points| for a + touch surface by adding touch points, removing touch points, or + updating the values of existing touch points, update + touchEvents by running the following steps: +

+
    +
  1. Let |surfaceId:unsigned long| be 0. +
  2. +
  3. Remove any existing events from + {{Gamepad}}.{{Gamepad/[[touchEvents]]}}. +
  4. +
  5. Repeat the following steps for each [=touch surface=] on + |gamepad| in [=touch surface enumeration order=]: +
      +
    1. Let |surfaceDimensions| be an `null`. +
    2. +
    3. If the touch surface exposes maximum surface dimensions in + device units: +
        +
      1. Set |touchEvent|.{{GamepadTouch/surfaceDimensions}}[0] to + the maximum X dimension on the [=touch surface=] in device + units. +
      2. +
      3. Set |touchEvent|.{{GamepadTouch/surfaceDimensions}}[1] to + the maximum Y dimension on the [=touch surface=] in device + units. +
      4. +
      +
    4. +
    5. Repeat the following steps for each [=active touch point=] + reported by the |gamepad| for the current [=touch surface=]. +
        +
      1. Let |nextTouchId:unsigned long| be the next available + touchId for the |gamepad|. +
      2. +
      3. Let |touchEvent:GamepadTouch| be a {{GamepadTouch}}. +
      4. +
      5. Set |touchEvent|.{{GamepadTouch/surfaceId}} to be + |surfaceId|. +
      6. +
      7. If the touch data is part of an existing [=active touch + point=] tracked by the user agent: +
          +
        1. Set |touchEvent|.{{GamepadTouch/touchId}} to the + touchId of the [=active touch point=]. +
        2. +
        3. Otherwise, set touchId to nextTouchId and increment + nextTouchId. +

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

          +
        4. +
        +
      8. +
      9. Let |position| be a [=new=] {{Float32Array}} with length + 2. +
      10. +
      11. +

        + |position|[0] should be set to the device X coordinate + relative to the device [=touch surface=] and normalized + to [-1.0,1.0] where -1.0 is the leftmost coordinate and + 1.0 is the rightmost coordinate. +

        +

        + `position[0] = (2.0 * touchData.x / surfaceDimensions[0]) + - 1` +

        +
      12. +
      13. +

        + |position|[1] should be set to the device Y coordinate + relative to the device [=touch surface=] and normalized + to [-1.0,1.0] where -1.0 is the topmost coordinate and + 1.0 is the bottommost coordinate. +

        +

        + `position[1] = (2.0 * touchData.y / surfaceDimensions[1]) + - 1` +

        +
      14. +
      +
    6. +
    7. Set |touchEvent|.{{GamepadTouch/position}} to be + |position:Float32Array|. +
    8. +
    9. Add |touchEvent| to {{Gamepad}}.{{Gamepad/[[touchEvents]]}}. +
    10. +
    +
  6. +
  7. Increment |surfaceId| +
  8. +
  9. Increment |surfaceId| +
  10. +
+
+
+

+ Constructing a `Gamepad` +

+

+ A new `Gamepad` representing a connected gamepad device is + constructed by performing the following steps: +

+
    +
  1. Let |gamepad:Gamepad| be a newly created {{Gamepad}} instance: +
      +
    1. Initialize |gamepad|'s {{Gamepad/id}} attribute to an + identification string for the gamepad. +
    2. +
    3. Initialize |gamepad|'s {{Gamepad/index}} attribute to the + result of [=selecting an unused gamepad index=] for |gamepad|. +
    4. +
    5. Initialize |gamepad|'s {{Gamepad/mapping}} attribute to the + result of [=selecting a mapping=] for the gamepad device. +
    6. +
    7. Initialize |gamepad|.{{Gamepad/[[connected]]}} to `true`. +
    8. +
    9. Initialize |gamepad|.{{Gamepad/[[timestamp]]}} to the + [=current high resolution time=]. +
    10. +
    11. Initialize |gamepad|.{{Gamepad/[[axes]]}} to the result of + [=initializing axes=] for |gamepad|. +
    12. +
    13. Initialize |gamepad|.{{Gamepad/[[buttons]]}} to the result of + [=initializing buttons=] for |gamepad|. +
    14. +
    15. Initialize |gamepad|.{{Gamepad/[[touchEvents]]}} to the + result of [=initializing touchEvents=] for |gamepad|. +
    16. +
    17. Initialize |gamepad|.{{Gamepad/[[vibrationActuator]]}} + following the steps of [=constructing a GamepadHapticActuator=] + for |gamepad|. +
    18. +
    +
  2. +
  3. Return |gamepad|. +
  4. +
+

+ To select an unused + gamepad index for |gamepad:Gamepad|, run the following steps: +

+
    +
  1. Let |navigator:Navigator| be |gamepad|'s [=relevant global + object=]'s {{Navigator}} object. +
  2. +
  3. Let |maxGamepadIndex:long| be the [=list/size=] of + |navigator|.{{Navigator/[[gamepads]]}} − 1. +
  4. +
  5. [=list/For each=] |gamepadIndex:long| of [=the range=] from 0 to + |maxGamepadIndex|: +
      +
    1. If |navigator|.{{Navigator/[[gamepads]]}}[|gamepadIndex|] is + `null`, then return |gamepadIndex|. +
    2. +
    +
  6. +
  7. [=list/Append=] `null` to |navigator|.{{Navigator/[[gamepads]]}}. +
  8. +
  9. Return the [=list/size=] of + |navigator|.{{Navigator/[[gamepads]]}} − 1. +
  10. +
+

+ To initialize axes for + |gamepad:Gamepad|, run the following steps: +

+
    +
  1. Let |inputCount:long| be the number of axis inputs exposed by the + device represented by |gamepad|. +
  2. +
  3. Set |gamepad|.{{Gamepad/[[axisMinimums]]}} to a [=list=] of + {{unsigned long}} values with [=list/size=] equal to |inputCount| + containing minimum logical values for each of the axis inputs. +
  4. +
  5. Set |gamepad|.{{Gamepad/[[axisMaximums]]}} to a [=list=] of + {{unsigned long}} values with [=list/size=] equal to |inputCount| + containing maximum logical values for each of the axis inputs. +
  6. +
  7. Initialize |unmappedInputList| to be an empty [=list=]. +
  8. +
  9. Initialize |mappedIndexList| to be an empty [=list=]. +
  10. +
  11. Initialize |axesSize:long| to be 0. +
  12. +
  13. [=list/For each=] |rawInputIndex:long| of [=the range=] from 0 to + |inputCount| − 1: +
      +
    1. If the the gamepad axis at index |rawInputIndex| [=represents + a Standard Gamepad axis=]: +
        +
      1. Let |canonicalIndex:long| be the [=canonical index=] for + the axis. +
      2. +
      3. If |mappedIndexList| [=list/contain=]s |canonicalIndex|, + then append |rawInputIndex| to |unmappedInputList|. +

        + Otherwise: +

        +
          +
        1. Set + |gamepad|.{{Gamepad/[[axisMapping]]}}[|rawInputIndex|] to + |canonicalIndex|. +
        2. +
        3. [=list/Append=] |canonicalIndex| to + |mappedIndexList|. +
        4. +
        5. If |canonicalIndex| + 1 is greater than |axesSize|, + then set |axesSize| to |canonicalIndex| + 1. +
        6. +
        +
      4. +
      +

      + Otherwise, [=list/append=] |rawInputIndex| to + |unmappedInputList|. +

      +
    2. +
    +
  14. +
  15. Initialize |axisIndex:long| to be 0. +
  16. +
  17. [=list/For each=] |rawInputIndex:long| of |unmappedInputList|: +
      +
    1. While |mappedIndexList| [=list/contain=]s |axisIndex|: +
        +
      1. Increment |axisIndex|. +
      2. +
      +
    2. +
    3. Set |gamepad|.{{Gamepad/[[axisMapping]]}}[|rawInputIndex|] to + |axisIndex|. +
    4. +
    5. [=list/Append=] |axisIndex| to |mappedIndexList|. +
    6. +
    7. If |axisIndex| + 1 is greater than |axesSize|, then set + |axesSize| to |axisIndex| + 1. +
    8. +
    +
  18. +
  19. Initialize |axes| to be an empty [=list=]. +
  20. +
  21. [=list/For each=] |axisIndex:long| of [=the range=] from 0 to + |axesSize| − 1, [=list/append=] 0 to |axes|. +
  22. +
  23. Return |axes|. +
  24. +
+

+ To initialize buttons for a + gamepad, run the following steps: +

+
    +
  1. Let |inputCount:long| be the number of button inputs exposed by + the device represented by |gamepad|. +
  2. +
  3. Set |gamepad|.{{Gamepad/[[buttonMinimums]]}} to be a [=list=] of + {{unsigned long}} values with [=list/size=] equal to |inputCount| + containing minimum logical values for each of the button inputs. +
  4. +
  5. Set |gamepad|.{{Gamepad/[[buttonMaximums]]}} to be a [=list=] of + {{unsigned long}} values with [=list/size=] equal to |inputCount| + containing maximum logical values for each of the button inputs. +
  6. +
  7. Initialize |unmappedInputList| to be an empty [=list=]. +
  8. +
  9. Initialize |mappedIndexList| to be an empty [=list=]. +
  10. +
  11. Initialize |buttonsSize:long| to be 0. +
  12. +
  13. [=list/For each=] |rawInputIndex:long| of [=the range=] from 0 to + |inputCount| − 1: +
      +
    1. If the the gamepad button at index |rawInputIndex| + [=represents a Standard Gamepad button=]: +
        +
      1. Let |canonicalIndex:long| be the [=canonical index=] for + the button. +
      2. +
      3. If |mappedIndexList| [=list/contain=]s |canonicalIndex|, + then append |rawInputIndex| to |unmappedInputList|. +

        + Otherwise: +

        +
          +
        1. Set + |gamepad|.{{Gamepad/[[buttonMapping]]}}[|rawInputIndex|] + to |canonicalIndex|. +
        2. +
        3. [=list/Append=] |canonicalIndex| to + |mappedIndexList|. +
        4. +
        5. If |canonicalIndex| + 1 is greater than + |buttonsSize|, then set |buttonsSize| to |canonicalIndex| + + 1. +
        6. +
        +
      4. +
      +

      + Otherwise, [=list/append=] |rawInputIndex| to + |unmappedInputList|. +

      +
    2. +
    3. Increment |rawInputIndex|. +
    4. +
    +
  14. +
  15. Initialize |buttonIndex:long| to be 0. +
  16. +
  17. [=list/For each=] |rawInputIndex:long| of |unmappedInputList|: +
      +
    1. While |mappedIndexList| [=list/contain=]s |buttonIndex|: +
        +
      1. Increment |buttonIndex|. +
      2. +
      +
    2. +
    3. Set |gamepad|.{{Gamepad/[[buttonMapping]]}}[|rawInputIndex|] + to |buttonIndex|. +
    4. +
    5. [=list/Append=] |buttonIndex| to |mappedIndexList|. +
    6. +
    7. If |buttonIndex| + 1 is greater than |buttonsSize|, then set + |buttonsSize| to |buttonIndex| + 1. +
    8. +
    +
  18. +
  19. Initialize |buttons| to be an empty [=list=]. +
  20. +
  21. [=list/For each=] |buttonIndex:long| of [=the range=] from 0 to + |buttonsSize| − 1, [=list/append=] a [=new=] {{GamepadButton}} to + |buttons|. +
  22. +
  23. Return |buttons|. +
  24. +
+

+ To initialize + touchEvents for a gamepad, run the following steps: +

+
    +
  1. If the |gamepad| has touch surfaces, initialize |gamepad|'s + {{Gamepad/touchEvents}} to an empty [=list=]. +
  2. +
  3. Otherwise, initialize |gamepad|'s {{Gamepad/touchEvents}} + null. +
  4. +
+
+
+
+

+ GamepadButton Interface +

+

+ This interface defines the state of an individual button on a gamepad + device. +

+
+        [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.0 + + A {{double}} representing the button value scaled to the range [0.0 + .. 1.0] +
+
+
+ pressed attribute +
+
+

+ The pressed state of the button. 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 {{GamepadButton/pressed}} getter steps are: +

+
    +
  1. Return [=this=].{{GamepadButton/[[pressed]]}}. +
  2. +
+
+
+ touched attribute +
+
+

+ The touched state of the button. If the button is capable of + detecting touch, this property MUST be `true` if the button is + currently being touched, and `false` otherwise. If the button is + not capable of detecting touch and is capable of reporting an + analog value, this property MUST be `true` if the value property is + greater than 0, and `false` if the value is 0. If the button is not + capable of detecting touch and can only report a digital value, + this property MUST mirror the {{GamepadButton/pressed}} attribute. +

+

+ The {{GamepadButton/touched}} getter steps are: +

+
    +
  1. Return [=this=].{{GamepadButton/[[touched]]}}. +
  2. +
+
+
+ value attribute +
+
+

+ For buttons that have an analog sensor, this property MUST + represent the amount which the button has been pressed. All button + values MUST be linearly normalized to the range [0.0 .. 1.0]. 0.0 + MUST mean fully unpressed, and 1.0 MUST mean fully pressed. For + buttons without an analog sensor, only the values 0.0 and 1.0 for + fully unpressed and fully pressed respectively, MUST be provided. +

+

+ The {{GamepadButton/value}} getter steps are: +

+
    +
  1. Return [=this=].{{GamepadButton/[[value]]}}. +
  2. +
+
+
+
+
+

+ GamepadTouch Interface +

+

+ This interface defines a single touch event on a gamepad device that + supports input. The event consists of a touch id that uniquely + identifies the touch point from the time the input medium (e.g. finger, + stylus, etc) makes contact with the touch device, up to the time the + input medium is no longer making contact with the touch device. +

+
+        [Exposed=Window, SecureContext]
+        interface GamepadTouch {
+          readonly attribute unsigned long touchId;
+          readonly attribute octet surfaceId;
+          readonly attribute Float32Array position;
+          readonly attribute Uint32Array? surfaceDimensions;
+        };
+      
+
+
+ touchId attribute +
+
+ Unique id of the touch event. Range is [0, 4294967295]. +

+ The user agent is responsible for: +

+
    +
  • Incrementing the id for each subsequent touch event based on + information provided by the device API. +
  • +
  • Maintaining the uniqueness of the id per origin to prevent + fingerprinting. +
  • +
{{GamepadTouch/touchId}} SHOULD be set to a default value of 0 + when a new {{Gamepad}} object is created. +
+
+ surfaceId +
+
+ Unique id of the surface that generated the touch event. +
+
+ position +
+
+ x, y coordinates of the touch event. Range of each coordinate is + normalized to [-1.0, 1.0]. Along the x-axis, -1.0 references the + leftmost coordinate and 1.0 references the rightmost coordinate. + Along the y-axis, -1.0 references the topmost coordinate and 1.0 + references the bottommost coordinate. MUST be a two-element array. +
+
+ surfaceDimensions +
+
+ Width and height of the touch surface in integer units. When not + null, MUST be a two-element array. +
+
+
+
+

+ GamepadMappingType enum +

+

+ This enum defines the set of known mappings for a Gamepad. +

+
+        enum GamepadMappingType {
+          "",
+          "standard",
+          "xr-standard",
+        };
+      
+
+
+ "" +
+
+ The empty string indicates that no mapping is in use for this + gamepad. +
+
+ "standard" +
+
+ The Gamepad's controls have been mapped to the [=Standard Gamepad=] + layout. +
+
+ "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-1]]]. Gamepads returned by + {{Navigator/getGamepads()}} MUST NOT report a {{Gamepad/mapping}} of + {{GamepadMappingType/"xr-standard"}}. +
+
+
+
+

+ GamepadHapticActuator Interface +

+

+ A {{GamepadHapticActuator}} corresponds to a configuration of motors or + other actuators that can apply a force for the purposes of haptic + feedback. +

+
+        [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 {{Gamepad/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 {{Gamepad/GamepadHapticEffectType}} values representing + all the types of haptic effects that the actuator supports. This + property lists the {{Gamepad/GamepadHapticEffectType}} values that + the actuator supports, unless the [=user agent=] does not support + playing effects of that type. +

+

+ The {{GamepadHapticActuator/effects}} getter steps are: +

+
    +
  1. Return [=this=].{{GamepadHapticActuator/[[effects]]}}. +
  2. +
+
+
+ playEffect() method +
+
+

+ The {{GamepadHapticActuator/playEffect()}} method steps, called + with {{GamepadHapticEffectType}} |type:GamepadHapticEffectType| and + {{GamepadEffectParameters}} |params:GamepadEffectParameters |, are: +

+
    +
  1. If |params:GamepadEffectParameters| does not describe a [=valid + effect=] of type |type:GamepadHapticEffectType|, return [=a promise + rejected with=] a {{TypeError}}. +
  2. +
  3. Let |document:Document?| be the [=current settings object=]'s + [=relevant global object=]'s [=associated `Document`=]. +
  4. +
  5. If |document| is `null` or |document| is not [=Document/fully + active=] or |document|'s [=Document/visibility state=] is + `"hidden"`, return [=a promise rejected with=] an + "{{InvalidStateError}}" {{DOMException}}. +
  6. +
  7. If [=this=].{{GamepadHapticActuator/[[playingEffectPromise]]}} + is not `null`: +
      +
    1. Let |effectPromise| be + [=this=].{{GamepadHapticActuator/[[playingEffectPromise]]}}. +
    2. +
    3. Set + [=this=].{{GamepadHapticActuator/[[playingEffectPromise]]}} to + `null`. +
    4. +
    5. [=Queue a global task=] on the [=relevant global object=] + of [=this=] using the [=gamepad task source=] to [=resolve=] + |effectPromise| with {{GamepadHapticsResult/"preempted"}}. +
    6. +
    +
  8. +
  9. If |this|'s gamepad's actuator cannot [=play effects with + type=] |type|, return [=a promise rejected with=] reason + {{NotSupportedError}}. +
  10. +
  11. Let {{GamepadHapticActuator/[[playingEffectPromise]]}} be [=a + new promise=]. +
  12. +
  13. Let |playEffectTimestamp:DOMHighResTimestamp| be the [=current + high resolution time=] given the |document|'s [=relevant global + object=]. +
  14. +
  15. Do the following steps [=in parallel=]: +
      +
    1. [=Issue a haptic effect=] to the actuator with |type|, + |params|, and the |playEffectTimestamp|. +
    2. +
    3. When the effect completes, if + [=this=].{{GamepadHapticActuator/[[playingEffectPromise]]}} is + not `null`, [=queue a global task=] on the [=relevant global + object=] of [=this=] using the [=gamepad task source=] to run + the following steps: +
        +
      1. If + [=this=].{{GamepadHapticActuator/[[playingEffectPromise]]}} + is `null`, abort these steps. +
      2. +
      3. [=Resolve=] + [=this=].{{GamepadHapticActuator/[[playingEffectPromise]]}} + with {{GamepadHapticsResult/"complete"}}. +
      4. +
      5. Set + [=this=].{{GamepadHapticActuator/[[playingEffectPromise]]}} + to `null`. +
      6. +
      +
    4. +
    +
  16. +
  17. Return {{GamepadHapticActuator/[[playingEffectPromise]]}}. +
  18. +
+
+
+ reset() method +
+
+

+ The {{GamepadHapticActuator/reset()}} method steps are: +

+
    +
  1. Let |document:Document?| be the [=current settings object=]'s + [=relevant global object=]'s [=associated `Document`=]. +
  2. +
  3. If |document| is `null` or |document| is not [=Document/fully + active=] or |document|'s [=Document/visibility state=] is + `"hidden"`, return [=a promise rejected with=] an + "{{InvalidStateError}}" {{DOMException}}. +
  4. +
  5. Let |resetResultPromise:Promise| be [=a new promise=]. +
  6. +
  7. If [=this=].{{GamepadHapticActuator/[[playingEffectPromise]]}} + is not `null`, do the following steps [=in parallel=]: +
      +
    1. Let |effectPromise| be + [=this=].{{GamepadHapticActuator/[[playingEffectPromise]]}}. +
    2. +
    3. [=Stop haptic effects=] on [=this=]'s gamepad's actuator. +
    4. +
    5. If the effect has been successfully stopped, do: +
        +
      1. If |effectPromise| and + [=this=].{{GamepadHapticActuator/[[playingEffectPromise]]}} + are still the same, set + [=this=].{{GamepadHapticActuator/[[playingEffectPromise]]}} + to `null`. +
      2. +
      3. [=Queue a global task=] on the [=relevant global + object=] of [=this=] using the [=gamepad task source=] to + [=resolve=] |effectPromise| with + {{GamepadHapticsResult/"preempted"}}. +
      4. +
      +
    6. +
    7. [=Resolve=] |resetResultPromise| with + {{GamepadHapticsResult/"complete"}} +
    8. +
    +
  8. +
  9. Return |resetResultPromise|. +
  10. +
+
+
+

+ A {{GamepadHapticActuator}} can play effects with type + |type:GamepadHapticEffectType| if |type:GamepadHapticEffectType| can be + found in the {{GamepadHapticActuator/[[effects]]}} [=list=]. +

+

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

+
    +
  1. Given the value of {{GamepadHapticEffectType}} + |type:GamepadHapticEffectType|, switch on: +
    +
    + {{GamepadHapticEffectType/"dual-rumble"}} +
    +
    + If |params| does not describe a [=valid dual-rumble effect=], + return `false`. +
    +
    + {{GamepadHapticEffectType/"trigger-rumble"}} +
    +
    + If |params| does not describe a [=valid trigger-rumble effect=], + return `false`. +
    +
    +
  2. +
  3. Return `true` +
  4. +
+

+ To issue a haptic effect on an actuator, the [=user agent=] + MUST send a command to the device to render an effect of + |type:GamepadHapticEffectType| and try to make it use the provided + |params:GamepadEffectParameters|. The [=user agent=] SHOULD use the + provided |playEffectTimestamp:DOMHighResTimestamp| for more precise + playback timing when |params|.{{GamepadEffectParameters/startDelay}} is + not `0.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 an actuator, the [=user agent=] + MUST send a command to the device to abort any effects currently being + played. If a haptic effect was interrupted, the actuator SHOULD return + to a motionless state as quickly as possible. +

+
+

+ Handling visibility change +

+

+ When the |document|'s [=Document/visibility state=] becomes + `"hidden"`, run these steps for each {{GamepadHapticActuator}} + |actuator:GamepadHapticActuator|: +

+
    +
  1. If |actuator|.{{GamepadHapticActuator/[[playingEffectPromise]]}} + is `null`, abort these steps. +
  2. +
  3. [=Queue a global task=] on the [=relevant global object=] of + |actuator| using the [=gamepad task source=] to run the following + steps: +
      +
    1. If + |actuator|.{{GamepadHapticActuator/[[playingEffectPromise]]}} is + `null`, abort these steps. +
    2. +
    3. [=Resolve=] + |actuator|.{{GamepadHapticActuator/[[playingEffectPromise]]}} + with {{GamepadHapticsResult/"preempted"}}. +
    4. +
    5. Set + |actuator|.{{GamepadHapticActuator/[[playingEffectPromise]]}} to + `null`. +
    6. +
    +
  4. +
  5. [=Stop haptic effects=] on |actuator|. +
  6. +
+
+
+

+ Constructing a `GamepadHapticActuator` +

+

+ A new + |gamepadHapticActuator:GamepadHapticActuator| representing a + {{Gamepad}}'s primary vibration actuator is constructed by performing + the following steps: +

+
    +
  1. Let |gamepadHapticActuator:GamepadHapticActuator| be a newly + created {{GamepadHapticActuator}} instance. +
  2. +
  3. Let `supportedEffectsList` be an empty list. +
  4. +
  5. For each enum value |type:GamepadHapticEffectType| of + {{GamepadHapticEffectType}}, if the [=user agent=] can send a command + to initiate effects of that type on that actuator, append |type| to + `supportedEffectsList`. +
  6. +
  7. Set |gamepadHapticActuator|.{{GamepadHapticActuator/[[effects]]}} + to `supportedEffectsList`. +
  8. +
+
+
+
+

+ GamepadHapticsResult Enum +

+
+        enum GamepadHapticsResult {
+          "complete",
+          "preempted"
+        };
+      
+
+
+ complete +
+
+

+ The haptic effected completed playing. +

+
+
+ preempted +
+
+

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

+
+
+
+
+

+ GamepadHapticEffectType enum +

+

+ The effect type defines how the effect parameters are interpreted by + the actuator. +

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

+ {{GamepadHapticEffectType/"dual-rumble"}} describes a haptic + configuration with an eccentric rotating mass (ERM) vibration motor + in each handle of a standard 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 {{GamepadHapticEffectType/"dual-rumble"}} effect is a + fixed-duration, constant-intensity vibration effect intended for an + actuator of this type. {{GamepadHapticEffectType/"dual-rumble"}} + effects are defined by {{GamepadEffectParameters/startDelay}}, + {{GamepadEffectParameters/duration}}, + {{GamepadEffectParameters/strongMagnitude}}, and + {{GamepadEffectParameters/weakMagnitude}}, none of which are + required because they default to 0. +

+

+ {{GamepadEffectParameters/strongMagnitude}} and + {{GamepadEffectParameters/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:GamepadEffectParameters|, + a valid dual-rumble effect must have a [=valid + effect|valid=] {{GamepadEffectParameters/duration}}, a [=valid + effect|valid=] {{GamepadEffectParameters/startDelay}}, and both the + {{GamepadEffectParameters/strongMagnitude}} and the + {{GamepadEffectParameters/weakMagnitude}} must be in the range + `[0,1]`. +

+
+
+ "trigger-rumble" effect type +
+
+

+ {{GamepadHapticEffectType/"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 + {{GamepadHapticEffectType/"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 {{GamepadHapticEffectType/"trigger-rumble"}} effect is a + fixed-duration, constant-intensity vibration effect intended for an + actuator of this type. {{GamepadHapticEffectType/"trigger-rumble"}} + effects are defined by {{GamepadEffectParameters/startDelay}}, + {{GamepadEffectParameters/duration}}, + {{GamepadEffectParameters/strongMagnitude}}, + {{GamepadEffectParameters/weakMagnitude}}, + {{GamepadEffectParameters/leftTrigger}}, and + {{GamepadEffectParameters/rightTrigger}}, none of which are + required because they default to 0. +

+

+ {{GamepadEffectParameters/startDelay}}, + {{GamepadEffectParameters/duration}}, + {{GamepadEffectParameters/strongMagnitude}}, + {{GamepadEffectParameters/weakMagnitude}} share the same definition + with {{GamepadHapticEffectType/"dual-rumble"}}. + {{GamepadEffectParameters/leftTrigger}} and + {{GamepadEffectParameters/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:GamepadEffectParameters|, + a valid trigger-rumble effect must have a [=valid + effect|valid=] {{GamepadEffectParameters/duration}}, a [=valid + effect|valid=] {{GamepadEffectParameters/startDelay}}, and the + {{GamepadEffectParameters/strongMagnitude}}, + {{GamepadEffectParameters/weakMagnitude}}, + {{GamepadEffectParameters/leftTrigger}}, and + {{GamepadEffectParameters/rightTrigger}} must be in the range + `[0,1]`. +

+
+
+
+
+

+ GamepadEffectParameters Dictionary +

+

+ A GamepadEffectParameters dictionary contains keys for + parameters used by haptic effects. The meaning of each key is defined + by the haptic effect, 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. +

+
+        dictionary 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 +
+
+ {{GamepadEffectParameters/duration}} sets the duration of the + vibration effect in milliseconds. +
+
+ startDelay member +
+
+ {{GamepadEffectParameters/startDelay}} sets the duration of the delay + after {{GamepadHapticActuator/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 + {{GamepadHapticEffectType/"dual-rumble"}} or + {{GamepadHapticEffectType/"trigger-rumble"}} effect. +
+
+ weakMagnitude member +
+
+ The vibration magnitude for the high frequency rumble in a + {{GamepadHapticEffectType/"dual-rumble"}} or + {{GamepadHapticEffectType/"trigger-rumble"}} effect. +
+
+ leftTrigger member +
+
+ The vibration magnitude for the bottom left front button ([=canonical + index=] 6) rumble in a {{GamepadHapticEffectType/"trigger-rumble"}} + effect. +
+
+ rightTrigger member +
+
+ The vibration magnitude for the bottom right front button + ([=canonical index=] 7) rumble in a + {{GamepadHapticEffectType/"trigger-rumble"}} effect. +
+
+
+
+

+ Extensions to the `Navigator` interface +

+
+        [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 + {{Gamepad/index}} attribute, or `null` for unassigned indices. +
+
+

+ getGamepads() method +

+

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

+

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

+ +

+ The {{Navigator/getGamepads()}} method steps are: +

+
    +
  1. Let |doc| be the [=current global object=]'s [=associated + `Document`=]. +
  2. +
  3. If |doc| is `null` or |doc| is not [=Document/fully active=], + then return an empty [=list=]. +
  4. +
  5. If |doc| is not [=allowed to use=] the `"gamepad"` permission, + then [=exception/throw=] a {{"SecurityError"}} {{DOMException}} and + abort these steps. +
  6. +
  7. If [=this=].{{Navigator/[[hasGamepadGesture]]}} is `false`, then + return an empty [=list=]. +
  8. +
  9. Let |now:DOMHighResTimeStamp| be the [=current high resolution + time=]. +
  10. +
  11. [=list/For each=] |gamepad:Gamepad| of + [=this=].{{Navigator/[[gamepads]]}}: +
      +
    1. If |gamepad| is not `null` and + |gamepad|.{{Gamepad/[[exposed]]}} is `false`: +
        +
      1. Set |gamepad|.{{Gamepad/[[exposed]]}} to `true`. +
      2. +
      3. Set |gamepad|.{{Gamepad/[[timestamp]]}} to |now|. +
      4. +
      +
    2. +
    +
  12. +
  13. Return |gamepads|. +
  14. +
+

+ A |gamepad: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 support a neutral + default value and have reported a {{GamepadButton/pressed}} value of + `false` at least once, a {{GamepadButton/pressed}} value of `true` + SHOULD be considered interaction. If a button does not support a + neutral default value (for example, a toggle switch), then a + {{GamepadButton/pressed}} value of `true` SHOULD NOT be considered + interaction. If a button has never reported a + {{GamepadButton/pressed}} value of `false` then it SHOULD NOT be + considered interaction. Axis movements SHOULD be considered + interaction if the axis supports a neutral default value, the current + displacement from neutral 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 support a neutral default value + (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. +

+
+
+
+

+ GamepadEvent Interface +

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

+ GamepadEventInit dictionary +

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

+ Remapping +

+

+ Each device manufacturer creates many different products and each has + unique styles and layouts of buttons and axes. 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 {{Gamepad/axes}} and + {{Gamepad/buttons}} should correspond as closely as possible to the + physical locations in the diagram below. Additionally, + {{Gamepad/mapping}} SHOULD be set to {{GamepadMappingType/"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 axes 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 buttons/axes and their physical + locations. +

+

+ An axis input represents a Standard Gamepad axis if it + reports the input value 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 axes 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 value for a button or trigger, and the button or + trigger is located in approximately the same location as the + corresponding [=Standard Gamepad=] button. +

+

+ 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) +
+
+ +
+ Visual representation of a [=Standard Gamepad=] layout. +
+
+
+

+ 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 buttons and axes + defined in the [=Standard Gamepad=] layout even if more or fewer + inputs are present on the connected device. + [[FINGERPRINTING-GUIDANCE]] +

+
+
+
+

+ Usage Examples +

+

+ The example below demonstrates typical access to gamepads. Note the + relationship with the + {{AnimationFrameProvider/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);
+      
+
+ Coordination with + requestAnimationFrame() +

+ Interactive applications will typically be using the + {{AnimationFrameProvider/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. +

+
+
+
+

+ The gamepadconnected event +

+

+ When a gamepad becomes available on the system, run the following + steps: +

+
    +
  1. Let |document:Document?| be the [=current global object=]'s + [=associated `Document`=]; otherwise `null`. +
  2. +
  3. If |document| is not `null` and is not [=allowed to use=] the + `"gamepad"` permission, then abort these steps. +
  4. +
  5. [=Queue a task=] on the [=gamepad task source=] to perform the + following steps: +
      +
    1. Let |gamepad:Gamepad| be [=a new `Gamepad`=] representing the + gamepad. +
    2. +
    3. Let |navigator:Navigator| be |gamepad|'s [=relevant global + object=]'s {{Navigator}} object. +
    4. +
    5. Set + |navigator|.{{Navigator/[[gamepads]]}}[|gamepad|.{{Gamepad/index}}] + to |gamepad|. +
    6. +
    7. If |navigator|.{{Navigator/[[hasGamepadGesture]]}} is `true`: +
        +
      1. Set |gamepad|.{{Gamepad/[[exposed]]}} to `true`. +
      2. +
      3. If |document| is not `null` and is [=Document/fully + active=], then [=fire an event=] named {{gamepadconnected}} at + |gamepad|'s [=relevant global object=] using {{GamepadEvent}} + with its {{GamepadEvent/gamepad}} attribute initialized to + |gamepad|. +
      4. +
      +
    8. +
    +
  6. +
+

+ 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 the user has + connected a gamepad. If a gamepad was already connected when the page + was loaded, the {{gamepadconnected}} event SHOULD be dispatched when + the user presses a button or moves an axis. +

+
+
+

+ The gamepaddisconnected event +

+

+ When a gamepad becomes unavailable on the system, run the following + steps: +

+
    +
  1. Let |gamepad:Gamepad| be the {{Gamepad}} representing the + unavailable device. +
  2. +
  3. [=Queue a task=] on the [=gamepad task source=] to perform the + following steps: +
      +
    1. Set |gamepad|.{{Gamepad/[[connected]]}} to `false`. +
    2. +
    3. Let |document:Document?| be |gamepad|'s [=relevant global + object=]'s [=associated `Document`=]; otherwise `null`. +
    4. +
    5. If |gamepad|.{{Gamepad/[[exposed]]}} is `true` and |document| + is not `null` and is [=Document/fully active=], then [=fire an + event=] named {{gamepaddisconnected}} at |gamepad|'s [=relevant + global object=] using {{GamepadEvent}} with its + {{GamepadEvent/gamepad}} attribute initialized to |gamepad|. +
    6. +
    7. Let |navigator:Navigator| be |gamepad|'s [=relevant global + object=]'s {{Navigator}} object. +
    8. +
    9. Set + |navigator|.{{Navigator/[[gamepads]]}}[|gamepad|.{{Gamepad/index}}] + to `null`. +
    10. +
    11. While |navigator|.{{Navigator/[[gamepads]]}} [=list/is not + empty=] and the last [=list/item=] of + |navigator|.{{Navigator/[[gamepads]]}} is `null`, [=list/remove=] + the last [=list/item=] of |navigator|.{{Navigator/[[gamepads]]}}. +
    12. +
    +
  4. +
+

+ 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 is disconnected from the [=user agent=], 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}}. +

+
+
+

+ 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. +

+
+
+

+ 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. +

+
+        partial interface mixin WindowEventHandlers {
+          attribute EventHandler ongamepadconnected;
+          attribute EventHandler ongamepaddisconnected;
+        };
+      
+
+
+

+ Integration with Permissions Policy +

+

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

+
+

+ A [=document=]’s [=Document/permissions policy=] determines whether + any content in that document is allowed to access + {{Navigator/getGamepads()}}. If disabled in any document, no content + in the document will be [=allowed to use=] + {{Navigator/getGamepads()}}, nor will the {{gamepadconnected}} and + {{gamepaddisconnected}} events fire. +

+
+
+
+

+ Glossary +

+

+ touch surface is a surface that can detect contact from a + users fingers and report where on the surface the contact is made. +

+

+ touch surface enumeration order is an ordered listing of all + the surfaces in a |gamepad|. +

+

+ active touch point is defined in the Touch + Events - Level 2 specification. +

+
+
+ +
+
+

+ Acknowledgements +

+

+ The following people contributed to the development of this document. +

+ +
+ +