Skip to main content
dev-phaser-input-handlers Keyboard, mouse, touch, and gamepad input management for Phaser
Jump to install Skills Marketplace Discover and explore AI skills built by the community.
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Copy promptShow prompt details A direct command skips the review prompt. Inspect the source before running it.
npx skills add https://github.com/feliperyba/ralph-orchestra --skill dev-phaser-input-handlersThe command stays on one line. Scroll horizontally to inspect it before copying.
Prefer a local copy? Download the files currently available to SkillsMP.
Download Zip Downloading... More from this repository Complete Developer workflow orchestration - task research sequence, implementation flow, validation gates, PRD synchronization, exit conditions.
Complete Game Designer workflow - skill invocation protocol, GDD creation, playtest flow with GDD review, design sessions. MUST load before starting assignments.
Complete PM Coordinator workflow - task assignment, project orchestration, PRD management, worker coordination. Use proactively when starting PM agent work.
Related occupations SOC
Based on SOC occupation classification
name dev-phaser-input-handlers description Keyboard, mouse, touch, and gamepad input management for Phaser
Phaser Input Handlers
"Responsive controls for every platform and input method."
Before/After: Manual DOM Events vs Phaser Input System
❌ Before: Manual DOM Event Listeners
const keys : { [key : string ]: boolean } = {};
document .addEventListener ('keydown' , (e ) => {
keys[e.key ] = true ;
keys[e.code ] = true ;
if (e.key === 'ArrowLeft' ) player.x -= 5 ;
if (e.key === 'ArrowRight' ) player.x += 5 ;
if (e.code === 'KeyW' ) player.y -= 5 ;
});
. ( , {
keys[e. ] = ;
keys[e. ] = ;
});
canvas. ( , {
(e. , e. );
});
canvas. ( , {
touch = e. [ ];
(touch. , touch. );
});
document
addEventListener
'keyup'
(e ) =>
key
false
code
false
addEventListener
'mousedown'
(e ) =>
shoot
clientX
clientY
addEventListener
'touchstart'
(e ) =>
const
touches
0
shoot
clientX
clientY
✅ After: Phaser Unified Input System
export class GameScene extends Phaser.Scene {
private cursors!: Phaser .Types .Input .Keyboard .CursorKeys ;
private gamepadIndex = 0 ;
private inputManager!: InputManager ;
create ( ) {
this .cursors = this .input .keyboard !.createCursorKeys ();
this .inputManager = new InputManager (this );
this .input .on ('pointerdown' , (pointer : Phaser .Input .Pointer ) => {
this .shoot (pointer.x , pointer.y );
});
this .input .gamepad !.on ('connected' , (pad ) => {
console .log ('Gamepad connected:' , pad.id );
});
}
update ( ) {
const move = this .inputManager .getMovement ();
this .player .setVelocity (move.x * 200 , move.y * 200 );
if (this .inputManager .isJumpPressed ()) {
this .player .jump ();
}
}
}
When to Use This Skill
Implementing player controls
Handling keyboard input
Adding mouse/touch interaction
Supporting gamepad controllers
Creating virtual controls for mobile
Quick Start create ( ) {
this .cursors = this .input .keyboard !.createCursorKeys ();
this .wasd = this .input .keyboard !.addKeys ('W,A,S,D' );
this .input .on ('pointerdown' , (pointer ) => {
this .spawnBullet (pointer.x , pointer.y );
});
}
update ( ) {
if (this .cursors .left .isDown ) {
this .player .x -= 5 ;
}
}
Decision Framework Need Use Arrow keys createCursorKeys()WASD addKeys('W,A,S,D')Click/tap pointerdown eventDrag setInteractive({ draggable: true })Gamepad gamepadpad plugin
Progressive Guide
Level 1: Keyboard Input export class GameScene extends Phaser.Scene {
private cursors!: Phaser .Types .Input .Keyboard .CursorKeys ;
private wasd!: {
W : Phaser .Input .Keyboard .Key ;
A : Phaser .Input .Keyboard .Key ;
S : Phaser .Input .Keyboard .Key ;
D : Phaser .Input .Keyboard .Key ;
};
private jumpKey!: Phaser .Input .Keyboard .Key ;
private shootKey!: Phaser .Input .Keyboard .Key ;
create ( ) {
this .cursors = this .input .keyboard !.createCursorKeys ();
this .wasd = this .input .keyboard !.addKeys ("W,A,S,D" ) as any ;
this .jumpKey = this .input .keyboard !.addKey ("SPACE" );
this .shootKey = this .input .keyboard !.addKey (
Phaser .Input .Keyboard .KeyCodes .X ,
);
const shiftKey = this .input .keyboard !.addKey ("SHIFT" );
this .input .keyboard !.on ("keydown-SHIFT" , () => {
this .player .sprint = true ;
});
this .input .keyboard !.on ("keyup-SHIFT" , () => {
this .player .sprint = false ;
});
}
update ( ) {
if (this .cursors .left .isDown ) {
this .player .setVelocityX (-160 );
} else if (this .cursors .right .isDown ) {
this .player .setVelocityX (160 );
} else {
this .player .setVelocityX (0 );
}
if (this .cursors .up .isDown && this .player .body !.touching .down ) {
this .player .setVelocityY (-330 );
}
if (this .wasd .A .isDown ) {
this .player .setVelocityX (-160 );
}
if (Phaser .Input .Keyboard .JustDown (this .jumpKey )) {
this .player .jump ();
}
if (this .shootKey .isDown ) {
this .player .shoot ();
}
}
}
Level 2: Mouse and Touch Input create ( ) {
this .input .on ('pointermove' , (pointer : Phaser .Input .Pointer ) => {
this .player .rotation = Phaser .Math .Angle .Between (
this .player .x , this .player .y ,
pointer.x , pointer.y
);
});
this .input .on ('pointerdown' , (pointer : Phaser .Input .Pointer ) => {
this .shoot (pointer.x , pointer.y );
});
const box = this .add .image (400 , 300 , 'box' );
box.setInteractive ({ draggable : true });
this .input .setDraggable ([box]);
this .input .on ('drag' , (pointer : any , gameObject : Phaser .GameObjects .Image , dragX : number , dragY : number ) => {
gameObject.x = dragX;
gameObject.y = dragY;
});
this .input .on ('gameobjectdown' , (pointer : any , gameObject : any ) => {
gameObject.setTint (0xff0000 );
});
const zone = this .add .zone (400 , 300 , 200 , 200 );
zone.setInteractive ({ useHandCursor : true });
zone.on ('pointerover' , () => {
zone.setFillStyle (0x444444 );
});
zone.on ('pointerout' , () => {
zone.setFillStyle (0x000000 );
});
}
Level 3: Virtual Joystick for Mobile export class GameScene extends Phaser.Scene {
private joystick!: {
base : Phaser .GameObjects .Image ;
knob : Phaser .GameObjects .Image ;
};
private joystickActive = false ;
private joystickPointerId : number | null = null ;
private joystickVector = { x : 0 , y : 0 };
create ( ) {
if (this .sys .game .device .os .android || this .sys .game .device .os .iOS ) {
this .createVirtualJoystick ();
}
}
createVirtualJoystick ( ) {
const joyX = 100 ;
const joyY = this .scale .height - 100 ;
const radius = 50 ;
const base = this .add .circle (joyX, joyY, radius, 0x444444 , 0.5 );
base.setScrollFactor (0 );
base.setDepth (1000 );
const knob = this .add .circle (joyX, joyY, 20 , 0x888888 , 0.8 );
knob.setScrollFactor (0 );
knob.setDepth (1001 );
this .joystick = { base, knob };
this .input .on ("pointerdown" , (pointer : Phaser .Input .Pointer ) => {
const dist = Phaser .Math .Distance .Between (
pointer.x ,
pointer.y ,
joyX,
joyY,
);
if (dist < radius && this .joystickPointerId === null ) {
this .joystickActive = true ;
this .joystickPointerId = pointer.id ;
}
});
this .input .on ("pointermove" , (pointer : Phaser .Input .Pointer ) => {
if (this .joystickActive && pointer.id === this .joystickPointerId ) {
const angle = Phaser .Math .Angle .Between (
joyX,
joyY,
pointer.x ,
pointer.y ,
);
const dist = Math .min (
Phaser .Math .Distance .Between (joyX, joyY, pointer.x , pointer.y ),
radius,
);
this .joystick .knob .x = joyX + Math .cos (angle) * dist;
this .joystick .knob .y = joyY + Math .sin (angle) * dist;
this .joystickVector .x = (this .joystick .knob .x - joyX) / radius;
this .joystickVector .y = (this .joystick .knob .y - joyY) / radius;
}
});
this .input .on ("pointerup" , (pointer : Phaser .Input .Pointer ) => {
if (pointer.id === this .joystickPointerId ) {
this .joystickActive = false ;
this .joystickPointerId = null ;
this .joystick .knob .x = joyX;
this .joystick .knob .y = joyY;
this .joystickVector = { x : 0 , y : 0 };
}
});
}
update ( ) {
if (this .joystickActive ) {
this .player .setVelocity (
this .joystickVector .x * this .player .speed ,
this .joystickVector .y * this .player .speed ,
);
}
}
}
Level 4: Gamepad Support create ( ) {
this .input .gamepad !.on ('connected' , (gamepad : Phaser .Input .Gamepad .Gamepad ) => {
console .log ('Gamepad connected:' , gamepad.id );
});
this .input .gamepad !.on ('disconnected' , (gamepad : Phaser .Input .Gamepad .Gamepad ) => {
console .log ('Gamepad disconnected:' , gamepad.id );
});
}
update ( ) {
const pads = this .input .gamepad !.gamepads ;
for (let i = 0 ; i < pads.length ; i++) {
const pad = pads[i];
if (!pad || !pad.connected ) continue ;
if (pad.axes .length >= 2 ) {
const axisX = pad.axes [0 ].getValue ();
const axisY = pad.axes [1 ].getValue ();
this .player .setVelocity (axisX * 200 , axisY * 200 );
}
if (pad.buttons [12 ]?.isDown ) this .player .setVelocityY (-160 );
if (pad.buttons [13 ]?.isDown ) this .player .setVelocityY (160 );
if (pad.buttons [14 ]?.isDown ) this .player .setVelocityX (-160 );
if (pad.buttons [15 ]?.isDown ) this .player .setVelocityX (160 );
if (pad.buttons [0 ]?.isDown ) this .player .jump ();
if (pad.buttons [1 ]?.isDown ) this .player .shoot ();
if (pad.buttons [4 ]?.isDown ) this .player .previousWeapon ();
if (pad.buttons [5 ]?.isDown ) this .player .nextWeapon ();
}
}
Level 5: Input Manager Class class InputManager {
private keyboard : { cursors : Phaser .Types .Input .Keyboard .CursorKeys ; wasd : any };
private gamepadIndex = 0 ;
private virtualJoystick = { active : false , x : 0 , y : 0 };
constructor (private scene : Phaser .Scene ) {
this .setupKeyboard ();
this .setupGamepad ();
this .setupVirtualControls ();
}
private setupKeyboard ( ) {
this .keyboard = {
cursors : this .scene .input .keyboard !.createCursorKeys (),
wasd : this .scene .input .keyboard !.addKeys ('W,A,S,D' )
};
}
private setupGamepad ( ) {
this .scene .input .gamepad !.on ('connected' , () => {
this .gamepadIndex = 0 ;
});
}
private setupVirtualControls ( ) {
if (this .scene .sys .game .device .os .android ||
this .scene .sys .game .device .os .iOS ) {
}
}
getMovement (): { x : number ; y : number } {
const pad = this .scene .input .gamepad !.getPad (this .gamepadIndex );
if (pad && pad.connected && pad.axes .length >= 2 ) {
return {
x : pad.axes [0 ].getValue (),
y : pad.axes [1 ].getValue ()
};
}
if (this .virtualJoystick .active ) {
return {
x : this .virtualJoystick .x ,
y : this .virtualJoystick .y
};
}
let x = 0 , y = 0 ;
if (this .keyboard .cursors .left .isDown || this .keyboard .wasd .A .isDown ) x = -1 ;
if (this .keyboard .cursors .right .isDown || this .keyboard .wasd .D .isDown ) x = 1 ;
if (this .keyboard .cursors .up .isDown || this .keyboard .wasd .W .isDown ) y = -1 ;
if (this .keyboard .cursors .down .isDown || this .keyboard .wasd .S .isDown ) y = 1 ;
return { x, y };
}
isJumpPressed (): boolean {
return this .keyboard .cursors .up .isDown ||
this .keyboard .wasd .W .isDown ||
(this .scene .input .gamepad !.getPad (this .gamepadIndex )?.buttons [0 ]?.isDown );
}
isAttackJustPressed (): boolean {
return Phaser .Input .Keyboard .JustDown (
this .scene .input .keyboard !.addKey ('SPACE' )
) || this .scene .input .gamepad !.getPad (this .gamepadIndex )?.buttons [1 ]?.justDown ;
}
}
create ( ) {
this .inputManager = new InputManager (this );
}
update ( ) {
const move = this .inputManager .getMovement ();
this .player .setVelocity (move.x * 200 , move.y * 200 );
if (this .inputManager .isJumpPressed ()) {
this .player .jump ();
}
}
Anti-Patterns
Create new Key objects every frame - reuse from create()
Ignore JustDown/JustUp for trigger actions
Mix coordinate systems for input
Forget pointer ID for multi-touch
Hardcode only keyboard input
Use polling for all input (use events when appropriate)
Create keys once in create()
Use JustDown/JustUp for one-shot actions
Normalize input vectors
Track pointer IDs for multi-touch
Support multiple input methods
Combine event listeners with polling
Code Patterns
Key Combination Detection
this .input .keyboard !.on ("keydown-Z" , () => {
const ctrlKey = this .input .keyboard !.checkDown (
this .input .keyboard !.addKey ("CTRL" ),
100 ,
);
if (ctrlKey) {
this .undo ();
}
});
let lastTapTime = 0 ;
this .input .on ("pointerdown" , () => {
const now = this .time .now ;
if (now - lastTapTime < 300 ) {
this .doubleTap ();
}
lastTapTime = now;
});
Smooth Input Damping update ( ) {
const targetX = this .input .activePointer .x ;
const targetY = this .input .activePointer .y ;
this .player .x = Phaser .Math .Linear (this .player .x , targetX, 0.1 );
this .player .y = Phaser .Math .Linear (this .player .y , targetY, 0.1 );
}
Checklist
Reference