A fast, free and fun HTML5 Game Framework for Desktop and Mobile web browsers
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Phaser provides comprehensive input handling for keyboard, mouse, touch, and gamepad input across desktop and mobile platforms. The input system supports both polling and event-driven approaches for maximum flexibility.
Each scene has its own input manager accessible via this.input:
class InputScene extends Phaser.Scene {
create() {
const input = this.input;
// Input properties
console.log('Active pointer count:', input.activePointer);
console.log('Mouse enabled:', input.mouse.enabled);
console.log('Touch enabled:', input.touch.enabled);
console.log('Gamepad enabled:', input.gamepad.enabled);
// Global input events
input.on('pointerdown', this.handlePointerDown, this);
input.on('pointermove', this.handlePointerMove, this);
input.on('pointerup', this.handlePointerUp, this);
input.on('gameout', () => console.log('Pointer left game area'));
input.on('gameover', () => console.log('Pointer entered game area'));
}
handlePointerDown(pointer) {
console.log('Pointer down at:', pointer.x, pointer.y);
console.log('Left button:', pointer.leftButtonDown());
console.log('Right button:', pointer.rightButtonDown());
console.log('Middle button:', pointer.middleButtonDown());
}
}Pointers represent mouse cursors or touch points:
class PointerScene extends Phaser.Scene {
create() {
this.input.on('pointerdown', (pointer) => {
// Pointer properties
console.log('Position:', pointer.x, pointer.y);
console.log('World position:', pointer.worldX, pointer.worldY);
console.log('Previous position:', pointer.prevPosition.x, pointer.prevPosition.y);
console.log('Velocity:', pointer.velocity.x, pointer.velocity.y);
console.log('Distance:', pointer.distance);
console.log('Duration:', pointer.getDuration());
console.log('Angle:', pointer.angle);
// Pointer identification
console.log('Pointer ID:', pointer.id);
console.log('Is primary pointer:', pointer.primaryDown);
console.log('Active buttons:', pointer.buttons);
// Touch-specific properties
console.log('Touch ID:', pointer.identifier);
console.log('Touch pressure:', pointer.pressure);
console.log('Touch size:', pointer.touchSizeX, pointer.touchSizeY);
});
// Multi-touch support
this.input.on('pointerdown', (pointer) => {
if (pointer.id === 0) {
console.log('First finger down');
} else if (pointer.id === 1) {
console.log('Second finger down - pinch gesture possible');
}
});
}
}Quick access to arrow keys:
class KeyboardScene extends Phaser.Scene {
create() {
// Create cursor keys object
this.cursors = this.input.keyboard.createCursorKeys();
}
update() {
// Polling approach
if (this.cursors.left.isDown) {
this.player.x -= 200 * this.game.loop.delta / 1000;
}
if (this.cursors.right.isDown) {
this.player.x += 200 * this.game.loop.delta / 1000;
}
if (this.cursors.up.isDown) {
this.player.y -= 200 * this.game.loop.delta / 1000;
}
if (this.cursors.down.isDown) {
this.player.y += 200 * this.game.loop.delta / 1000;
}
// Check for just pressed/released
if (Phaser.Input.Keyboard.JustDown(this.cursors.space)) {
this.playerJump();
}
if (Phaser.Input.Keyboard.JustUp(this.cursors.space)) {
this.playerLand();
}
}
}Create and manage individual key objects:
class IndividualKeysScene extends Phaser.Scene {
create() {
// Create individual keys
this.wasdKeys = this.input.keyboard.addKeys('W,S,A,D');
this.spaceKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);
this.escKey = this.input.keyboard.addKey('ESC');
this.enterKey = this.input.keyboard.addKey(13); // Key code
// Key events
this.spaceKey.on('down', () => {
console.log('Space pressed');
});
this.spaceKey.on('up', () => {
console.log('Space released');
});
// Global keyboard events
this.input.keyboard.on('keydown', (event) => {
console.log('Key pressed:', event.code);
});
this.input.keyboard.on('keyup-ESC', () => {
this.scene.pause();
});
}
update() {
// WASD movement
const speed = 200;
const delta = this.game.loop.delta / 1000;
if (this.wasdKeys.A.isDown) {
this.player.x -= speed * delta;
}
if (this.wasdKeys.D.isDown) {
this.player.x += speed * delta;
}
if (this.wasdKeys.W.isDown) {
this.player.y -= speed * delta;
}
if (this.wasdKeys.S.isDown) {
this.player.y += speed * delta;
}
}
}Handle complex key combinations:
class KeyCombinationsScene extends Phaser.Scene {
create() {
// Create key combinations
this.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,
'shift': Phaser.Input.Keyboard.KeyCodes.SHIFT,
'ctrl': Phaser.Input.Keyboard.KeyCodes.CTRL,
'alt': Phaser.Input.Keyboard.KeyCodes.ALT
});
// Key combo events
this.input.keyboard.createCombo([
Phaser.Input.Keyboard.KeyCodes.CTRL,
Phaser.Input.Keyboard.KeyCodes.S
], {
resetOnMatch: true,
maxKeyDelay: 0,
resetOnWrongKey: true,
deleteOnMatch: false
});
this.input.keyboard.on('keycombomatch', (combo) => {
console.log('Key combo matched!');
this.saveGame();
});
}
update() {
const speed = this.keys.shift.isDown ? 400 : 200; // Run when shift held
const delta = this.game.loop.delta / 1000;
// Modifier key combinations
if (this.keys.ctrl.isDown && this.keys.up.isDown) {
this.player.jump();
} else if (this.keys.up.isDown) {
this.player.y -= speed * delta;
}
if (this.keys.alt.isDown && this.keys.left.isDown) {
this.player.strafe(-1);
} else if (this.keys.left.isDown) {
this.player.x -= speed * delta;
}
}
}Common key code constants:
const KeyCodes = Phaser.Input.Keyboard.KeyCodes;
// Letter keys
KeyCodes.A // 65
KeyCodes.B // 66
// ... through Z
// Number keys
KeyCodes.ZERO // 48
KeyCodes.ONE // 49
// ... through NINE (57)
// Function keys
KeyCodes.F1 // 112
KeyCodes.F2 // 113
// ... through F12 (123)
// Special keys
KeyCodes.SPACE // 32
KeyCodes.ENTER // 13
KeyCodes.ESC // 27
KeyCodes.TAB // 9
KeyCodes.SHIFT // 16
KeyCodes.CTRL // 17
KeyCodes.ALT // 18
// Arrow keys
KeyCodes.LEFT // 37
KeyCodes.UP // 38
KeyCodes.RIGHT // 39
KeyCodes.DOWN // 40Handle mouse and touch uniformly through pointers:
class MouseTouchScene extends Phaser.Scene {
create() {
// Basic pointer events
this.input.on('pointerdown', (pointer, gameObject) => {
console.log('Pointer down at:', pointer.x, pointer.y);
this.createClickEffect(pointer.x, pointer.y);
});
this.input.on('pointermove', (pointer) => {
if (pointer.isDown) {
console.log('Dragging at:', pointer.x, pointer.y);
this.drawTrail(pointer.x, pointer.y);
}
});
this.input.on('pointerup', (pointer) => {
console.log('Pointer released after:', pointer.getDuration(), 'ms');
});
// Mouse-specific events
this.input.on('wheel', (pointer, gameObjects, deltaX, deltaY, deltaZ) => {
console.log('Mouse wheel:', deltaY);
this.cameras.main.zoom += deltaY > 0 ? -0.1 : 0.1;
});
// Right-click context menu
this.input.on('pointerdown', (pointer) => {
if (pointer.rightButtonDown()) {
this.showContextMenu(pointer.x, pointer.y);
}
});
}
createClickEffect(x, y) {
const effect = this.add.circle(x, y, 20, 0xffffff, 0.5);
this.tweens.add({
targets: effect,
scaleX: 2,
scaleY: 2,
alpha: 0,
duration: 300,
onComplete: () => effect.destroy()
});
}
}Make game objects respond to input:
class InteractiveScene extends Phaser.Scene {
create() {
// Basic interactive object
const button = this.add.rectangle(400, 300, 200, 100, 0x00ff00);
button.setInteractive();
button.on('pointerdown', () => {
console.log('Button clicked!');
});
button.on('pointerover', () => {
button.setFillStyle(0x00aa00);
});
button.on('pointerout', () => {
button.setFillStyle(0x00ff00);
});
// Custom hit areas
const sprite = this.add.sprite(200, 200, 'player');
sprite.setInteractive(new Phaser.Geom.Circle(25, 25, 25), Phaser.Geom.Circle.Contains);
// Rectangle hit area
const image = this.add.image(600, 200, 'logo');
image.setInteractive(new Phaser.Geom.Rectangle(0, 0, 100, 50), Phaser.Geom.Rectangle.Contains);
// Pixel-perfect hit detection
const pixelSprite = this.add.sprite(400, 500, 'character');
pixelSprite.setInteractive({
pixelPerfect: true,
alphaTolerance: 1
});
}
}Implement drag and drop functionality:
class DragDropScene extends Phaser.Scene {
create() {
// Create draggable objects
const box1 = this.add.rectangle(200, 200, 100, 100, 0xff0000);
const box2 = this.add.rectangle(400, 200, 100, 100, 0x00ff00);
const box3 = this.add.rectangle(600, 200, 100, 100, 0x0000ff);
// Make objects draggable
box1.setInteractive({ draggable: true });
box2.setInteractive({ draggable: true });
box3.setInteractive({ draggable: true });
// Drag events
this.input.on('dragstart', (pointer, gameObject) => {
console.log('Drag started:', gameObject);
gameObject.setTint(0x888888);
});
this.input.on('drag', (pointer, gameObject, dragX, dragY) => {
gameObject.x = dragX;
gameObject.y = dragY;
});
this.input.on('dragend', (pointer, gameObject) => {
console.log('Drag ended:', gameObject);
gameObject.clearTint();
});
// Drop zones
const dropZone = this.add.zone(400, 500, 200, 100);
dropZone.setRectangleDropZone(200, 100);
// Visual feedback for drop zone
const dropZoneGraphic = this.add.graphics();
dropZoneGraphic.lineStyle(2, 0xffffff);
dropZoneGraphic.strokeRect(300, 450, 200, 100);
// Drop events
this.input.on('drop', (pointer, gameObject, dropZone) => {
console.log('Object dropped in zone!');
gameObject.x = dropZone.x;
gameObject.y = dropZone.y;
});
this.input.on('dragenter', (pointer, gameObject, dropZone) => {
dropZoneGraphic.clear();
dropZoneGraphic.lineStyle(2, 0x00ff00);
dropZoneGraphic.strokeRect(300, 450, 200, 100);
});
this.input.on('dragleave', (pointer, gameObject, dropZone) => {
dropZoneGraphic.clear();
dropZoneGraphic.lineStyle(2, 0xffffff);
dropZoneGraphic.strokeRect(300, 450, 200, 100);
});
}
}Support for multiple gamepads:
class GamepadScene extends Phaser.Scene {
create() {
// Enable gamepad input
this.input.gamepad.start();
// Listen for gamepad connection
this.input.gamepad.on('connected', (pad) => {
console.log('Gamepad connected:', pad.index, pad.id);
this.setupGamepad(pad);
});
this.input.gamepad.on('disconnected', (pad) => {
console.log('Gamepad disconnected:', pad.index);
});
// Get existing gamepads
if (this.input.gamepad.total > 0) {
this.gamepad = this.input.gamepad.pad1; // First gamepad
this.setupGamepad(this.gamepad);
}
}
setupGamepad(pad) {
// Button events
pad.on('down', (index, value, button) => {
console.log('Button pressed:', index, button.id);
});
pad.on('up', (index, value, button) => {
console.log('Button released:', index);
});
// Specific button events
if (pad.A) {
pad.A.on('down', () => {
this.playerJump();
});
}
if (pad.B) {
pad.B.on('down', () => {
this.playerAttack();
});
}
}
update() {
if (this.gamepad) {
// Left stick movement
const leftStick = this.gamepad.leftStick;
if (leftStick.length > 0.1) { // Deadzone
this.player.x += leftStick.x * 200 * (this.game.loop.delta / 1000);
this.player.y += leftStick.y * 200 * (this.game.loop.delta / 1000);
}
// Right stick camera
const rightStick = this.gamepad.rightStick;
if (rightStick.length > 0.1) {
this.cameras.main.scrollX += rightStick.x * 100 * (this.game.loop.delta / 1000);
this.cameras.main.scrollY += rightStick.y * 100 * (this.game.loop.delta / 1000);
}
// D-pad
if (this.gamepad.left) {
this.selectMenuItem(-1);
}
if (this.gamepad.right) {
this.selectMenuItem(1);
}
// Triggers
if (this.gamepad.L2 > 0.5) {
this.aimWeapon();
}
if (this.gamepad.R2 > 0.5) {
this.fireWeapon(this.gamepad.R2); // Pressure sensitive
}
}
}
}Access gamepad buttons by name or index:
class GamepadMappingScene extends Phaser.Scene {
update() {
const pad = this.input.gamepad.pad1;
if (pad) {
// Face buttons (Xbox layout)
if (pad.A && pad.A.pressed) { console.log('A pressed'); }
if (pad.B && pad.B.pressed) { console.log('B pressed'); }
if (pad.X && pad.X.pressed) { console.log('X pressed'); }
if (pad.Y && pad.Y.pressed) { console.log('Y pressed'); }
// Shoulder buttons
if (pad.L1 && pad.L1.pressed) { console.log('Left bumper'); }
if (pad.R1 && pad.R1.pressed) { console.log('Right bumper'); }
// Triggers (analog values)
if (pad.L2 > 0) { console.log('Left trigger:', pad.L2); }
if (pad.R2 > 0) { console.log('Right trigger:', pad.R2); }
// D-pad
if (pad.up) { console.log('D-pad up'); }
if (pad.down) { console.log('D-pad down'); }
if (pad.left) { console.log('D-pad left'); }
if (pad.right) { console.log('D-pad right'); }
// Stick buttons
if (pad.L3 && pad.L3.pressed) { console.log('Left stick pressed'); }
if (pad.R3 && pad.R3.pressed) { console.log('Right stick pressed'); }
// System buttons
if (pad.select && pad.select.pressed) { console.log('Select/Back'); }
if (pad.start && pad.start.pressed) { console.log('Start/Menu'); }
}
}
}Detect complex input patterns:
class InputSequenceScene extends Phaser.Scene {
create() {
// Fighting game combo system
this.comboSequence = [];
this.comboTimer = 0;
this.maxComboDelay = 1000; // 1 second between inputs
// Define combos
this.combos = {
'hadoken': ['down', 'down-forward', 'forward', 'punch'],
'shoryuken': ['forward', 'down', 'down-forward', 'punch'],
'spin-kick': ['back', 'back', 'forward', 'kick']
};
// Input detection
this.input.keyboard.on('keydown-S', () => this.addToCombo('down'));
this.input.keyboard.on('keydown-W', () => this.addToCombo('up'));
this.input.keyboard.on('keydown-A', () => this.addToCombo('back'));
this.input.keyboard.on('keydown-D', () => this.addToCombo('forward'));
this.input.keyboard.on('keydown-J', () => this.addToCombo('punch'));
this.input.keyboard.on('keydown-K', () => this.addToCombo('kick'));
}
addToCombo(input) {
this.comboSequence.push(input);
this.comboTimer = this.time.now;
// Check for combo matches
this.checkCombos();
// Clear old inputs
if (this.comboSequence.length > 10) {
this.comboSequence.shift();
}
}
checkCombos() {
const sequence = this.comboSequence.join('-');
for (let [comboName, comboInputs] of Object.entries(this.combos)) {
const comboString = comboInputs.join('-');
if (sequence.includes(comboString)) {
this.executeCombo(comboName);
this.comboSequence = [];
break;
}
}
}
update(time) {
// Clear combo if too much time has passed
if (time - this.comboTimer > this.maxComboDelay) {
this.comboSequence = [];
}
}
}Basic gesture detection for touch input:
class GestureScene extends Phaser.Scene {
create() {
this.gesturePoints = [];
this.isRecording = false;
this.input.on('pointerdown', (pointer) => {
this.isRecording = true;
this.gesturePoints = [{ x: pointer.x, y: pointer.y, time: this.time.now }];
});
this.input.on('pointermove', (pointer) => {
if (this.isRecording) {
this.gesturePoints.push({
x: pointer.x,
y: pointer.y,
time: this.time.now
});
}
});
this.input.on('pointerup', () => {
this.isRecording = false;
this.recognizeGesture();
});
}
recognizeGesture() {
if (this.gesturePoints.length < 3) return;
const startPoint = this.gesturePoints[0];
const endPoint = this.gesturePoints[this.gesturePoints.length - 1];
const deltaX = endPoint.x - startPoint.x;
const deltaY = endPoint.y - startPoint.y;
const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if (distance > 100) { // Minimum swipe distance
const angle = Math.atan2(deltaY, deltaX);
const degrees = Phaser.Math.RadToDeg(angle);
if (degrees > -45 && degrees <= 45) {
this.onSwipeRight();
} else if (degrees > 45 && degrees <= 135) {
this.onSwipeDown();
} else if (degrees > 135 || degrees <= -135) {
this.onSwipeLeft();
} else {
this.onSwipeUp();
}
} else if (this.gesturePoints.length < 10) {
this.onTap();
}
}
onSwipeLeft() { console.log('Swiped left'); }
onSwipeRight() { console.log('Swiped right'); }
onSwipeUp() { console.log('Swiped up'); }
onSwipeDown() { console.log('Swiped down'); }
onTap() { console.log('Tapped'); }
}Handle complex multi-touch interactions:
class MultiTouchScene extends Phaser.Scene {
create() {
this.pinchStart = null;
this.initialDistance = 0;
this.input.on('pointerdown', (pointer) => {
if (this.input.pointer1.isDown && this.input.pointer2.isDown) {
// Two fingers down - start pinch gesture
this.startPinch();
}
});
this.input.on('pointermove', () => {
if (this.input.pointer1.isDown && this.input.pointer2.isDown) {
this.updatePinch();
}
});
this.input.on('pointerup', () => {
if (!this.input.pointer1.isDown || !this.input.pointer2.isDown) {
this.endPinch();
}
});
}
startPinch() {
const pointer1 = this.input.pointer1;
const pointer2 = this.input.pointer2;
this.initialDistance = Phaser.Math.Distance.Between(
pointer1.x, pointer1.y,
pointer2.x, pointer2.y
);
this.pinchStart = {
zoom: this.cameras.main.zoom,
centerX: (pointer1.x + pointer2.x) / 2,
centerY: (pointer1.y + pointer2.y) / 2
};
}
updatePinch() {
if (!this.pinchStart) return;
const pointer1 = this.input.pointer1;
const pointer2 = this.input.pointer2;
const currentDistance = Phaser.Math.Distance.Between(
pointer1.x, pointer1.y,
pointer2.x, pointer2.y
);
const scale = currentDistance / this.initialDistance;
const newZoom = this.pinchStart.zoom * scale;
this.cameras.main.setZoom(Phaser.Math.Clamp(newZoom, 0.5, 3));
}
endPinch() {
this.pinchStart = null;
}
}This comprehensive input system provides all the tools needed to create responsive, multi-platform games that work seamlessly across desktop and mobile devices.
Install with Tessl CLI
npx tessl i tessl/npm-phaser