phaser-input
This skill should be used when the user asks to "handle input", "keyboard controls", "mouse click", "touch controls", "gamepad support", "drag and drop", "virtual joystick", "WASD movement", "detect click", "pointer events", "keyboard shortcut", or "input manager".
How do I install this agent skill?
npx skills add https://github.com/yakoub-ai/phaser4-gamedev --skill phaser-inputIs this agent skill safe to install?
- Gen Agent Trust Hubpass
The phaser-input skill provides educational resources, code snippets, and design patterns for handling input devices (keyboard, mouse, touch, gamepad) and a virtual joystick within the Phaser 4 game framework. No security issues or malicious practices were identified.
- Socketpass
No alerts
- Snykpass
Risk: LOW · No issues
What does this agent skill do?
Phaser 4 Input
Phaser 4's Input system supports keyboard, mouse/pointer, touch, and gamepad. All input is accessed through this.input inside a Scene.
Keyboard Input
Cursor Keys
Create a cursor key set to track arrow keys, space, and shift in one call:
private cursors!: Phaser.Types.Input.Keyboard.CursorKeys;
create(): void {
this.cursors = this.input.keyboard!.createCursorKeys();
}
update(): void {
if (this.cursors.left.isDown) { /* move left */ }
if (this.cursors.right.isDown) { /* move right */ }
if (this.cursors.up.isDown) { /* move up / jump */ }
if (this.cursors.down.isDown) { /* move down / crouch */ }
if (this.cursors.space.isDown) { /* fire / action */ }
if (this.cursors.shift.isDown) { /* sprint / modifier */ }
}
WASD Keys
Use addKeys to bind arbitrary keys by name:
private wasd!: { up: Phaser.Input.Keyboard.Key; left: Phaser.Input.Keyboard.Key; down: Phaser.Input.Keyboard.Key; right: Phaser.Input.Keyboard.Key };
create(): void {
this.wasd = this.input.keyboard!.addKeys({ up: 'W', left: 'A', down: 'S', right: 'D' }) as typeof this.wasd;
}
update(): void {
if (this.wasd.left.isDown) { /* strafe left */ }
if (this.wasd.right.isDown) { /* strafe right */ }
}
Single Key
Bind a single key using KeyCodes:
private spaceKey!: Phaser.Input.Keyboard.Key;
private shiftKey!: Phaser.Input.Keyboard.Key;
create(): void {
this.spaceKey = this.input.keyboard!.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);
this.shiftKey = this.input.keyboard!.addKey(Phaser.Input.Keyboard.KeyCodes.SHIFT);
}
isDown vs JustDown vs JustUp
| Method | Behavior |
|---|---|
key.isDown | true every frame the key is held |
Phaser.Input.Keyboard.JustDown(key) | true on the single frame the key is first pressed |
Phaser.Input.Keyboard.JustUp(key) | true on the single frame the key is released |
update(): void {
// Continuous — fires every frame while held
if (this.cursors.left.isDown) {
this.player.setVelocityX(-200);
}
// One-shot — fires once per key press
if (Phaser.Input.Keyboard.JustDown(this.spaceKey)) {
this.fireBullet();
}
// Release — fires once per key release
if (Phaser.Input.Keyboard.JustUp(this.shiftKey)) {
this.stopSprint();
}
}
Key Combos (Konami Code, Cheat Codes)
create(): void {
// Konami: Up Up Down Down Left Right Left Right A B
const combo = this.input.keyboard!.createCombo(
[38, 38, 40, 40, 37, 39, 37, 39, 65, 66],
{ resetOnMatch: true }
);
this.input.keyboard!.on('keycombomatch', (combo: Phaser.Input.Keyboard.KeyCombo) => {
console.log('Konami code entered!');
this.activateCheatMode();
});
}
Disabling Browser Default Keys
Prevent the browser from intercepting arrow keys (page scroll) and other keys:
create(): void {
// Disable global capture — let browser handle keys normally
this.input.keyboard!.disableGlobalCapture();
// Enable global capture — Phaser intercepts keys before the browser
this.input.keyboard!.enableGlobalCapture();
// Capture only specific keys (recommended approach)
this.input.keyboard!.addCapture([
Phaser.Input.Keyboard.KeyCodes.UP,
Phaser.Input.Keyboard.KeyCodes.DOWN,
Phaser.Input.Keyboard.KeyCodes.LEFT,
Phaser.Input.Keyboard.KeyCodes.RIGHT,
Phaser.Input.Keyboard.KeyCodes.SPACE,
]);
}
Pointer / Mouse Input
Global Pointer Events
Listen on this.input for events anywhere in the game canvas:
create(): void {
this.input.on('pointerdown', (ptr: Phaser.Input.Pointer) => {
console.log(`Clicked at canvas: ${ptr.x}, ${ptr.y}`);
console.log(`World position: ${ptr.worldX}, ${ptr.worldY}`);
console.log(`Left button: ${ptr.leftButtonDown()}`);
console.log(`Right button: ${ptr.rightButtonDown()}`);
});
this.input.on('pointermove', (ptr: Phaser.Input.Pointer) => {
// ptr.velocity.x / ptr.velocity.y for movement delta
});
this.input.on('pointerup', (ptr: Phaser.Input.Pointer) => {
// handle release
});
}
Use ptr.worldX / ptr.worldY when the camera has moved — these give the world-space position rather than screen-space.
Making Sprites Interactive
Call setInteractive() before listening to pointer events on a specific object:
create(): void {
const button = this.add.image(400, 300, 'button');
button.setInteractive({ useHandCursor: true }); // shows pointer cursor on hover
button.on('pointerdown', () => {
this.scene.start('GameScene');
});
button.on('pointerover', () => {
button.setTint(0xcccccc); // lighten on hover
});
button.on('pointerout', () => {
button.clearTint();
});
}
Drag and Drop
Enable dragging on a game object:
create(): void {
const card = this.add.image(200, 200, 'card');
card.setInteractive();
this.input.setDraggable(card);
this.input.on('dragstart', (ptr: Phaser.Input.Pointer, obj: Phaser.GameObjects.Image) => {
obj.setTint(0x88ff88);
});
this.input.on('drag', (ptr: Phaser.Input.Pointer, obj: Phaser.GameObjects.Image, dragX: number, dragY: number) => {
obj.setPosition(dragX, dragY);
});
this.input.on('dragend', (ptr: Phaser.Input.Pointer, obj: Phaser.GameObjects.Image) => {
obj.clearTint();
});
}
Input Zones
Use a Zone as an invisible interactive area (useful for UI hit areas):
create(): void {
const zone = this.add.zone(400, 300, 200, 100).setInteractive();
zone.on('pointerdown', () => {
console.log('Zone was clicked');
});
}
Right-Click (Context Menu)
Prevent the browser context menu from appearing on right-click:
create(): void {
this.input.mouse!.disableContextMenu();
this.input.on('pointerdown', (ptr: Phaser.Input.Pointer) => {
if (ptr.rightButtonDown()) {
console.log('Right click at', ptr.worldX, ptr.worldY);
}
});
}
Stop Event Propagation
Prevent a click on a game object from also firing the global pointerdown handler:
button.on('pointerdown', (
ptr: Phaser.Input.Pointer,
localX: number,
localY: number,
event: Phaser.Types.Input.EventData
) => {
event.stopPropagation();
this.handleButtonClick();
});
Touch Input
Touch events share the same pointer API — pointerdown, pointermove, pointerup fire on touch devices automatically.
Multi-Touch
Add extra pointer slots (default is 2 total; expand for more fingers):
create(): void {
// Support up to 4 simultaneous touches
this.input.addPointer(2); // adds 2 more (total becomes 4)
// Access individual touch pointers
// this.input.pointer1 — first touch
// this.input.pointer2 — second touch
// this.input.pointer3 — third touch
}
Swipe Detection
Phaser has no built-in swipe API. Track the start position manually:
private swipeStart = { x: 0, y: 0 };
private readonly SWIPE_THRESHOLD = 50; // pixels
create(): void {
this.input.on('pointerdown', (ptr: Phaser.Input.Pointer) => {
this.swipeStart = { x: ptr.x, y: ptr.y };
});
this.input.on('pointerup', (ptr: Phaser.Input.Pointer) => {
const dx = ptr.x - this.swipeStart.x;
const dy = ptr.y - this.swipeStart.y;
if (Math.abs(dx) > this.SWIPE_THRESHOLD || Math.abs(dy) > this.SWIPE_THRESHOLD) {
if (Math.abs(dx) > Math.abs(dy)) {
console.log(dx > 0 ? 'Swipe Right' : 'Swipe Left');
} else {
console.log(dy > 0 ? 'Swipe Down' : 'Swipe Up');
}
}
});
}
Preventing Browser Gestures
create(): void {
// Prevent pinch-zoom, browser swipe navigation, pull-to-refresh
this.input.mouse!.disableContextMenu();
// In your HTML, also add to the canvas element:
// style="touch-action: none;"
// Or in Phaser config: input: { activePointers: 3 }
}
Gamepad Input
The Gamepad plugin is included by default in Phaser 4. Access it via this.input.gamepad.
Connection
create(): void {
this.input.gamepad!.on('connected', (pad: Phaser.Input.Gamepad.Gamepad) => {
console.log(`Gamepad connected: ${pad.id}`);
});
this.input.gamepad!.on('disconnected', (pad: Phaser.Input.Gamepad.Gamepad) => {
console.log(`Gamepad disconnected`);
});
}
Reading Buttons
update(): void {
const pad = this.input.gamepad!.pad1;
if (!pad) return;
// Face buttons
if (pad.A) { this.jump(); }
if (pad.B) { this.dodge(); }
if (pad.X) { this.attack(); }
if (pad.Y) { this.interact(); }
// Shoulder / trigger buttons
if (pad.L1) { /* left bumper */ }
if (pad.R1) { /* right bumper */ }
if (pad.L2) { /* left trigger (analog, 0–1) */ }
if (pad.R2) { /* right trigger (analog, 0–1) */ }
// D-pad
if (pad.up) { /* d-pad up */ }
if (pad.down) { /* d-pad down */ }
if (pad.left) { /* d-pad left */ }
if (pad.right) { /* d-pad right */ }
}
Analog Sticks
Values range from -1 to 1 on each axis. Apply a dead zone to prevent drift:
private readonly DEAD_ZONE = 0.1;
update(): void {
const pad = this.input.gamepad!.pad1;
if (!pad) return;
let lx = pad.leftStick.x;
let ly = pad.leftStick.y;
let rx = pad.rightStick.x;
let ry = pad.rightStick.y;
// Apply dead zone
if (Math.abs(lx) < this.DEAD_ZONE) lx = 0;
if (Math.abs(ly) < this.DEAD_ZONE) ly = 0;
if (Math.abs(rx) < this.DEAD_ZONE) rx = 0;
if (Math.abs(ry) < this.DEAD_ZONE) ry = 0;
this.player.setVelocity(lx * 300, ly * 300);
// Aim with right stick
if (rx !== 0 || ry !== 0) {
const angle = Math.atan2(ry, rx) * (180 / Math.PI);
this.player.setAngle(angle);
}
}
Virtual Joystick (Mobile)
Phaser 4 has no built-in virtual joystick. For mobile games, implement a pointer-based joystick or use the reference implementation.
See references/virtual-joystick.md for a complete, production-ready TypeScript class that renders a base circle and thumb and exposes normalized direction.x / direction.y values.
Two common bugs are documented in that reference file: (1) First-Contact Lock — the base must stay locked during a drag; if the base "jumps" as the user drags, someone is updating baseX/baseY in onPointerMove. (2) Cross-scene listener loss — if the joystick works in one scene but not another, instantiate it ONCE in a persistent InputScene launched via scene.launch(), not per-gameplay-scene.
Usage pattern:
private joystick!: VirtualJoystick;
create(): void {
this.joystick = new VirtualJoystick(this, 120, this.scale.height - 120);
}
update(): void {
if (this.joystick.isActive) {
this.player.setVelocity(
this.joystick.direction.x * 200,
this.joystick.direction.y * 200
);
} else {
this.player.setVelocity(0, 0);
}
}
Additional Resources
Reference Files
references/input-api.md— Complete API reference for InputPlugin, KeyboardPlugin, Key, Pointer, Gamepad, and all input eventsreferences/virtual-joystick.md— Full TypeScript virtual joystick implementation for mobile games
How can the creator link this skill?
Add the canonical catalog link to the repository README so users can inspect current installs and available audits. The publishing guide covers the complete discovery path.
<a href="https://skillzs.dev/skills/yakoub-ai/phaser4-gamedev/phaser-input">View phaser-input on skillZs</a>