input-keyboard-mouse-touch
Original:🇺🇸 English
Translated
Use this skill when handling user input in Phaser 4. Covers keyboard keys, mouse clicks and movement, touch events, pointer handling, drag and drop, hit areas, interactive objects, and gamepad support. Triggers on: keyboard, mouse, touch, pointer, drag, drop, click, input, gamepad, cursor keys.
15installs
Sourcephaserjs/phaser
Added on
NPX Install
npx skill4agent add phaserjs/phaser input-keyboard-mouse-touchTags
Translated version includes tags in frontmatterSKILL.md Content
View Translation Comparison →Input: Keyboard, Mouse, Touch, and Gamepad
Phaser provides a unified input system accessed viain any Scene. It supports keyboard polling and events, mouse/pointer interaction with Game Objects (click, hover, drag), multi-touch, mouse wheel, and gamepad input. Input can be handled through event listeners or by polling state each frame.this.input
Key source paths: , , , , , , , , ,
Related skills: ../sprites-and-images/SKILL.md, ../events-system/SKILL.md, ../scenes/SKILL.md
src/input/InputPlugin.jssrc/input/Pointer.jssrc/input/keyboard/KeyboardPlugin.jssrc/input/keyboard/keys/Key.jssrc/input/keyboard/keys/KeyCodes.jssrc/input/keyboard/combo/KeyCombo.jssrc/input/gamepad/GamepadPlugin.jssrc/input/gamepad/Gamepad.jssrc/input/events/src/input/keyboard/events/Quick Start (basic keyboard + pointer input)
js
class MyScene extends Phaser.Scene {
create() {
// Keyboard: create cursor keys (up, down, left, right, space, shift)
this.cursors = this.input.keyboard.createCursorKeys();
// Keyboard: listen for a specific key event
this.input.keyboard.on('keydown-SPACE', (event) => {
console.log('Space pressed');
});
// Pointer: listen for click/tap anywhere on the game canvas
this.input.on('pointerdown', (pointer) => {
console.log('Clicked at', pointer.x, pointer.y);
});
// Pointer: make a Game Object interactive and clickable
const sprite = this.add.sprite(400, 300, 'player');
sprite.setInteractive();
sprite.on('pointerdown', (pointer, localX, localY, event) => {
console.log('Sprite clicked at local', localX, localY);
});
}
update() {
// Poll cursor keys each frame
if (this.cursors.left.isDown) {
// move left
}
if (this.cursors.right.isDown) {
// move right
}
if (Phaser.Input.Keyboard.JustDown(this.cursors.space)) {
// fire once per press
}
}
}Core Concepts
The Input Plugin (this.input)
Accessed via in any Scene. It is an that handles all input for that Scene.
this.inputEventEmitterKey properties:
- (boolean) - toggle input processing for the Scene
this.input.enabled - (boolean, default
this.input.topOnly) - only emit events from the top-most Game Object under the pointertrue - - the KeyboardPlugin instance
this.input.keyboard - - the GamepadPlugin instance
this.input.gamepad - - the MouseManager reference
this.input.mouse - - the most recently active Pointer
this.input.activePointer - - the mouse Pointer (pointers[0], distinct from pointer1 which is the first touch)
this.input.mousePointer - through
this.input.pointer1- individual pointer referencesthis.input.pointer10 - (number, default 0) - pixels a pointer must move before drag starts
this.input.dragDistanceThreshold - (number, default 0) - ms a pointer must be held before drag starts
this.input.dragTimeThreshold - (number, default -1) - how often pointers are polled; 0 = every frame, -1 = only on movement
this.input.pollRate
Key methods:
- - add extra pointers for multi-touch (default is 2; max 10)
addPointer(quantity) - - set custom hit area on Game Objects
setHitArea(gameObjects, hitArea, hitAreaCallback) setHitAreaCircle(gameObjects, x, y, radius, callback)setHitAreaEllipse(gameObjects, x, y, width, height, callback)setHitAreaRectangle(gameObjects, x, y, width, height, callback)setHitAreaTriangle(gameObjects, x1, y1, x2, y2, x3, y3, callback)- - use the texture frame dimensions
setHitAreaFromTexture(gameObjects, callback) - - enable or disable dragging
setDraggable(gameObjects, value) - - returns a callback for pixel-perfect hit testing
makePixelPerfect(alphaTolerance)
Pointers
A object encapsulates both mouse and touch input. By default Phaser creates 2 pointers. Use for more (up to 10 total).
Pointerthis.input.addPointer(quantity)Key properties:
- ,
x- position in screen space (read fromy,position.x)position.y - ,
worldX- position translated through the most recent CameraworldY - ,
downX- position when button was presseddownY - ,
upX- position when button was releasedupY - (boolean) - true if any button is held
isDown - (boolean) - true if primary button (left click / touch) is held
primaryDown - (number) - which button was pressed/released (0=left, 1=middle, 2=right)
button - (number) - bitmask of currently held buttons (1=left, 2=right, 4=middle, 8=back, 16=forward)
buttons - (boolean) - true if input came from touch
wasTouch - (Vector2) - smoothed velocity of pointer movement
velocity - (number) - angle of movement in radians
angle - (number) - smoothed distance moved per frame
distance - ,
movementX- relative movement when pointer is lockedmovementY - ,
deltaX,deltaY- mouse wheel scroll amountsdeltaZ - (boolean) - whether pointer lock is active
locked - - the Camera this Pointer last interacted with
camera
Key methods:
- ,
leftButtonDown(),rightButtonDown(),middleButtonDown(),backButtonDown()- check specific buttonsforwardButtonDown() - ,
leftButtonReleased(),rightButtonReleased()- check recent releasemiddleButtonReleased() - - distance between down position and current/up position
getDistance() - ,
getDistanceX()- horizontal/vertical distancegetDistanceY() - - ms between down and current time or up time
getDuration() - - angle between down and current/up position
getAngle() - - recalculate worldX/worldY for a given camera
updateWorldPoint(camera) - - translate pointer position through a camera
positionToCamera(camera, output)
Interactive Game Objects (setInteractive)
Call to enable input on a Game Object. This uses the texture frame as the hit area by default.
gameObject.setInteractive()js
// Default hit area from texture
sprite.setInteractive();
// Custom shape hit areas (Rectangle, Circle, Ellipse, Triangle, Polygon)
sprite.setInteractive(new Phaser.Geom.Circle(32, 32, 32), Phaser.Geom.Circle.Contains);
sprite.setInteractive(new Phaser.Geom.Ellipse(50, 50, 100, 60), Phaser.Geom.Ellipse.Contains);
sprite.setInteractive(new Phaser.Geom.Triangle(0,64,32,0,64,64), Phaser.Geom.Triangle.Contains);
sprite.setInteractive(new Phaser.Geom.Polygon(points), Phaser.Geom.Polygon.Contains);
// Pixel-perfect hit testing (expensive — use sparingly)
sprite.setInteractive({ pixelPerfect: true, alphaTolerance: 1 });
sprite.setInteractive(this.input.makePixelPerfect());
// With alpha tolerance (default 1):
sprite.setInteractive(this.input.makePixelPerfect(150));
// Config object with multiple options
sprite.setInteractive({
draggable: true,
dropZone: false,
useHandCursor: true,
cursor: 'pointer',
pixelPerfect: true,
alphaTolerance: 1
});
// Containers must specify a shape or call setSize first
container.setSize(200, 200);
container.setInteractive();Common Patterns
Keyboard Input (cursors, addKey, isDown, JustDown)
Cursor keys return an object with , , , , , Key objects:
updownleftrightspaceshiftjs
this.cursors = this.input.keyboard.createCursorKeys();
// In update():
if (this.cursors.up.isDown) { /* held */ }
if (this.cursors.space.isDown) { /* held */ }addKey creates a Key object for any key:
js
// By string name
const keyW = this.input.keyboard.addKey('W');
// By key code
const keySpace = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);
// With options: addKey(key, enableCapture, emitOnRepeat)
const keyA = this.input.keyboard.addKey('A', true, false);addKeys creates multiple keys at once:
js
// Comma-separated string returns { W, S, A, D } Key objects
const keys = this.input.keyboard.addKeys('W,S,A,D');
if (keys.W.isDown) { /* ... */ }
// Object form with custom names
const keys = this.input.keyboard.addKeys({
up: Phaser.Input.Keyboard.KeyCodes.W,
down: Phaser.Input.Keyboard.KeyCodes.S,
left: Phaser.Input.Keyboard.KeyCodes.A,
right: Phaser.Input.Keyboard.KeyCodes.D
});Polling vs events:
js
// Polling in update() - check every frame
if (keyW.isDown) { /* key is currently held */ }
if (keyW.isUp) { /* key is currently released */ }
// JustDown - returns true only once per press, resets after check
if (Phaser.Input.Keyboard.JustDown(keyW)) { /* fire once */ }
if (Phaser.Input.Keyboard.JustUp(keyW)) { /* released once */ }
// checkDown with duration - throttled polling
if (this.input.keyboard.checkDown(keySpace, 250)) {
// true at most once every 250ms while held
}
// Event-driven: listen for specific key
this.input.keyboard.on('keydown-SPACE', (event) => { /* ... */ });
this.input.keyboard.on('keyup-SPACE', (event) => { /* ... */ });
// Event-driven: listen for any key
this.input.keyboard.on('keydown', (event) => {
console.log(event.key); // native DOM KeyboardEvent
});
// Event on a Key object itself
const spaceBar = this.input.keyboard.addKey('SPACE');
spaceBar.on('down', (key, event) => { /* Key object + native event */ });
spaceBar.on('up', (key, event) => { /* ... */ });Key object properties:
- /
isDown(boolean)isUp - (number)
keyCode - ,
altKey,ctrlKey,shiftKey(boolean) - modifier state when pressedmetaKey - (number) - ms held in previous down-up cycle
duration - ,
timeDown(number) - timestampstimeUp - (number) - repeat count while held
repeats - (boolean) - if true, fires 'down' event on each repeat
emitOnRepeat - (boolean) - can this key be processed
enabled
Prevent browser default behavior:
js
// Capture specific keys to prevent browser scrolling etc.
this.input.keyboard.addCapture('SPACE,UP,DOWN,LEFT,RIGHT');
this.input.keyboard.addCapture([ 32, 37, 38, 39, 40 ]);
this.input.keyboard.removeCapture('SPACE');
// Note: captures are global across all ScenesCommon KeyCodes: , , , , , , , , , , , , , , . Access via etc.
BACKSPACE(8)TAB(9)ENTER(13)SHIFT(16)CTRL(17)ALT(18)ESC(27)SPACE(32)LEFT(37)UP(38)RIGHT(39)DOWN(40)A-Z(65-90)ZERO-NINE(48-57)F1-F12(112-123)Phaser.Input.Keyboard.KeyCodes.SPACEMouse/Pointer Click and Hover
Scene-level pointer events (fire anywhere on the canvas):
js
this.input.on('pointerdown', (pointer, currentlyOver) => {
// pointer: Pointer object, currentlyOver: array of interactive Game Objects under pointer
});
this.input.on('pointerup', (pointer, currentlyOver) => { /* ... */ });
this.input.on('pointermove', (pointer, currentlyOver) => { /* ... */ });
this.input.on('wheel', (pointer, currentlyOver, deltaX, deltaY, deltaZ) => { /* ... */ });Game Object pointer events (require setInteractive):
js
sprite.setInteractive();
// pointerdown on this specific object
sprite.on('pointerdown', (pointer, localX, localY, event) => {
// localX/localY are relative to the Game Object's top-left
// event.stopPropagation() prevents the event from going further
});
sprite.on('pointerup', (pointer, localX, localY, event) => { /* ... */ });
sprite.on('pointermove', (pointer, localX, localY, event) => { /* ... */ });
sprite.on('pointerover', (pointer, localX, localY, event) => { /* ... */ });
sprite.on('pointerout', (pointer, event) => { /* ... */ });
sprite.on('wheel', (pointer, deltaX, deltaY, deltaZ, event) => { /* ... */ });Right-click handling:
js
this.input.on('pointerdown', (pointer) => {
if (pointer.rightButtonDown()) {
// right-click
}
});
// Disable context menu
this.input.mouse.disableContextMenu();Pointer lock (FPS-style mouse capture):
js
// Request lock on click
this.input.on('pointerdown', () => {
this.input.mouse.requestPointerLock();
});
this.input.on('pointerlockchange', (event, locked) => {
// locked: boolean
});
// Read relative movement while locked
// pointer.movementX, pointer.movementYDrag and Drop
js
const sprite = this.add.sprite(400, 300, 'item');
sprite.setInteractive();
this.input.setDraggable(sprite);
// Or use config:
// sprite.setInteractive({ draggable: true });
// Drag events on the Scene input
this.input.on('dragstart', (pointer, gameObject) => {
gameObject.setTint(0xff0000);
});
this.input.on('drag', (pointer, gameObject, dragX, dragY) => {
gameObject.x = dragX;
gameObject.y = dragY;
});
this.input.on('dragend', (pointer, gameObject) => {
gameObject.clearTint();
});
// Drop zones
const zone = this.add.zone(600, 300, 200, 200).setRectangleDropZone(200, 200);
this.input.on('drop', (pointer, gameObject, dropZone) => {
gameObject.x = dropZone.x;
gameObject.y = dropZone.y;
});
this.input.on('dragenter', (pointer, gameObject, dropZone) => { /* ... */ });
this.input.on('dragleave', (pointer, gameObject, dropZone) => { /* ... */ });
this.input.on('dragover', (pointer, gameObject, dropZone) => { /* ... */ });Game Object-level drag events are also available:
js
sprite.on('drag', (pointer, dragX, dragY) => {
sprite.x = dragX;
sprite.y = dragY;
});
sprite.on('dragstart', (pointer, dragX, dragY) => { /* ... */ });
sprite.on('dragend', (pointer, dragX, dragY) => { /* ... */ });
sprite.on('drop', (pointer, dropZone) => { /* ... */ });Drag thresholds:
js
this.input.dragDistanceThreshold = 16; // must move 16px before drag starts
this.input.dragTimeThreshold = 200; // must hold 200ms before drag startsThree Levels of Pointer Events
Pointer events fire at three levels (see reference for details):
- Game Object:
gameObject.on('pointerdown', (pointer, localX, localY, event) => {}) - Scene per-object:
this.input.on('gameobjectdown', (pointer, gameObject, event) => {}) - Scene global:
this.input.on('pointerdown', (pointer, currentlyOver) => {})
Input Debug, topOnly, and Multi-touch Config
js
// Debug visualize hit areas
this.input.enableDebug(gameObject);
this.input.enableDebug(gameObject, 0xff00ff);
this.input.removeDebug(gameObject);
// Let all objects under pointer receive events (not just top-most)
this.input.topOnly = false;
this.input.setTopOnly(false);Multi-touch: set to reserve pointer slots at startup, or call at runtime. Access via through .
config.input.activePointersthis.input.addPointer(num)this.input.pointer1pointer10Gamepad Input
Enable gamepads in the game config:
js
const config = {
input: {
gamepad: true
}
};Access via . Gamepads are available as through :
this.input.gamepadpad1pad4js
// Wait for connection
this.input.gamepad.once('connected', (pad) => {
console.log('Gamepad connected:', pad.id);
});
// If already connected, check total
if (this.input.gamepad.total > 0) {
const pad = this.input.gamepad.pad1;
}Polling gamepad state in update():
js
update() {
const pad = this.input.gamepad.pad1;
if (!pad) return;
// D-pad (boolean properties)
if (pad.up) { /* d-pad up */ }
if (pad.down) { /* d-pad down */ }
if (pad.left) { /* d-pad left */ }
if (pad.right) { /* d-pad right */ }
// Face buttons (boolean) - Xbox naming convention
if (pad.A) { /* bottom button (Xbox A / PS X) */ }
if (pad.B) { /* right button (Xbox B / PS Circle) */ }
if (pad.X) { /* left button (Xbox X / PS Square) */ }
if (pad.Y) { /* top button (Xbox Y / PS Triangle) */ }
// Shoulder buttons (float 0-1)
if (pad.L1 > 0) { /* left shoulder top (LB) */ }
if (pad.L2 > 0) { /* left shoulder bottom / trigger (LT) */ }
if (pad.R1 > 0) { /* right shoulder top (RB) */ }
if (pad.R2 > 0) { /* right shoulder bottom / trigger (RT) */ }
// Analog sticks (Vector2, values -1 to 1)
const lx = pad.leftStick.x; // left stick horizontal
const ly = pad.leftStick.y; // left stick vertical
const rx = pad.rightStick.x;
const ry = pad.rightStick.y;
// Raw axis/button access
pad.getAxisValue(0); // float
pad.getButtonValue(0); // float 0-1
pad.isButtonDown(0); // boolean
pad.setAxisThreshold(0.1); // ignore values below threshold
}Gamepad events:
js
// Plugin-level events (any gamepad)
this.input.gamepad.on('connected', (pad, event) => { /* ... */ });
this.input.gamepad.on('disconnected', (pad, event) => { /* ... */ });
this.input.gamepad.on('down', (pad, button, value) => { /* any button on any pad */ });
this.input.gamepad.on('up', (pad, button, value) => { /* ... */ });
// Gamepad-instance events
pad.on('down', (index, value, button) => { /* button on this specific pad */ });
pad.on('up', (index, value, button) => { /* ... */ });Vibration (experimental, hardware/browser dependent):
js
if (pad.vibration) {
pad.vibration.playEffect('dual-rumble', {
duration: 200,
strongMagnitude: 1.0,
weakMagnitude: 0.5
});
}Key Combos
Listen for a sequence of keys:
js
// String-based combo
this.input.keyboard.createCombo('PHASER');
// Array of key codes (Konami code)
this.input.keyboard.createCombo(
[ 38, 38, 40, 40, 37, 39, 37, 39, 66, 65, 13 ],
{ resetOnMatch: true }
);
// Listen for match
this.input.keyboard.on('keycombomatch', (keyCombo, event) => {
console.log('Combo matched!');
});createCombo(keys, config):
- : a string (each character is a key) or an array of key codes / Key objects
keys - (boolean, default true) - reset progress if wrong key is pressed
config.resetOnWrongKey - (number, default 0) - max ms between key presses; 0 = no limit
config.maxKeyDelay - (boolean, default false) - reset combo after a successful match
config.resetOnMatch - (boolean, default false) - remove combo after first match
config.deleteOnMatch
For detailed configuration options, API reference tables, and source file maps, see the reference guide.