Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Gamepad Support Guide

Version: 3.0 Status: Stable Platforms: macOS, Linux, Windows

Overview

Conductor v3.0 introduces full support for gamepad controllers, allowing you to use Xbox, PlayStation, Nintendo Switch Pro, and other SDL-compatible gamepads as macro input devices alongside MIDI controllers.

Quick Start

1. Connect Your Gamepad

  1. Connect your gamepad via USB or Bluetooth
  2. Ensure it’s recognized by your system
  3. Conductor will automatically detect compatible gamepads

Supported Controllers:

  • Xbox 360, Xbox One, Xbox Series X|S
  • PlayStation DualShock 4, DualSense (PS5)
  • Nintendo Switch Pro Controller
  • Any SDL2-compatible gamepad

2. Start from a Template Config

The quickest way to get started is from a pre-configured template config file — copy one of the shipped configs and point Conductor at it:

# Conductor's config lives at:
#   macOS  ~/Library/Application Support/conductor/config.toml
#   Linux  ~/.config/conductor/config.toml
# Create the directory if needed, then copy a template into it. Linux example:
mkdir -p ~/.config/conductor
cp config/examples/gamepad-xbox-basic.toml ~/.config/conductor/config.toml

Built-in gamepad templates (Xbox, PlayStation, Switch Pro, flight stick, HOTAS, racing wheel) also ship under conductor-gui/src-tauri/templates/ — copy one as a starting point and adapt the mappings.

Tip: the GUI also has a Device Templates pane (workspace nav → Device Templates) where you can browse the built-in templates and apply one with a single click — it adds the template to your current config additively, so it never overwrites your existing mappings. When a controller is connected, its matching template is flagged Suggested. (The copy-a-template-file path above is simpler but replaces your config.toml, so use the pane if you want to keep what you already have.)

3. Start Using

# Start Conductor daemon
conductor --foreground

# Your gamepad is now ready to trigger actions!

Configuration Reference

Basic Setup

[device]
name = "Gamepad"
auto_connect = true

[advanced_settings]
chord_timeout_ms = 50          # Multi-button chord detection window
double_tap_timeout_ms = 300    # Double-tap detection window
hold_threshold_ms = 2000       # Long press threshold

Trigger Types

1. GamepadButton

Simple button press trigger.

[[modes.mappings]]
description = "A button: Confirm"
[modes.mappings.trigger]
type = "GamepadButton"
button = 128  # A button (Xbox) / Cross (PS) / B (Switch)
[modes.mappings.action]
type = "Keystroke"
keys = "Return"

Optional fields:

  • velocity_min: Minimum pressure (0-255)

2. GamepadButtonChord

Multiple buttons pressed simultaneously.

[[modes.mappings]]
description = "A+B: Screenshot"
[modes.mappings.trigger]
type = "GamepadButtonChord"
buttons = [128, 129]  # A + B buttons
timeout_ms = 50       # Buttons must be pressed within 50ms
[modes.mappings.action]
type = "Keystroke"
keys = "3"
modifiers = ["cmd", "shift"]

3. GamepadAnalogStick

Analog stick movement detection.

[[modes.mappings]]
description = "Right stick right: Forward"
[modes.mappings.trigger]
type = "GamepadAnalogStick"
axis = 130              # Right stick X-axis
direction = "Clockwise" # Moving right
[modes.mappings.action]
type = "Keystroke"
keys = "RightArrow"
modifiers = ["cmd"]

Stick Axes:

  • 128: Left stick X-axis
  • 129: Left stick Y-axis
  • 130: Right stick X-axis
  • 131: Right stick Y-axis

Directions:

  • "Clockwise": Right (X-axis) or Up (Y-axis)
  • "CounterClockwise": Left (X-axis) or Down (Y-axis)

Dead Zone: Automatic 10% dead zone prevents false triggers

4. GamepadTrigger

Analog trigger threshold detection (L2/R2, LT/RT, ZL/ZR).

[[modes.mappings]]
description = "Right trigger: Volume up"
[modes.mappings.trigger]
type = "GamepadTrigger"
trigger = 133   # Right trigger
threshold = 128 # Half-pull (0-255 range)
[modes.mappings.action]
type = "VolumeControl"
operation = "Up"

Triggers:

  • 132: Left trigger (L2, LT, ZL)
  • 133: Right trigger (R2, RT, ZR)

Button ID Reference

Standard Gamepad Layout

Conductor uses a unified button ID scheme across all gamepads:

Face Buttons (128-131)

IDXboxPlayStationSwitch
128A (South)CrossB
129B (East)CircleA
130X (West)SquareY
131Y (North)TriangleX

The button IDs are vendor-neutral and never change — these are what you map against in config. The GUI labels them for the connected controller: an Xbox pad shows A/B/X/Y, a PlayStation pad Cross/Circle/Square/Triangle, and a Switch Pro pad applies Nintendo’s A↔B / X↔Y position swap (so ID 128 is labeled B there). This is display-only — it doesn’t change IDs or matching.

D-Pad (132-135)

IDButton
132Up
133Down
134Left
135Right

Shoulder Buttons (136-137)

IDXboxPlayStationSwitch
136LB (L1)L1L
137RB (R1)R1R

Stick Clicks (138-139)

IDButton
138Left stick click (L3)
139Right stick click (R3)
IDXboxPlayStationSwitch
140Menu (Start)Options+ (Plus)
141View (Select)Share/Create- (Minus)
142Xbox buttonPS buttonHome

Trigger Buttons Digital (143-144)

IDXboxPlayStationSwitch
143LT (digital)L2 (digital)ZL
144RT (digital)R2 (digital)ZR

Analog Axes

Stick Axes (128-131)

IDControl
128Left stick X-axis
129Left stick Y-axis
130Right stick X-axis
131Right stick Y-axis

Trigger Axes (132-133)

IDControl
132Left trigger analog
133Right trigger analog

Value Range: 0-255 (128 = center for sticks)

Advanced Features

Pattern Detection

Conductor automatically detects advanced button patterns:

Double-Tap

Press the same button twice within 300ms.

# Automatically detected by MIDI Learn
# Manual configuration:
[modes.mappings.trigger]
type = "DoubleTap"
note = 128  # Button ID (reuses Note type)
timeout_ms = 300

Long Press

Hold a button for 2+ seconds.

# Automatically detected by MIDI Learn
# Manual configuration:
[modes.mappings.trigger]
type = "LongPress"
note = 128  # Button ID (reuses Note type)
duration_ms = 2000

MIDI Learn Mode

MIDI Learn now supports gamepad inputs:

Via GUI:

  1. Open Conductor GUI
  2. Click “Learn” next to any mapping
  3. Press a button or move a stick on your gamepad
  4. Conductor automatically generates the correct trigger config

Pattern Detection:

  • Press button once → GamepadButton
  • Press button twice quickly → DoubleTap
  • Hold button → LongPress
  • Press multiple buttons → GamepadButtonChord
  • Move analog stick → GamepadAnalogStick
  • Pull trigger → GamepadTrigger

Mode Switching

Use button chords for mode switching:

[[global_mappings]]
description = "LB+RB: Switch to Media mode"
[global_mappings.trigger]
type = "GamepadButtonChord"
buttons = [136, 137]  # LB + RB
timeout_ms = 50
[global_mappings.action]
type = "ModeChange"
mode = "Media"

Hybrid MIDI + Gamepad Setup

You can use MIDI controllers and gamepads simultaneously:

[device]
name = "Maschine Mikro MK3"  # MIDI device name
auto_connect = true

# Gamepad is automatically detected
# No additional configuration needed!

# MIDI mappings (button IDs 0-127)
[[modes.mappings]]
[modes.mappings.trigger]
type = "Note"
note = 36  # MIDI note

# Gamepad mappings (button IDs 128-255)
[[modes.mappings]]
[modes.mappings.trigger]
type = "GamepadButton"
button = 128  # Gamepad button

Key Points:

  • MIDI uses IDs 0-127
  • Gamepad uses IDs 128-255
  • No conflicts, works seamlessly together

Common Use Cases

Desktop Navigation

[[modes]]
name = "Desktop"
color = "blue"

# Arrow keys on D-Pad
[[modes.mappings]]
[modes.mappings.trigger]
type = "GamepadButton"
button = 132  # D-Pad Up
[modes.mappings.action]
type = "Keystroke"
keys = "UpArrow"

# Copy/Paste on face buttons
[[modes.mappings]]
[modes.mappings.trigger]
type = "GamepadButton"
button = 130  # X button
[modes.mappings.action]
type = "Keystroke"
keys = "c"
modifiers = ["cmd"]

Media Control

[[modes]]
name = "Media"
color = "purple"

# Play/Pause
[[modes.mappings]]
[modes.mappings.trigger]
type = "GamepadButton"
button = 128  # A button
[modes.mappings.action]
type = "Keystroke"
keys = "PlayPause"

# Volume control with triggers
[[modes.mappings]]
[modes.mappings.trigger]
type = "GamepadTrigger"
trigger = 133  # Right trigger
threshold = 64
[modes.mappings.action]
type = "VolumeControl"
operation = "Up"

Browser Control

# Navigate with right stick
[[modes.mappings]]
[modes.mappings.trigger]
type = "GamepadAnalogStick"
axis = 130  # Right stick X
direction = "Clockwise"
[modes.mappings.action]
type = "Keystroke"
keys = "RightArrow"
modifiers = ["cmd"]  # Forward in browser

# Scroll with right stick Y
[[modes.mappings]]
[modes.mappings.trigger]
type = "GamepadAnalogStick"
axis = 131  # Right stick Y
direction = "CounterClockwise"
[modes.mappings.action]
type = "Keystroke"
keys = "DownArrow"  # Scroll down

Troubleshooting

Gamepad Not Detected

Check connection:

# List connected gamepads
conductorctl status

# Check system recognition
# macOS: System Settings > Game Controllers
# Linux: ls /dev/input/js*
# Windows: Devices and Printers

Solutions:

  1. Reconnect the gamepad
  2. Try USB instead of Bluetooth (or vice versa)
  3. Ensure drivers are installed (Windows)
  4. Check SDL2 compatibility

Buttons Not Working

Verify button mapping:

  1. Use MIDI Learn to discover the actual button ID
  2. Check that button IDs are in the 128-255 range
  3. Ensure no conflicting mappings exist

Common mistakes:

  • Using MIDI note IDs (0-127) instead of gamepad IDs (128-255)
  • Incorrect axis ID for analog sticks
  • Dead zone preventing stick triggers

Analog Stick Too Sensitive

Adjust the dead zone threshold by using button triggers instead:

# Instead of analog stick trigger
# Use button-based threshold
[modes.mappings.trigger]
type = "GamepadButton"
button = 134  # D-Pad left instead of stick

Trigger Not Firing

Check threshold:

  • Threshold too high: Lower the value (try 32, 64, 128)
  • Threshold too low: Increase to prevent false triggers
[modes.mappings.trigger]
type = "GamepadTrigger"
trigger = 133
threshold = 64  # Start with 25% pull

Button Chords Not Detected

Adjust chord timeout:

[advanced_settings]
chord_timeout_ms = 100  # Increase from 50ms

Tips:

  • Press buttons as simultaneously as possible
  • Practice the timing
  • Use MIDI Learn to test detection

Performance Notes

Latency

  • Input latency: <1ms (1000Hz polling)
  • Event processing: <1ms
  • Total latency: ~2-5ms (comparable to MIDI)

Resource Usage

  • CPU: <1% (1ms polling intervals)
  • Memory: ~5-10MB additional
  • Battery: Minimal impact on wireless controllers

Compatibility

  • gilrs v0.11: Industry-standard gamepad library
  • SDL2 mappings: Supports 100+ controller types
  • Auto-detection: Works with most modern gamepads

Best Practices

  1. Start with templates: Use official Xbox/PS/Switch templates
  2. Use MIDI Learn: Let pattern detection configure triggers
  3. Test incrementally: Add mappings one at a time
  4. Document custom configs: Add descriptions to mappings
  5. Use global mappings: Mode switches work everywhere
  6. Backup configs: Save working configurations

Custom controller mappings (gamecontrollerdb.txt)

If Conductor doesn’t recognize your controller, or maps its buttons wrongly, you can supply your own SDL mapping without waiting for an update — drop it in ~/.conductor/gamecontrollerdb.txt:

# one SDL mapping per line; first field is the 32-hex controller GUID
03000000de280000ff11000001000000,My Controller,a:b0,b:b1,x:b2,y:b3,platform:Mac OS X,
  • Precedence (low → high): your file < Conductor’s bundled SDL DB < SDL_GAMECONTROLLERCONFIG (the env var Steam sets, which always wins — so your file adds mappings for controllers the bundled DB doesn’t know, but can’t override a recognized one; use SDL_GAMECONTROLLERCONFIG for that).
  • Restart to apply — the file is read only at daemon startup.
  • Size cap 2 MiB; malformed lines are warned-and-skipped (never fatal).
  • Start the daemon with --ignore-user-mappings to bypass a bad override file.
  • Machine-local: a controller’s raw GUID differs across OSes and between USB and Bluetooth, so a file authored on one machine isn’t portable to another.

Run cargo run --bin gamepad_diagnostic to print each connected controller’s guid (the value to put in the file) and the id each control reports.

Examples

Complete examples available in:

  • config/examples/gamepad-xbox-basic.toml
  • The shipped templates under conductor-gui/src-tauri/templates/
  • See Configuration Examples

Further Reading


Need Help?

  • GitHub Issues: https://github.com/amiable-dev/conductor/issues
  • Documentation: https://getconductor.dev/docs
  • Examples: config/examples/