tiny-essentials
Version:
Collection of small, essential scripts designed to be used across various projects. These simple utilities are crafted for speed, ease of use, and versatility.
981 lines (621 loc) โข 27.9 kB
Markdown
# ๐ฎ TinyGamepad
TinyGamepad is a powerful and flexible JavaScript library designed to manage gamepad, keyboard, and mouse inputs seamlessly.
It supports advanced features like input mapping, combo detection, haptic feedback, and multiple event callbacks โ all in a clean, easy-to-use API.
Perfect for game developers and interactive apps who want precise control over input devices without hassle.
## ๐ฎ Type Definitions
This section documents the key **types**, **structures**, and **callback signatures** used by **TinyGamepad** for handling gamepad, keyboard, and mouse inputs.
### ๐ `KeyStatus`
Represents the **status** of a key or button.
```ts
type KeyStatus = {
pressed: boolean; // Whether the key is currently pressed
value?: number; // Optional analog value
value2?: number; // Optional secondary analog value
};
```
### ๐ฏ `InputMode`
Defines the available **input modes**:
* `'gamepad-only'` โ Only gamepad input
* `'keyboard-only'` โ Only keyboard input
* `'both'` โ Accept both gamepad and keyboard
```ts
type InputMode = 'gamepad-only' | 'keyboard-only' | 'both';
```
### ๐บ๏ธ `MappedInputCallback`
Callback fired when a **mapped logical input** (e.g., `"Jump"`, `"Shoot"`) is activated or deactivated.
```ts
type MappedInputCallback = (payload: {
logicalName: string; // Logical input name
activeTime: number; // Time active in milliseconds
comboTime: number; // Combo sequence timing
}) => void;
```
### โจ๏ธ `MappedKeyCallback`
Callback fired when a **mapped key** (e.g., `"KeyA"`, `"KeyB"`) is activated or deactivated.
```ts
type MappedKeyCallback = (payload: {
key: string; // Physical key name
activeTime: number; // Time active in milliseconds
}) => void;
```
### ๐ฆ `PayloadCallback`
Generic callback for handling **input events**:
```ts
type PayloadCallback = (payload: InputPayload | InputAnalogPayload) => void;
```
### ๐ `ConnectionCallback`
Callback for **gamepad connection events**:
```ts
type ConnectionCallback = (payload: ConnectionPayload) => void;
```
### ๐น๏ธ `InputSequenceCallback`
Fired when a **registered input sequence** is fully activated:
```ts
type InputSequenceCallback = (timestamp: number) => void;
```
### โจ๏ธ `KeySequenceCallback`
Fired when a **registered key sequence** is fully activated:
```ts
type KeySequenceCallback = (timestamp: number) => void;
```
### ๐ `CallbackList`
Union of all possible **callback types** in the event system:
```ts
type CallbackList =
| ConnectionCallback
| PayloadCallback
| MappedInputCallback
| MappedKeyCallback;
```
### ๐ฎ `GamepadDeviceSource`
Specific gamepad input sources:
* `'gamepad-analog'` โ Analog sticks / triggers
* `'gamepad-button'` โ Digital buttons
```ts
type GamepadDeviceSource = 'gamepad-analog' | 'gamepad-button';
```
### ๐ฑ๏ธ `DeviceSource`
All possible **physical input sources**:
* `'mouse'`
* `'keyboard'`
* `'gamepad-analog'`
* `'gamepad-button'`
```ts
type DeviceSource = 'mouse' | 'keyboard' | GamepadDeviceSource;
```
### ๐ `DeviceInputType`
Describes **input interaction types**:
* `'up'` โ Released
* `'down'` โ Pressed
* `'hold'` โ Held
* `'change'` โ Value changed
* `'move'` โ Analog motion detected
```ts
type DeviceInputType = 'up' | 'down' | 'hold' | 'change' | 'move';
```
### ๐ `InputPayload`
Payload structure for **digital button input events**:
```ts
type InputPayload = {
id: string;
event?: Event;
type: DeviceInputType;
source: DeviceSource;
key: string;
isPressure: boolean;
logicalName: string;
value: number;
value2: number;
pressed: boolean;
prevPressed?: boolean | null;
timestamp: number;
gp?: Gamepad;
};
```
### ๐ `InputAnalogPayload`
Payload structure for **analog input events**:
```ts
type InputAnalogPayload = {
id: string;
event?: Event;
type: DeviceInputType;
source: DeviceSource;
key: string;
logicalName: string;
value: number;
value2: number;
timestamp: number;
gp?: Gamepad;
};
```
### ๐๏ธ `InputEvents`
Internal structure for **digital input tracking**:
```ts
type InputEvents = {
id: string;
event?: Event;
key: string;
source: DeviceSource;
value: number;
value2: number;
type: DeviceInputType;
isPressure: boolean;
pressed: boolean;
prevPressed?: boolean | null;
timestamp: number;
gp?: Gamepad;
};
```
### ๐๏ธ `InputAnalogEvents`
Internal structure for **analog input tracking**:
```ts
type InputAnalogEvents = {
id: string;
event?: Event;
key: string;
source: DeviceSource;
value: number;
value2: number;
type: DeviceInputType;
timestamp: number;
gp?: Gamepad;
};
```
### ๐ `ConnectionPayload`
Payload for **gamepad connection events**:
```ts
type ConnectionPayload = {
id: string;
timestamp: number;
gp: Gamepad;
};
```
### โ๏ธ `ExportedConfig`
Structure for **exporting and importing TinyGamepad configurations**:
```ts
type ExportedConfig = {
expectedId: string | null; // Expected gamepad ID or null
ignoreIds: string[]; // IDs to ignore
deadZone: number; // Analog dead zone threshold
timeoutComboKeys: number; // Max time between combo inputs
axisActiveSensitivity: number; // Analog movement sensitivity
inputMap: [string, string | string[]][]; // Logical-to-physical mappings
};
```
## ๐ฎ Constructor & Input Event Initialization
### โ๏ธ Constructor
```js
constructor({
expectedId = null,
inputMode = 'both',
ignoreIds = [],
deadZone = 0.1,
axisActiveSensitivity = 0.3,
timeoutComboKeys = 500,
allowMouse = false,
elementBase = window,
} = {}) { ... }
```
Initializes a new instance of **TinyGamepad** with customizable input behavior.
#### ๐ Parameters:
| Name | Type | Default | Description | | |
| ----------------------- | ------------------- | -------------- | ----------------------------------------------------------------------------------- | -------- | ------------------------------------ |
| `expectedId` | `string \| null` | `null` | Specific controller ID to expect; filters gamepads. | | |
| `inputMode` | \`'keyboard-only' | 'gamepad-only' | 'both'\` | `'both'` | Selects input sources to listen for. |
| `ignoreIds` | `string[]` | `[]` | List of device IDs to ignore during detection. | | |
| `deadZone` | `number` | `0.1` | Threshold under which analog stick inputs are ignored (0 to 1). | | |
| `axisActiveSensitivity` | `number` | `0.3` | Sensitivity threshold to detect meaningful analog axis movement (0 most sensitive). | | |
| `timeoutComboKeys` | `number` | `500` | Time (ms) allowed between inputs in a key sequence before reset. | | |
| `allowMouse` | `boolean` | `false` | Whether mouse input events should be treated as triggers. | | |
| `elementBase` | `Window \| Element` | `window` | DOM element or window to bind keyboard and mouse event listeners. | | |
#### ๐จ Validation:
* Throws `TypeError` or `RangeError` on invalid types or out-of-range values.
* Ensures only valid configurations are accepted.
#### ๐ฅ Behavior:
* Initializes gamepad event listeners if mode includes `'gamepad-only'` or `'both'`.
* Initializes keyboard and mouse listeners if mode includes `'keyboard-only'` or `'both'`.
## ๐ฎ Input Mapping & Sequence Management
### โณ `awaitInputMapping({ timeout, eventName, canMove })`
Waits for a single input event from the user and resolves with detailed input info.
Useful for input configuration screens where the user chooses a key/button to assign.
#### โ๏ธ Parameters (all optional):
| Name | Type | Default | Description |
| ----------- | ------- | ---------------- | -------------------------------------------------------- |
| `timeout` | number | `10000` ms | How long to wait before resolving with `null` (no input) |
| `eventName` | string | `'MappingInput'` | Temporary logical event name used internally |
| `canMove` | boolean | `false` | Whether to accept movement inputs like mouse move |
#### ๐ Returns:
A `Promise` that resolves with an object:
```ts
{
key: string | null, // Input identifier ("KeyW", "Button0", "LeftClick")
source: DeviceSource | null, // Input origin: "keyboard", "mouse", "gamepad-button", "gamepad-analog"
gp?: Gamepad // Gamepad object if the source is a gamepad
}
```
### ๐ ๏ธ `mapInput(logicalName, physicalInput)`
Maps one or multiple physical inputs to a logical input name.
| Parameter | Type | Description |
| --------------- | ---------------------- | --------------------------------------------------------------- |
| `logicalName` | `string` | Logical input identifier (e.g. "Jump") |
| `physicalInput` | `string` or `string[]` | Physical input(s) mapped (e.g. "Button1", \["KeyW", "ArrowUp"]) |
### โ `unmapInput(logicalName)`
Removes the mapping for the specified logical input.
| Parameter | Type | Description |
| ------------- | -------- | ---------------------------------- |
| `logicalName` | `string` | Logical input identifier to remove |
### ๐ `hasMappedInput(logicalName): boolean`
Checks if a logical input name has any physical inputs mapped.
| Parameter | Type | Returns |
| ------------- | -------- | ------------------------------------------- |
| `logicalName` | `string` | `true` if mapping exists, otherwise `false` |
### ๐ `getMappedInput(logicalName): string | string[]`
Gets the physical input(s) mapped to a logical input name.
| Parameter | Type | Returns |
| ------------- | -------- | ------------------------ |
| `logicalName` | `string` | Mapped physical input(s) |
### ๐งน `clearMapInputs()`
Clears **all** logical to physical input mappings.
## ๐ Input Sequences โ Logical & Physical
#### โก๏ธ `registerInputSequence(sequence, callback)`
Registers a sequence of logical inputs that triggers a callback when fully held.
| Parameter | Type | Description |
| ---------- | ----------------------- | ----------------------------------------------------------- |
| `sequence` | `string[]` | Ordered list of logical inputs (e.g., `['Jump', 'Action']`) |
| `callback` | `InputSequenceCallback` | Function invoked when sequence is triggered |
#### โ `unregisterInputSequence(sequence)`
Removes a registered logical input sequence.
#### ๐งน `unregisterAllInputSequences()`
Removes **all** registered logical input sequences.
#### ๐ `hasInputSequence(sequence): boolean`
Checks if a logical input sequence is registered.
#### โก๏ธ `registerKeySequence(sequence, callback)`
Same as `registerInputSequence` but for **physical key** sequences.
#### โ `unregisterKeySequence(sequence)`
Unregisters a physical key sequence.
#### ๐งน `unregisterAllKeySequences()`
Removes all physical key sequences.
#### ๐ `hasKeySequence(sequence): boolean`
Checks if a physical key sequence is registered.
## โณ Combo Logical Keys Management
#### ๐ `renewComboMapped()`
Refreshes the timer that tracks the currently held combo logical keys.
Useful to prevent premature combo resets when keys are still being held.
#### โ `resetComboMapped()`
Resets and clears all current combo keys and combo inputs, stopping timers and resetting timestamps.
## ๐ฏ Event Listener Methods for Input Events
This section covers how to register, unregister, and manage callbacks for various input-related events.
Each event type has methods for regular (`on`), one-time (`once`), prepend, and removal of callbacks (`off`).
### ๐ Mapped Key Events
#### `onMappedKeyStart(callback)`
Register callback for when a mapped key is **pressed down**.
#### `onceMappedKeyStart(callback)`
Register a one-time callback that triggers once when a mapped key is pressed.
#### `prependMappedKeyStart(callback)`
Adds a callback at the start of the callback list for mapped key start.
#### `offMappedKeyStart(callback)`
Removes a specific callback from mapped key start event.
#### `offAllMappedKeyStart()`
Removes **all** callbacks from mapped key start event.
#### `onMappedKeyEnd(callback)`
Register callback for when a mapped key is **released**.
#### `onceMappedKeyEnd(callback)`
One-time callback for mapped key release.
#### `prependMappedKeyEnd(callback)`
Prepends callback for mapped key release event.
#### `offMappedKeyEnd(callback)`
Removes a callback from mapped key release event.
#### `offAllMappedKeyEnd()`
Removes all callbacks from mapped key release event.
### ๐๏ธ Mapped Input Events (includes buttons, analogs, sensors, etc.)
#### `onMappedInputStart(callback)`
Triggered when any mapped input is activated (pressed or engaged).
#### `onceMappedInputStart(callback)`
One-time callback for mapped input activation.
#### `prependMappedInputStart(callback)`
Prepends callback for mapped input start.
#### `offMappedInputStart(callback)`
Removes a callback from mapped input start.
#### `offAllMappedInputStart()`
Removes all callbacks from mapped input start.
#### `onMappedInputEnd(callback)`
Triggered when a mapped input is deactivated (released or disengaged).
#### `onceMappedInputEnd(callback)`
One-time callback for mapped input release.
#### `prependMappedInputEnd(callback)`
Prepends callback for mapped input end.
#### `offMappedInputEnd(callback)`
Removes a callback from mapped input end.
#### `offAllMappedInputEnd()`
Removes all callbacks from mapped input end.
### ๐งฉ Logical Input Events by Name
These methods use the logical input names to manage callbacks for different stages of input:
| Event Type | Description |
| --------------- | ------------------------------------------------- |
| `onInput` | Callback on **any** input event |
| `onInputStart` | Callback when input is **pressed down** |
| `onInputEnd` | Callback when input is **released** |
| `onInputHold` | Callback when input is **held down** |
| `onInputChange` | Callback on **value changes** (e.g. analog move) |
| `onInputMove` | Callback on **movement inputs** (e.g. mouse move) |
#### Common method patterns for logical inputs:
* `on<EventType>(logicalName, callback)`
Register a callback for the event.
* `once<EventType>(logicalName, callback)`
Register a one-time callback for the event.
* `prepend<EventType>(logicalName, callback)`
Add a callback to the **start** of the callback list.
* `off<EventType>(logicalName, callback)`
Remove a specific callback.
#### Example: Logical Input "Jump" pressed down event
```js
gamepad.onInputStart('Jump', callback);
gamepad.onceInputStart('Jump', callback);
gamepad.prependInputStart('Jump', callback);
gamepad.offInputStart('Jump', callback);
```
#### Detailed List of Methods:
| Method | Description |
| ------------------------------------- | ----------------------------- |
| `onInput(logicalName, callback)` | On any input event |
| `onceInput(logicalName, callback)` | One-time on any input |
| `prependInput(logicalName, callback)` | Prepend callback on any input |
| `offInput(logicalName, callback)` | Remove callback on any input |
| Method | Description |
| ------------------------------------------ | ------------------------------ |
| `onInputStart(logicalName, callback)` | On input pressed down |
| `onceInputStart(logicalName, callback)` | One-time on input pressed down |
| `prependInputStart(logicalName, callback)` | Prepend on input pressed down |
| `offInputStart(logicalName, callback)` | Remove on input pressed down |
| Method | Description |
| ---------------------------------------- | -------------------------- |
| `onInputEnd(logicalName, callback)` | On input released |
| `onceInputEnd(logicalName, callback)` | One-time on input released |
| `prependInputEnd(logicalName, callback)` | Prepend on input released |
| `offInputEnd(logicalName, callback)` | Remove on input released |
| Method | Description |
| ----------------------------------------- | ------------------------ |
| `onInputHold(logicalName, callback)` | While input is held down |
| `onceInputHold(logicalName, callback)` | One-time on hold |
| `prependInputHold(logicalName, callback)` | Prepend on hold |
| `offInputHold(logicalName, callback)` | Remove on hold |
| Method | Description |
| ------------------------------------------- | ------------------------------------------- |
| `onInputChange(logicalName, callback)` | On input value change (e.g., analog change) |
| `onceInputChange(logicalName, callback)` | One-time on input change |
| `prependInputChange(logicalName, callback)` | Prepend on input change |
| `offInputChange(logicalName, callback)` | Remove on input change |
| Method | Description |
| ----------------------------------------- | ------------------------------------ |
| `onInputMove(logicalName, callback)` | On movement input (e.g., mouse move) |
| `onceInputMove(logicalName, callback)` | One-time on move |
| `prependInputMove(logicalName, callback)` | Prepend on move |
| `offInputMove(logicalName, callback)` | Remove on move |
## ๐ Callback Management for Logical Inputs
These methods help you inspect and control the callbacks registered for logical inputs and their event types.
### ๐ Get Registered Callbacks
#### `getCalls(logicalName, type = 'all')`
Returns a **shallow clone** of the callback list for a given logical input and event type.
* `logicalName`: Logical input name (e.g., "Jump")
* `type`: Event type filter โ `'all' | 'start' | 'end' | 'hold' | 'change' | 'move'` (default: `'all'`)
* Returns an array of registered callback functions.
### โ Remove All Callbacks for an Input
#### `offAllInputs(logicalName, type = 'all')`
Removes **all callbacks** for a specific logical input and event type.
* `logicalName`: Logical input name
* `type`: Event type filter (same as `getCalls`)
### ๐ข Count Registered Callbacks
#### `getCallSize(logicalName, type = 'all')`
Returns the number of callbacks registered for the given logical input and event type.
## ๐ฎ Haptic Feedback (Vibration) Control
### โ๏ธ Default Haptic Effect Settings
* Private field storing default effect type and parameters (duration, intensity, etc.)
### ๐ `get defaultHapticEffect()`
Returns the current default haptic feedback effect configuration, including effect type and parameters like duration and intensity.
### ๐ง `setDefaultHapticEffect(type, params)`
Sets the default haptic feedback effect for gamepad vibration.
* `type`: Effect type (e.g., `'dual-rumble'`)
* `params`: Effect parameters like duration, intensity
### ๐ต `hasHapticEffect()`
Checks if the currently connected gamepad supports vibration feedback.
* Returns: `true` if supported, `false` otherwise.
### ๐ณ `vibrate(params?, type?)`
Triggers a vibration on the connected gamepad using either custom or default parameters.
* Returns a Promise resolved when vibration completes.
## ๐ซ Ignoring Specific Input IDs
### `ignoreId(id)`
Adds an input ID to the ignored list (wonโt trigger events).
### `unignoreId(id)`
Removes an input ID from the ignored list.
## ๐ Gamepad Connection Events
### Connection Callbacks
| Method | Description |
| ---------------------- | -------------------------------------- |
| `onConnected(cb)` | Register callback on gamepad connected |
| `onceConnected(cb)` | One-time callback on connection |
| `prependConnected(cb)` | Prepend callback for connection |
| `offConnected(cb)` | Remove callback from connection event |
| `offAllConnected()` | Remove **all** connection callbacks |
### Disconnection Callbacks
| Method | Description |
| ------------------------- | ----------------------------------------- |
| `onDisconnected(cb)` | Register callback on gamepad disconnected |
| `onceDisconnected(cb)` | One-time callback on disconnection |
| `prependDisconnected(cb)` | Prepend callback for disconnection |
| `offDisconnected(cb)` | Remove callback from disconnection event |
| `offAllDisconnected()` | Remove **all** disconnection callbacks |
## ๐ฎ Gamepad Info and State
### `hasGamepad()`
Returns `true` if a gamepad is currently connected, else `false`.
### `getGamepad()`
Returns the connected `Gamepad` instance. Throws error if none connected.
### Button Last State Tracking
#### `hasLastButtonState(index)`
Checks if there is a recorded last state for button index.
#### `getLastButtonState(index)`
Returns a copy of the last known state for a button index. Throws if none recorded.
## โ๏ธ Configuration Export & Import
### ๐พ `exportConfig()`
Exports the current TinyGamepad settings as a plain object, ready for saving or serialization.
Includes: device filters, ignored IDs, input mappings, sensitivities, and timeouts.
**Returns:** An object snapshot of the configuration.
### ๐ฅ `importConfig(json)`
Imports and applies a configuration object or JSON string to update TinyGamepad settings.
* Accepts either a JSON string or a config object.
* Validates types for each property and throws if invalid.
* Updates internal settings accordingly.
## ๐ Callback Lists Getters
### ๐ Mapped Key Callbacks
* `mappedKeyStartCalls` โ Gets cloned list of callbacks for `"mapped-key-start"` event.
* `mappedKeyEndCalls` โ Gets cloned list of callbacks for `"mapped-key-end"` event.
### ๐ Combo Keys and Inputs
* `comboMappedKeys` โ Clone of currently held **key combo** logical inputs.
* `comboMappedInputs` โ Clone of currently held **combo logical inputs**.
## ๐ Input Sequence Info
### ๐ข Key Sequences
* `keySequenceSize` โ Number of registered input sequences.
* `keySequences` โ Clone array of all input sequence callbacks.
### ๐ Active Mapped Keys & Inputs
* `activeMappedKeys` โ Clone of currently held mapped logical keys.
* `activeMappedInputs` โ Clone of currently held mapped logical inputs.
## ๐ Mapped Input Callbacks
* `mappedInputStartCalls` โ Cloned callbacks for `"mapped-input-start"`.
* `mappedInputEndCalls` โ Cloned callbacks for `"mapped-input-end"`.
## ๐งฉ Input Sequences (Again)
* `inputSequenceSize` โ Number of registered input sequences (physical/logical).
* `inputSequences` โ Clone array of all input sequence callbacks.
## ๐บ๏ธ Logical-to-Physical Mapping Info
* `mappedInputs` โ Shallow clone object of all logical โ physical input mappings.
* `mappedInputSize` โ Number of logical inputs currently mapped.
## ๐ Connection Event Callbacks
* `connectedCalls` โ Clone of callbacks for `"connected"` event.
* `disconnectedCalls` โ Clone of callbacks for `"disconnected"` event.
## ๐ซ Ignored Devices & Held Keys
* `ignoredDeviceIds` โ Clone of currently ignored device IDs.
* `heldKeys` โ Clone of currently held raw keys.
## ๐ Event Counts & Sizes
* `eventsSize` โ Total number of registered callbacks (all events).
* `callSize` โ Number of unique event keys (types).
* `connectedCallSize` โ Number of callbacks registered for `"connected"`.
* `disconnectedCallSize` โ Number of callbacks registered for `"disconnected"`.
* `mappedKeyStartCallSize` โ Callbacks count for `"mapped-key-start"`.
* `mappedKeyEndCallSize` โ Callbacks count for `"mapped-key-end"`.
* `mappedInputStartCallSize` โ Callbacks count for `"mapped-input-start"`.
* `mappedInputEndCallSize` โ Callbacks count for `"mapped-input-end"`.
## ๐ฎ Last Button and Axis States
* `lastButtonStates` โ Snapshot array of button statuses from last update cycle.
* `lastAxes` โ Snapshot array of axis values (thumbsticks, triggers) from last update.
## ๐๏ธ Input Modes and Event Targets
* `inputMode` โ Current input mode (e.g., `'keyboard-only'`, `'gamepad-only'`, `'both'`).
* `elementBase` โ DOM element or window used for event bindings.
## โฐ Timestamps & Timeouts
* `timeComboInputs` โ Timestamp of last mapped input combo.
* `timeComboKeys` โ Timestamp of last raw key combo.
* `timeMappedInputs` โ Timestamp of last mapped input activity.
* `timeoutComboKeys` โ Timeout in ms before raw key combos reset (getter/setter).
## ๐๏ธ Sensitivity & Dead Zone Settings
* `axisActiveSensitivity` โ Sensitivity threshold for axis movement to count as "active" input (getter/setter).
* `deadZone` โ Dead zone threshold for analog inputs (getter/setter).
## ๐ Device Identification
* `expectedId` โ Expected device ID filter (getter/setter).
## ๐งน Lifecycle & Cleanup
### ๐ `isDestroyed`
**Getter** that returns whether this TinyGamepad instance has been destroyed.
* **Returns:** `true` if the instance has been cleaned up and disabled; otherwise `false`.
* Use this to check if the instance is still active or has been terminated.
### ๐๏ธ `destroy()`
Cleans up and completely stops all internal input monitoring and loops.
* Cancels any ongoing animation frames or loops.
* Removes all event listeners (keyboard, mouse, gamepad) based on current input mode.
* Resets combos and clears all internal state (maps, callbacks, keys, sequences, etc).
* Marks the instance as destroyed (`isDestroyed` becomes `true`).
* After calling this, the instance should no longer be used.
โจ **Tip:** Always call `destroy()` when you want to cleanly remove TinyGamepad and free resources, especially before discarding the instance or when switching contexts.