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 🙏

© 2025 – Pkg Stats / Ryan Hefner

gamepad-node

v1.1.0

Published

Browser Gamepad API implementation for Node.js with native SDL2 bindings

Downloads

43

Readme

gamepad-node

npm version CI License: ISC

W3C Gamepad API for Node.js using SDL2. Works exactly like the browser API, but better - every controller gets mapping: "standard", not just the handful browsers recognize.

Features

  • Browser-compatible API - navigator.getGamepads() works exactly like in browsers
  • Every controller gets mapping: "standard" - not just the 20-30 browsers recognize
  • Positional button mapping - buttons mapped by physical position (N/S/E/W), not labels (A/B/X/Y)
  • 2100+ controllers via SDL2's community database
  • 321 more via EmulationStation configs (Knulli + Batocera - position-aware!)
  • Generic fallback for everything else
  • Hot-plug support with connect/disconnect events
  • Vibration/rumble support with automatic hardware detection
  • CLI tester with positional labels (N/S/E/W)
  • Zero config - SDL2 downloads automatically

Why this exists

Browsers only give mapping: "standard" to about 20-30 controllers. Everyone else gets unpredictable button mappings and has to implement config screens. That sucks for game developers.

This library ensures every controller gets standard mappings. Your game code stays simple.

Install

npm install gamepad-node

SDL2 is installed automatically by @kmamal/sdl. No compilation, no config.

Usage

import { installNavigatorShim } from 'gamepad-node';

installNavigatorShim();

// Same API as browsers
setInterval(() => {
    const gamepads = navigator.getGamepads();

    for (const gamepad of gamepads) {
        if (!gamepad) continue;

        if (gamepad.buttons[0].pressed) {
            console.log('A button pressed');
        }

        const leftStickX = gamepad.axes[0];
        const leftStickY = gamepad.axes[1];
    }
}, 16);

Events

const manager = installNavigatorShim();

manager.on('gamepadconnected', (event) => {
    console.log('Connected:', event.gamepad.id);
});

manager.on('gamepaddisconnected', (event) => {
    console.log('Disconnected:', event.gamepad.id);
});

Rumble

const gamepad = navigator.getGamepads()[0];

// vibrationActuator is null if controller doesn't support rumble
if (gamepad?.vibrationActuator) {
    await gamepad.vibrationActuator.playEffect('dual-rumble', {
        duration: 200,
        strongMagnitude: 1.0,
        weakMagnitude: 0.5
    });
}

Test your controllers

npx gamepad-node

Shows all buttons, triggers, sticks, and d-pad in real-time. Face buttons labeled N/S/E/W (North/South/East/West) for positional clarity. Press R to test rumble (if supported).

How it works

Four-tier fallback system with positional mapping priority:

  1. SDL_GameController with rumble - Keep as-is for rumble support
  2. SDL_GameController without rumble + db.json match - Force joystick mode for position-aware EmulationStation mappings
  3. EmulationStation database (321 controllers) - Position-based remapping using community configs
  4. Fallback - Generic Xbox 360 / PS4 style mapping

Why positional mapping matters

The W3C Gamepad API spec defines buttons by physical position (0=bottom, 1=right, 2=left, 3=top), not by label. But manufacturers print different labels at the same positions:

  • Xbox: South=A, East=B, West=X, North=Y
  • Nintendo/8BitDo: South=B, East=A, West=Y, North=X

SDL's mapping database uses label-based matching (maps "A button"), which breaks for Nintendo-layout controllers. EmulationStation's database uses position-based matching (maps "south button"), which works universally.

When possible, we use position-aware mappings from EmulationStation. This ensures button 0 is always the bottom button, regardless of what letter is printed on it.

See docs/CONTROLLER_VS_JOYSTICK.md for technical details, or docs/MAPPED_CONTROLLERS.md for the full controller list.

Platform support

Works on macOS (Intel + Apple Silicon), Linux (x64 + arm64), and Windows (x64). SDL2 binaries are downloaded automatically.

Why "better than browsers"?

Most browsers only recognize about 20-30 controllers for standard mapping. Try plugging in a Logitech Precision or some retro USB adapter - you'll get mapping: "" and buttons all over the place.

This library gives every controller standard mappings using position-aware databases. Your game works with anything, zero config required.

Bonus: We also correctly detect rumble support. Browsers often expose vibrationActuator even when hardware doesn't support it - we set it to null if rumble isn't available.

Development

Pure JavaScript on top of @kmamal/sdl, no build step. Run npm install and you're good.

npm test              # Basic test
npm run test:events   # Events & rumble
npm run test:unit     # Unit tests
npx gamepad-node      # Interactive tester

Terminal Gaming

I'm building this as part of a terminal gaming platform. Combine gamepad-node with webaudio-node and some clever half-block rendering, and you can make full games that run via npx. Check out docs/TERMINAL_GAMING_PLATFORM.md if that sounds interesting.

Credits

Built on @kmamal's SDL2 bindings, which made this whole thing possible. Also using controller databases from SDL_GameControllerDB, Knulli, and Batocera.

License

ISC