Gameinputjs
Javascript library to improve the Gamepad API
Install / Use
/learn @lunarcloud/GameinputjsREADME
<img src="img/generic.png" width="48" /> Game-Input JavaScript Library
A client-side JavaScript module one can import to add good gamepad support to web-powered games or other gamepad-powered web applications.
Installation
GameinputJS's src and img folders need to be accessible in the distributed version of your website.
I would recommend setting up a package.json script to help update a distributable copy of the library outside node_modules:
{
"name": "@me/my-client-webapp",
"scripts": {
"deps2lib": "shx rm -rf lib && shx mkdir lib && shx cp -r node_modules/gameinputjs lib/"
}
...
}
Which would allow you to run:
npm i --save-dev shx
npm i gameinputjs
npm run deps2lib
Usage
Import from within a Javascript module, construct it, and then use either the events or make a game loop that uses the properties.
import { GameInput, DetectedOS } from './lib/gameinputjs/src/gameinput.js'
import { GameInputSchemaSectionNames, GameInputSchemaButtonNames } from './lib/gameinputjs/src/gameinput-schema.js'
const gameInput = new GameInput()
Configuration Options
You can configure GameInput by passing options to the constructor:
const gameInput = new GameInput({
debugStatements: false, // Enable debug console logs (default: false)
maxPlayers: 8 // Force max players (normally auto-detected, 4-8 on supported browsers)
})
maxPlayers
GameInput supports at least 4 players, up to 8 when detected on supported browsers. The maxPlayers option forces a specific maximum, overriding auto-detection.
Example:
// Auto-detect (recommended)
const gameInput = new GameInput()
// Force a specific maximum
const gameInput = new GameInput({ maxPlayers: 16 })
// Access all detected players
for (const player of gameInput.Players) {
if (player.model) {
console.log(`Player ${player.number} connected`)
}
}
Event-Driven Style
gameInput
.onReinitialize(() => {
console.debug("Players updated")
const firstPlayer = gameInput.getPlayer(0)
if (!firstPlayer?.model) {
noPlayers()
return
}
displayButtons(firstPlayer)
document.querySelector('img.gamepad').src = `img/${firstPlayer?.model?.iconName ?? 'generic'}.png`
})
.onButtonDown((playerIndex, sectionName, buttonName) => {
const player = gameInput.getPlayer(playerIndex)
console.debug(`Player ${player} pushed ${player.getButtonText(sectionName, buttonName)} (${buttonName})`)
})
.onButtonUp((playerIndex, sectionName, buttonName) => {
const player = gameInput.getPlayer(playerIndex)
console.debug(`Player ${player} released ${player.getButtonText(sectionName, buttonName)} (${buttonName})`)
if (sectionName === 'center' && buttonName === 'menu') {
console.debug('menu requested')
return
}
if (sectionName === 'face' && buttonName === player.schema.ordinalButton(0)) {
console.debug('Jump / Confirm pushed')
}
})
Game-Loop Style
function gameLoop() {
for (const player of gameInput.Players) {
if (!player)
continue
if (player.state.face.ordinal(0))
player.rumble({ duration: 200, weakMagnitude: 1.0, strongMagnitude: 0.25 })
const leftStick = player.getStickVector('left')
console.debug(`Player left stick vector is ${leftStick.toString()}`)
requestAnimationFrame(() => gameLoop())
}
}
requestAnimationFrame(() => gameLoop()) // kick off
Lifecycle Management
GameInputJS provides proper cleanup APIs to prevent memory leaks in Single Page Applications (SPAs).
Unsubscribing from Events
Event listener methods (onButtonDown, onButtonUp, onReinitialize) return an unsubscribe function:
// Subscribe to an event
const unsubscribe = gameInput.onButtonDown((playerIndex, sectionName, buttonName) => {
console.debug(`Button ${buttonName} pressed`)
})
// Later: unsubscribe when no longer needed
unsubscribe()
Destroying the GameInput Instance
When you're done with a GameInput instance (e.g., navigating away from a game page), call destroy() to clean up all resources:
// Clean up everything
gameInput.destroy()
This will:
- Stop the update loop and connection watch loop
- Remove all event listeners (both custom and window events)
- Clear all gamepad and player references
Example: Cleanup in a SPA
// In your route setup
function initGame() {
const gameInput = new GameInput()
gameInput.onButtonDown((player, section, button) => {
// Handle input
})
return gameInput
}
// When navigating away
function cleanupGame(gameInput) {
gameInput.destroy()
}
