npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

dm-inputs

v0.1.2

Published

A comprehensive, high-performance, and type-safe input management library for browser-based applications and games.

Readme

DM-Inputs: A Unified Input Management Library

DM-Inputs is a high-performance, declarative, and unified input management library for browser-based applications and games. It provides a single, consistent API to handle a wide variety of input devices, including keyboard, mouse, touch, gamepads, VR controllers, device sensors, and geolocation.

Designed with a state-oriented approach, it simplifies complex input logic by abstracting away event listeners and focusing on "what is the current state of the input?" This makes it ideal for game loops and real-time applications.


✨ Features

  • Unified API: A single InputEvent format and KeyId system for all input types.
  • Comprehensive Device Support:
    • Keyboard (layout-independent via event.code)
    • Mouse (buttons, wheel, and movement tracking)
    • Multi-Touch (with pressure/force support)
    • Joystick / Gamepad API
    • WebXR (VR Headsets and Controllers)
    • Device Sensors (Accelerometer, Gyroscope, Orientation)
    • Geolocation
  • Performance-Oriented: Uses object pooling for events to minimize garbage collection and ensure smooth performance, even with input bursts.
  • Declarative & Stateful: Easily check for complex key combinations (isCombinationPressed, wasCombinationJustPressed, wasCombinationJustReleased).
  • Frame-Based Event Queue: Process inputs that happened "this frame" in a simple loop.
  • Tree-Shakable: Only include the input modules you need.
  • TypeScript Native: Strong typing for robust development.

🚀 Quick Start

The core idea is to create a shared InputsState object, initialize the input modules you need, and then check the state or consume events in your main application loop.

import {
    createInputsState,
    initInputKeyboard,
    initInputMouse,
    pendingInputsConsume,
    isCombinationPressed,
    wasCombinationJustPressed,
    KeyW,
    KeyMouseLeft,
    KeyCtrl,
    JustPressed
} from 'dm-inputs';

// --- 1. Setup ---
const canvas = document.getElementById('my-canvas');
const state = createInputsState();

// Initialize the input modules you want to use.
// The `destroy` function cleans up all event listeners.
const { destroy: destroyKeyboard } = initInputKeyboard(canvas, state);
const { destroy: destroyMouse } = initInputMouse(canvas, state);

// --- 2. Game Loop ---
function gameLoop() {
    // --- A: Process discrete events that happened since the last frame ---
    let event;
    while ((event = pendingInputsConsume(state))) {
        if (event.state === JustPressed && event.keyId === KeyMouseLeft) {
            console.log(`Mouse clicked at (${event.x}, ${event.y})`);
        }
    }
    
    // --- B: Check the current, persistent state of inputs ---

    // Is the 'W' key being held down right now?
    if (state.keysPressed.has(KeyW)) {
        console.log("Moving forward!");
    }

    // Was the combination Ctrl+W just completed in this frame?
    if (wasCombinationJustPressed(state, [KeyCtrl, KeyW])) {
        console.log("Just pressed Ctrl+W!");
    }

    // Is the Ctrl key currently held down? (Using the combination checker for one key)
    if (isCombinationPressed(state, [KeyCtrl])) {
        // ...
    }
    
    requestAnimationFrame(gameLoop);
}

gameLoop();

// --- 3. Cleanup ---
// When your application closes, call the destroy functions.
// destroyKeyboard();
// destroyMouse();

🧠 Core Concepts

InputsState

This is the single source of truth for all input. It contains the queue of events that have occurred since the last frame (pendingInputs) and a map of all keys/buttons that are currently held down (keysPressed).

The Game Loop Lifecycle

A typical frame consists of two phases:

  1. Consume Events: You loop through pendingInputsConsume(state) to react to discrete events like a key being pressed or released this frame. This is where you use functions like wasCombinationJustPressed. Once you have consumed all events, the queue is cleared for the next frame.
  2. Check State: After processing events, you check the persistent state.keysPressed map for continuous actions, like moving a character while a key is held down. The isCombinationPressed function is a convenient helper for this.

KeyId

A KeyId is a unique number that identifies a specific input, regardless of the device. KeyW on the keyboard, KeyMouseLeft for the mouse, KeyJoy0Btn0 for a gamepad button, and KeySensorAccelerationX_P for a device sensor are all KeyIds. This allows for a completely unified system.

InputEvent

Every interaction generates an InputEvent. This object contains all the information about the event:

  • keyId: The KeyId of the input.
  • state: Property indicating a state change (JustPressed, JustReleased, JustUpdated).
  • pressure: A normalized value (0-1) representing button pressure, trigger pull, touch force, or sensor reading.
  • x, y: Coordinates relative to the target element.
  • char: The typed character for keyboard events, if applicable.

Cheat Sheet & Examples

Check if a key was just pressed this frame

if (wasCombinationJustPressed(state, [KeySpace])) {
    // Player jumped
}

Check if a key is currently held down

if (isCombinationPressed(state, [KeyW])) {
    // Move player forward
}

Check for a key combination (e.g., Ctrl + S)

// Was the combo just completed?
if (wasCombinationJustPressed(state, [KeyCtrl, KeyS])) {
    // Save game
}

// Is the combo currently being held down?
if (isCombinationPressed(state, [KeyCtrl, KeyS])) {
    // Show "Saving..." indicator
}

Get Mouse Position

The latest mouse position is stored in the KeyMouseMove event if the mouse is moving.

const moveEvent = state.keysPressed.get(KeyMouseMove);
if (moveEvent) {
    const { x, y } = moveEvent;
    // Update cursor position, aim direction, etc.
}

Iterate Through All Events

Useful for logging, debugging, or complex event processing.

let event;
while ((event = pendingInputsConsume(state))) {
    console.log(`Event for KeyId ${event.keyId}, State: ${event.state}`);
    // Handle the event...
}

Using a Gamepad

// In setup:
initInputJoystick(state);

// In loop:
// Check A/X button on most controllers
if (isCombinationPressed(state, [KeyJoy0Btn0])) { 
    // Fire weapon
}

// Get the left stick's horizontal axis value (-1 to 1)
const stickX = (state.keysPressed.get(KeyJoy0Axis0_P)?.pressure ?? 0) - (state.keysPressed.get(KeyJoy0Axis0_N)?.pressure ?? 0);
// stickX is now a value from -1 to 1 for the first joystick's horizontal axis.

Using VR (WebXR)

VR initialization is async and must be triggered by a user gesture.

vrButton.addEventListener('click', async () => {
    const { destroy } = await initInputVR(state);
    // Now you can check VR keys in your game loop
});

// In loop:
// Check if the right controller trigger was just pulled
if (wasCombinationJustPressed(state, [KeyVRRightBtn0])) {
    // Grab object
}

📜 API Reference

Core Functions

  • createInputsState(maxEvents: number = 64): InputsState Creates the central state object. maxEvents is the initial size of the event pool, which can grow if needed.

  • pendingInputsConsume(state: InputsState): InputEvent | undefined Retrieves the next event from the pending queue. Returns undefined when the queue is empty. This is the primary way to process events per frame.

  • pendingInputsClear(state: InputsState) Manually clears the pending event queue. pendingInputsConsume handles this automatically when the queue is exhausted.

Combination Checkers

  • isCombinationPressed(state: InputsState, keys: readonly KeyId[]): boolean Checks if all keys in the combination are currently held down.

  • wasCombinationJustPressed(state: InputsState, keys: readonly KeyId[]): boolean Checks if the combination was just completed in the current frame (i.e., one key was pressed while the others were already down).

  • wasCombinationJustReleased(state: InputsState, keys: readonly KeyId[]): boolean Checks if the combination was just broken in the current frame (i.e., one key was released while the others were held).

Initialization Functions

All init functions return an object with a destroy method to clean up event listeners.

  • initInputKeyboard(element: HTMLElement, state: InputsState)
  • initInputMouse(element: HTMLElement, state: InputsState, options?: MouseOptions)
  • initInputTouch(element: HTMLElement, state: InputsState, options?: TouchOptions)
  • initInputJoystick(state: InputsState, options?: JoystickOptions)
  • initInputAccelerometer(state: InputsState, options?: SensorOptions)
  • initInputGyroscope(state: InputsState, options?: SensorOptions)
  • initInputOrientation(state: InputsState)
  • initInputGeolocation(state: InputsState, options?: GeolocationOptions)
  • initInputVR(state: InputsState, options?: VROptions): Promise<{ destroy: () => void }> (Asynchronous)

KeyId Constants

Each module exports its own set of KeyId constants for easy access.

  • Keyboard: KeyA, KeyB, ..., KeySpace, KeyShift, KeyEnter, etc.
  • Mouse: KeyMouseLeft, KeyMouseRight, KeyMouseMiddle, KeyMouseWheelUp, KeyMouseWheelDown, KeyMouseMove.
  • Touch: KeyTouch0...KeyTouch9, KeyTouchMove0...KeyTouchMove9.
  • Joystick: KeyJoy0Btn0...KeyJoy7Btn19, KeyJoy0Axis0_P, KeyJoy0Axis0_N, etc.
  • Sensors: KeyAccelerometerLinearX_P, KeyGyroscopeYaw_P, KeyOrientationHeading, etc.
  • Geolocation: KeyGeolocationChange, KeyGeolocationAccuracy.
  • VR: KeyVRHmdPositionX, KeyVRLeftBtn0 (Trigger), KeyVRRightAxis2_P (Right Thumbstick X+), etc.