Skip to content

Controls

BiosSystem edited this page May 22, 2026 · 1 revision

Controls

Universal Retro Arcade supports three input methods: keyboard, hardware gamepad (Xbox / PlayStation), and touch (Android / tablet). All input handling is performed in the browser layer via Phaser 4 and the HTML5 Gamepad API.


Keyboard Controls

Lobby Navigation

Action Key
Move selection up Arrow Up
Move selection down Arrow Down
Confirm / Select game Space or Enter
Back / Close modal Esc
Toggle CRT Shader Ctrl + Shift + C
B-I-O-S Easter Egg Type B, I, O, S in sequence

In-Game Controls (Universal)

Action Key
Return to Lobby Esc
Restart (on Game Over) Space (most games)

Per-Game Keyboard Reference

Game Move Action / Fire Rotate Hard Drop
Snake Evolution ↑ ↓ ← →
Neon Pong W (up) / S (down)
Astro Drift W (thrust) / A D (rotate) Space (fire)
Brick Breaker ← → or Mouse Space (launch)
Froggie Crosser ↑ ↓ ← →
Space Defenders ← → Space (fire)
Tetris Pulse ← → (move) / (soft drop) Space (hard drop) (rotate) Space
Minesweeper Mouse only Left Click (reveal) / Right Click (flag)
Pixel Runner Space (jump / double jump)
Brave Bird Space (flap)
Cyber Chasm ↑ ↓ ← →

Gamepad Controls

Connection & Detection

The HTML5 Gamepad API (navigator.getGamepads()) is polled every frame inside Phaser 4's update() loop. Connect your controller before launching the app (or while on the lobby screen) for reliable detection. A 🎮 GAMEPAD CONNECTED banner will briefly appear in the lobby header upon connection.

Only one controller is supported at a time (index 0).

Button Mapping Reference

Xbox Controller

Physical Button Phaser Gamepad Property Function
Left Stick (Y-axis < -0.5) pad.leftStick.y < -0.5 Navigate Up / Move Up
Left Stick (Y-axis > 0.5) pad.leftStick.y > 0.5 Navigate Down / Move Down
D-Pad Up pad.up Navigate Up / Move Up
D-Pad Down pad.down Navigate Down / Move Down
D-Pad Left pad.left Move Left
D-Pad Right pad.right Move Right
A Button pad.A Confirm / Jump / Fire / Flap
B Button pad.B Back to Lobby
X Button pad.X Confirm (secondary)
Y Button pad.Y Confirm (secondary)
LB / L1 pad.L1 Back to Lobby (alternative)
Right Trigger pad.R2 Fire (Astro Drift)

PlayStation Controller

PlayStation controllers are automatically mapped to the same Phaser gamepad slots through the browser's standard gamepad mapping:

Physical Button Xbox Equivalent Function
Cross (✕) A Confirm / Jump / Flap
Circle (○) B Back to Lobby
Square (□) X Confirm (secondary)
Triangle (△) Y Confirm (secondary)
L1 L1 Back to Lobby
R2 Right Trigger Fire
Left Stick Left Stick Navigation / Movement
D-Pad D-Pad Navigation / Movement

Note: The standard gamepad mapping is used. Non-standard controllers (e.g., older generic USB gamepads) may require remapping via your OS or third-party software.

Gamepad Polling Code (Technical)

The lobby polls the gamepad each frame with edge detection to prevent held-button repeat:

const pad = this.input.gamepad.getPad(0);
const up     = pad.up || pad.leftStick.y < -0.5;
const down   = pad.down || pad.leftStick.y > 0.5;
const button = !!(pad.A || pad.B || pad.X || pad.Y);
const back   = !!(pad.B || pad.L1);

// Edge detection — only trigger on state change
if (up && !this.padLastState.up) this.handleUp();
if (down && !this.padLastState.down) this.handleDown();
if (button && !this.padLastState.button) this.handleSpace();
if (back && !this.padLastState.back) this.handleEsc();

this.padLastState = { up, down, button, back };

Touch Controls (Android)

On Android, the game uses on-screen virtual controls rendered as Phaser GameObjects overlaid on the game canvas. Phaser 4's Pointer API maps touch events to the corresponding game actions.

Touch Layout

┌─────────────────────────────────────────────┐
│                                             │
│           [ Game Canvas Area ]              │
│                                             │
│  ┌──────────────────┐   ┌─────────────────┐ │
│  │   D-PAD          │   │   ACTION BTNS   │ │
│  │      [▲]         │   │  [A]      [B]   │ │
│  │   [◄] [►]        │   │     [X]         │ │
│  │      [▼]         │   │                 │ │
│  └──────────────────┘   └─────────────────┘ │
└─────────────────────────────────────────────┘

Touch-Specific Behaviors

Game Touch Action
Minesweeper Single tap = reveal; long press (500ms) = flag
Brick Breaker Drag finger horizontally to move paddle
Brave Bird Tap anywhere on screen to flap
Pixel Runner Tap anywhere to jump; tap again mid-air to double jump

Pointer Events

Touch input is handled via Phaser 4's unified InputPlugin, which normalizes mouse clicks and touch events into Pointer objects:

this.input.on('pointerdown', (pointer: Phaser.Input.Pointer) => {
  // Works identically for mouse click and finger tap
  handleTap(pointer.x, pointer.y);
});

Input Priority

When multiple input methods are detected simultaneously:

  1. Keyboard — highest priority; all keyboard events are processed first.
  2. Gamepad — polled on every update() frame, edge-detected.
  3. Touch / Mouse — handled via event listeners, lowest priority but always active.

There is no conflict resolution needed since the input methods control the same game state — pressing D-Pad Up and at the same time will simply trigger the move twice in the same frame (harmless due to direction-lock in Snake, etc.).


Back to Home | See also: Games

Clone this wiki locally