Skip to main content

Overview

Minecraft Web Client includes first-class controller support with automatic gamepad detection, configurable button mappings, and a dedicated UI cursor system for menus. Connect any standard gamepad (Xbox, PlayStation, Switch Pro, etc.) and play seamlessly.

Supported Controllers

Xbox Controllers

Xbox One, Xbox Series X/S, Xbox 360 controllers fully supported

PlayStation

PS4 and PS5 DualShock/DualSense controllers

Generic Gamepads

Any controller compatible with the Gamepad API

Quick Start

1

Connect Your Controller

  • Wired: Plug in via USB
  • Wireless: Connect via Bluetooth
The game will automatically detect the controller when connected.
2

Test Detection

Move the analog sticks or press any button. If detected, you’ll see:
  • UI cursor appears on screen
  • Movement responds to left stick
3

Configure Bindings (Optional)

Open Settings → Controls → Keybindings to customize button mappings
4

Start Playing

Use the controller to navigate menus and play the game!

Default Button Mappings

From src/controls.ts:44-90:

Movement Controls

movement: {
  forward: ['KeyW'],
  back: ['KeyS'],
  left: ['KeyA'],
  right: ['KeyD'],
  jump: ['Space', 'A'],                    // A button (Xbox)
  sneak: ['ShiftLeft', 'Down'],            // D-pad Down
  toggleSneakOrDown: [null, 'Right Stick'], // R3 click
  sprint: ['ControlLeft', 'Left Stick'],    // L3 click
}

General Controls

general: {
  inventory: ['KeyE', 'X'],                // X button
  drop: ['KeyQ', 'B'],                     // B button
  nextHotbarSlot: [null, 'Right Bumper'],  // RB/R1
  prevHotbarSlot: [null, 'Left Bumper'],   // LB/L1
  attackDestroy: [null, 'Right Trigger'],  // RT/R2
  interactPlace: [null, 'Left Trigger'],   // LT/L2
  chat: [['KeyT', 'Enter'], 'Right'],      // D-pad Right
  playersList: ['Tab', 'Left'],            // D-pad Left
  togglePerspective: ['F5', 'Up'],         // D-pad Up
}

UI Navigation

ui: {
  back: [null, 'B'],             // B button
  leftClick: [null, 'A'],        // A button
  rightClick: [null, 'X'],       // X button
  speedupCursor: [null, 'Left Stick'], // L3 click (hold)
  pauseMenu: [null, 'Start']     // Start/Options button
}

Gamepad UI Cursor

When using a controller, a virtual cursor appears for navigating menus and UI elements.

Cursor Movement

From src/app/gamepadCursor.ts:7-18:
contro.on('movementUpdate', ({ vector, soleVector, gamepadIndex }) => {
  if (gamepadIndex !== undefined && gamepadUiCursorState.display) {
    const deadzone = 0.1
    if (Math.abs(soleVector.x) < deadzone && 
        Math.abs(soleVector.z) < deadzone) {
      return
    }
    moveGamepadCursorByPx(soleVector.x, true)
    moveGamepadCursorByPx(soleVector.z, false)
    emitMousemove()
    trackHoveredElement()
  }
})

Scrolling with Right Stick

From src/app/gamepadCursor.ts:66-85:
contro.on('stickMovement', ({ stick, vector }) => {
  if (stick !== 'right') return
  if (!gamepadUiCursorState.display) return

  let { x, z } = vector
  if (Math.abs(x) < 0.18) x = 0
  if (Math.abs(z) < 0.18) z = 0

  // Horizontal movement for inputs
  if (x !== 0) {
    emitGamepadInputChange(x, true)
  }

  // Vertical movement for scrolling
  if (z !== 0) {
    emulateGamepadScroll(z)
  }
})

Button Reference

Combat & Building

ActionXboxPlayStationFunction
Attack/BreakRTR2Break blocks, attack entities
Place/UseLTL2Place blocks, use items
Drop ItemBCircleDrop held item
Swap Hands(unmapped)(unmapped)Swap main/offhand

Movement & Camera

ActionXboxPlayStationFunction
MoveLeft StickLeft StickWalk/strafe
LookRight StickRight StickCamera rotation
JumpAXJump / Fly up
SneakD-pad DownD-pad DownSneak / Fly down
SprintL3 ClickL3 ClickToggle sprint
Toggle SneakR3 ClickR3 ClickToggle sneak

UI & Menus

ActionXboxPlayStationFunction
Select/ConfirmAXClick buttons, select items
Back/CancelBCircleClose menus, go back
Right ClickXSquareContext actions
Pause MenuStartOptionsOpen pause menu
Speed CursorL3 Click (hold)L3 Click (hold)Faster cursor movement

Hotbar & Inventory

ActionXboxPlayStationFunction
Next SlotRBR1Cycle hotbar right
Previous SlotLBL1Cycle hotbar left
InventoryXSquareOpen inventory

Advanced Features

Gamepad Polling

The client polls gamepad input at high frequency:
{
  gamepadPollingInterval: 10 // Poll every 10ms
}

Gamepad Detection

Automatic detection from movement updates:
contro.on('movementUpdate', ({ gamepadIndex }) => {
  miscUiState.usingGamepadInput = gamepadIndex !== undefined
})

UI Cursor State

Access cursor state programmatically:
// Check cursor display
console.log(gamepadUiCursorState.display)

// Get cursor position (0-100%)
console.log(gamepadUiCursorState.x, gamepadUiCursorState.y)

// Cursor speed multiplier
console.log(gamepadUiCursorState.multiply) // 1 or 2 (when speedup pressed)

Emulating Mouse Clicks

From src/app/gamepadCursor.ts:184-224:
export const emulateMouseClick = (isRightClick: boolean) => {
  const { x, y } = gamepadUiCursorState
  const xAbs = x / 100 * window.innerWidth
  const yAbs = y / 100 * window.innerHeight
  const el = document.elementFromPoint(xAbs, yAbs)
  
  if (el) {
    el.dispatchEvent(new MouseEvent('mousedown', {
      button: isRightClick ? 2 : 0,
      bubbles: true,
      clientX: xAbs,
      clientY: yAbs
    }))
    // ... additional events
  }
}

Customizing Controls

1

Open Keybindings

Navigate to Settings → Controls → Keybindings
2

Select Action

Click on any action you want to rebind
3

Press New Button

Press the gamepad button you want to assign
4

Save Changes

Changes are automatically saved to local storage

Troubleshooting

Controller Not Detected

  1. Check Connection
    • Ensure controller is properly connected (USB or Bluetooth)
    • Try pressing buttons to wake it up
    • Check browser console for Gamepad API errors
  2. Browser Compatibility
    • Use Chrome, Firefox, or Edge (best support)
    • Safari has limited gamepad support
    • Ensure browser permissions are granted
  3. Test Detection
    // In browser console
    navigator.getGamepads()
    

Buttons Not Working

  • Verify button mappings in Settings → Keybindings
  • Check that the controller is recognized by the Gamepad API
  • Try disconnecting and reconnecting
  • Test with different browser

Cursor Too Fast/Slow

  • Hold L3/Left Stick button to speed up cursor (2x multiplier)
  • Adjust sensitivity in game settings
  • Try different controllers with better analog precision

Touch Controls Override Gamepad

The client automatically hides touch controls when a gamepad is detected:
if (!usingTouch || usingGamepadInput || touchMovementType !== 'classic') {
  return null // Hide touch controls
}
If touch controls persist, check miscUiState.usingGamepadInput.

Platform Notes

Windows

  • Xbox controllers: Native support via XInput
  • PlayStation controllers: Use DS4Windows or Steam Input
  • Generic controllers: DirectInput support

macOS

  • PS4/PS5 controllers: Native Bluetooth support
  • Xbox controllers: Requires wireless adapter or wired connection
  • Switch Pro Controller: Works via Bluetooth

Linux

  • Most controllers work via evdev
  • May require xboxdrv for Xbox controllers
  • Steam Input provides universal support

Mobile

  • Android: Bluetooth controller support varies by device
  • iOS: MFi-certified controllers recommended
  • Some controllers require browser flags

Advanced Console Commands

Access gamepad internals via browser console:
// Access ControMax instance
window.controMax

// View pressed buttons
contro.pressedKeys

// Manually emit events
contro.emit('trigger', { command: 'movement.jump' })

// Test mouse click emulation
emulateMouseClick(false) // Left click
emulateMouseClick(true)  // Right click

// Check gamepad state
miscUiState.usingGamepadInput

Icon Support

The client includes PlayStation button icons:
  • playstation_triangle_console_controller_gamepad_icon.svg
  • playstation_square_console_controller_gamepad_icon.svg
  • cross_playstation_console_controller_gamepad_icon.svg
  • circle_playstation_console_controller_gamepad_icon.svg
And a generic gamepad icon from PixelartIcons.

See Also

Build docs developers (and LLMs) love