English | 简体中文 | Deutsch | 日本語
ControllerKeys — Gamepad-to-Keyboard Remapper for macOS
A native macOS app that remaps game controllers to keyboard shortcuts, mouse input, macros, scripts, webhooks, and system commands — turning any gamepad into a full desktop input device. Supports Xbox Series X|S, Xbox Elite Series 2, PS5 DualSense, PS4 DualShock 4, Nintendo Joy-Con, Switch Pro Controller, Steam Controller, the Apple TV Siri Remote, the keychain-sized 8BitDo Zero 2 and Micro, and 300+ third-party gamepads. Features DualSense and Steam Controller touchpad and gyroscope mouse control, swipe typing, a JavaScript scripting engine, per-app profile auto-switching, a realtime low-latency input mode, and Mac-to-Mac controller handoff over Wi-Fi.
Download — free trial | Website & Docs | Buy a license | Discord
⭐ Find it useful? Star the repo — it helps others discover ControllerKeys.
!ControllerKeys - Button Mappings
Use Cases
- Vibe coding from the couch — map shortcuts, run macros, and pair with voice transcription (e.g., VoiceInk) for full hands-free coding
- Couch computing & media browsing — navigate macOS, switch apps, scroll, and type from across the room
- Accessibility — alternative input for users who can't use a keyboard/mouse; works with macOS Accessibility Zoom
- Streaming & content creation — trigger OBS scenes, mute/unmute, fire webhooks, and show button overlays on stream
- Presentations — laser pointer overlay, app switching, and clicker-style navigation with any gamepad
Why This App?
There are other controller mapping apps for macOS, but none offered everything I needed:
| Feature | ControllerKeys | Joystick Mapper | Enjoyable | Controlly | |---------|:--------------:|:---------------:|:---------:|:---------:| | DualSense touchpad + quadrant mapping | ✅ | ❌ | ❌ | ❌ | | Steam Controller (no Steam required) | ✅ | ❌ | ❌ | ❌ | | Multi-touch gestures (tap, pinch, pan) | ✅ | ❌ | ❌ | ❌ | | Gyroscope aiming & gesture detection | ✅ | ❌ | ❌ | ❌ | | JavaScript scripting engine | ✅ | ❌ | ❌ | ❌ | | Swipe typing on-screen keyboard | ✅ | ❌ | ❌ | ❌ | | Chord mappings (button combos) | ✅ | ❌ | ❌ | ✅ | | Button sequence combos | ✅ | ❌ | ❌ | ❌ | | Layers (alternate mapping sets) | ✅ | ❌ | ❌ | ❌ | | Per-layer stick mode override | ✅ | ❌ | ❌ | ❌ | | Custom stick direction bindings (WASD, arrows, anything) | ✅ | ❌ | ❌ | ❌ | | Per-mapping haptic feedback | ✅ | ❌ | ❌ | ❌ | | Macros & system commands | ✅ | ❌ | ❌ | ❌ | | HTTP webhooks & OBS control | ✅ | ❌ | ❌ | ❌ | | Realtime low-latency key mode | ✅ | ❌ | ❌ | ❌ | | Mac-to-Mac controller handoff (Universal Control-style) | ✅ | ❌ | ❌ | ❌ | | Linked controllers (auto-switch profile per controller) | ✅ | ❌ | ❌ | ❌ | | Profile snapshots & undo (History tab) | ✅ | ❌ | ❌ | ❌ | | On-screen keyboard & command wheel | ✅ | ❌ | ❌ | ❌ | | Community profiles with setup guides | ✅ | ❌ | ❌ | ❌ | | App-specific profile auto-switching | ✅ | ❌ | ❌ | ❌ | | Stream overlay for OBS | ✅ | ❌ | ❌ | ❌ | | Xbox Elite Series 2 paddles | ✅ | ❌ | ❌ | ❌ | | Nintendo Joy-Con & Pro Controller | ✅ | ❌ | ❌ | ❌ | | Apple TV Siri Remote as a controller | ✅ | ❌ | ❌ | ❌ | | DualSense Edge (Pro) support | ✅ | ❌ | ❌ | ❌ | | DualShock 4 (PS4) touchpad & gyro | ✅ | ❌ | ❌ | ❌ | | DualSense LED & microphone control | ✅ | ❌ | ❌ | ❌ | | Drag-and-drop mapping swap | ✅ | ❌ | ❌ | ❌ | | Usage stats & Controller Wrapped | ✅ | ❌ | ❌ | ❌ | | Localized (EN / 简中 / 繁中 / DE / JA) | ✅ | ❌ | ❌ | ❌ | | Third-party controllers (~313) | ✅ | ✅ | ✅ | ✅ | | Native Apple Silicon | ✅ | ❌ | ❌ | ✅ | | Actively maintained (2026) | ✅ | ❌ | ❌ | ✅ | | Source available | ✅ | ❌ | ✅ | ❌ |
Joystick Mapper hasn't been updated since November 2019 and lacks modern controller support. Enjoyable is abandoned since 2014. Controlly is solid but doesn't support touchpad gestures, on-screen keyboard, or scripting. Steam's controller mapping only works within Steam games, not system-wide.
Features
- Button Mapping: Map any controller button to keyboard shortcuts
- Layers: Create alternate button mapping sets activated by holding or toggling a designated button
BASE / LAYER chip plus outlines in the layer's color show exactly which buttons, sticks, shoulders, D-pad directions, and touchpad regions the current layer overrides
- Custom Stick Direction Mappings: Set either stick's mode to Custom and each of its 8 directions (4 cardinal + 4 diagonals) becomes a real button you can bind from the controller graphic
- Realtime Input Latency Mode: Per-profile Input setting that sends simple key mappings as key-down on press and key-up on release, bypassing the chord-detection window for lower latency
- Universal Control-style Mac-to-Mac Relay: Pair two ControllerKeys-running Macs and push your controller's cursor against a configured screen edge to hand off mouse, keyboard, and mapped actions to the second Mac
100.64.0.0/10, localhost)
- HMAC-SHA256 authenticated frames with a Keychain-stored shared secret; oversized, replayed, or tampered frames are dropped
- Swipe typing, on-screen overlays, and stuck-button cleanup all relay through the same channel
- Linked Controllers: Bind a profile to a specific physical controller so it auto-activates whenever that controller connects (Linked Apps still win when the frontmost app has its own profile)
- JavaScript Scripting: Write custom automation scripts powered by JavaScriptCore
press(), hold(), click(), type(), paste(), delay(), shell(), openURL(), openApp(), notify(), haptic(), and more
- App-aware scripting with app.name, app.bundleId, app.is() for context-sensitive actions
- Trigger context (trigger.button, trigger.pressType, trigger.holdDuration)
- screenshotWindow() API for capturing the focused window
- Per-script persistent state that survives across invocations
- Built-in example gallery with ready-to-use scripts
- Script editor with syntax reference and AI prompt assistant
- Macros: Multi-step action sequences
- System Commands: Automate actions beyond key presses
- HTTP Webhooks: Send HTTP requests from controller buttons and chords
- OBS WebSocket Commands: Control OBS Studio directly from controller buttons
- Continuous Trigger MIDI: Hardware → Triggers → Continuous Trigger MIDI maps trigger travel to a channel/CC with range, inversion, and deadzone controls. Native meters sample raw pressure at 60 Hz without animated lag or repeated SwiftUI layout. A muted preview, actual conflict edit links, distinct suggested CCs, and a Logic Pro walkthrough help verify setup. Output uses the ControllerKeys virtual source, suppresses duplicate values, limits updates to 60 Hz, and returns to the configured neutral value on pause, disconnect, or profile change. Unchanged streams continue through layer changes.
- Joystick Control:
- Gyroscope Aiming & Gestures (DualSense/DualShock 4):
gyroToggle(), gyroSetActive(bool), gyroIsActive() compose gyro control with key output on one button
- 1-Euro filter for jitter-free smoothing with responsive tracking
- Gesture mappings: Tilt forward/back and steer left/right to trigger actions
- Per-profile gesture sensitivity and cooldown sliders
- DualShock 4 gyro parsed from raw HID reports with auto-calibration
- Touchpad Control (DualSense/DualShock 4/Steam Controller):
- On-Screen Keyboard, Commands, and Apps: Use the on-screen keyboard widget to quickly select apps, commands, or keyboard keys
- Command Wheel: GTA 5-inspired radial menu for quick app/website switching
- Stream Overlay for OBS: Floating overlay showing active button presses for stream capture
- Laser Pointer Overlay: On-screen pointer for presentations
- Directory Navigator: Controller-driven file browser overlay
- Cursor Hints: Visual feedback showing executed actions above the cursor
- Controller Wrapped: Usage stats with shareable personality-typed cards
- Profile System: Create and switch between multiple mapping profiles
- History & Snapshots: ControllerKeys silently snapshots your full configuration before every destructive operation (delete profile, import profile, restore snapshot) and exposes them in a dedicated History tab
~/.config/controllerkeys/snapshots/ (capped at 20)
- Profile-Import Safety Prompts: Imports that contain shell commands, scripts, Shortcuts actions, or webhook follow-up commands open an explicit consent sheet listing each execution surface.
- Visual Interface: Interactive controller-shaped UI for easy configuration
- DualSense Support: Full PlayStation 5 DualSense controller support
- DualSense Edge (Pro) Support: Full support for Edge-specific controls
- DualShock 4 (PS4) Support: Full PlayStation 4 DualShock 4 support
- Xbox Elite Series 2 Support: Full support for Elite-specific hardware
- Steam Controller Support: Detected over raw HID without requiring Steam to run
- Nintendo Controller Support: Joy-Con and Pro Controller
- Apple TV Siri Remote Support: Pair the 2nd-generation Siri Remote to your Mac over Bluetooth and use it as a controller
- 8BitDo Zero 2 & Micro Support: First-class support for the keychain-sized 8BitDo pads, with on-screen layouts built just for them
- S29 Bluetooth Button Ring Support (experimental): Use the S29 "TikTok scroller" ring as a tiny wireless controller
- 8BitDo Ultimate 2 Wireless Support (experimental): Elite-style on-screen layout with all four back paddles
- Third-Party Controller Support: ~313 controllers supported via SDL database
- Accessibility Zoom Support: Controller input works correctly when macOS Accessibility Zoom is active
- Controller Lock Toggle: Lock/unlock all controller input with haptic feedback
- Hide Dock Icon: Option to run as a menu-bar-only app
More Screenshots
On-Screen Keyboard with Swipe Typing
!On-Screen KeyboardJavaScript Scripting
!JavaScript ScriptingMacros
!MacrosChord Mappings
!Chord MappingsDualSense Touchpad
!DualSense Touchpad SettingsDualSense LED Customization
!DualSense LEDsKeyboard Widget Settings
!On-Screen Keyboard Settings8BitDo Zero 2 & Micro (custom on-screen layouts)
!8BitDo Zero 2 button mappings !8BitDo Micro button mappingsSupported Controllers
| Brand | Controllers | |-------|------------| | Xbox | Series X\|S, Xbox One, Xbox 360, Xbox Elite Series 2 (with paddles) | | PlayStation | DualSense (PS5), DualSense Edge, DualShock 4 v1/v2 (PS4) | | Nintendo | Switch Pro Controller, Joy-Con (single or paired L+R) | | Valve | Steam Controller (touchpads, gyro, grips, haptics — no Steam required) | | Apple | Siri Remote / Apple TV Remote 2nd gen (clickpad cursor, edge scroll, mappable side buttons) | | Rings | S29 Bluetooth button ring (swipes, holds, Camera + Home; experimental) | | 8BitDo | Zero 2 & Micro (dedicated on-screen layouts + corrected d-pad), Ultimate 2 Wireless (Home + 4 back paddles; experimental), Pro 2, SN30 Pro, SN30 Pro+, Ultimate, Lite, and more | | Logitech | F310, F510, F710 | | PowerA | Enhanced Wired, Fusion Pro | | Hori | HORIPAD, Fighting Commander, and more | | Others | ~313 controllers total via SDL gamecontrollerdb |
Any controller recognized by macOS's GameController framework works out of the box. Unrecognized controllers fall back to the SDL database for automatic button mapping.
DualSense as a wired USB controller (Pico bridge)
Want a DualSense to appear as a wired USB controller — for the lowest-latency real-time path, or a setup that rejects Bluetooth? A Raspberry Pi Pico 2 W running DS5Dongle bridges the controller over Bluetooth and presents it to macOS as wired USB; ControllerKeys then treats it like any wired DualSense, touchpad and all. To also get the controller's speaker working as a macOS audio output (the stock dongle firmware can't drive it on macOS), see ds5dongle-pico2w-macos — a prebuilt Pico 2 W build plus setup steps.
Requirements
- macOS 14.6 or later
- A supported controller (see above) connected via Bluetooth or USB
- Accessibility permissions (for input simulation)
- Automation permissions (for launching Terminal app with commands)
Installation
Free 14-day trial — no account needed. Install either way:
Homebrew
brew install --cask nsevent/tap/controllerkeys
Direct download
- Download the latest DMG from GitHub Releases
- Open the DMG and drag the app to
/Applications - Launch and grant Accessibility permissions when prompted
- Automation permissions will be requested when using terminal commands from the on-screen keyboard
Unlock the full version: ControllerKeys is free to use for 14 days. To keep using it afterward, buy a license on Gumroad and enter your key in Settings → General.
Want a tour first? The website has demo videos, the full feature breakdown, and an FAQ.
Trust & Transparency
This app requires Accessibility permissions to simulate keyboard and mouse input. We understand this is a sensitive permission, so the app's complete source is public for inspection under the PolyForm Noncommercial 1.0.0 license.
Why this app is safe:
- Source Available: The app source is available for audit. You can inspect it or build your own copy for noncommercial purposes under the PolyForm Noncommercial 1.0.0 license; it is not permissively licensed open-source software.
- Pseudonymous, Opt-Out Usage Analytics: ControllerKeys sends a random install ID, app/build and Mac details, locale, install channel, trial/license lifecycle and conversion events, one-time setup milestones, and daily coarse usage aggregates. Those aggregates contain only an action-count range, broad feature categories, and whether a complex action was used. License activation also sends the Gumroad sale ID, making that install linkable to its purchase record; the analytics database does not store buyer names or email addresses. The server derives approximate country and stores a salted IP hash, not the raw IP. Specific controller inputs, key codes, typed text, profile names or contents, app names, macro steps, scripts, quick text, URLs, controller identifiers, license keys, and configuration are never sent as analytics. Turn this off in Settings → General → Privacy; this deletes pending events and also disables Sparkle's weekly system profile, while update and license checks still connect. See the Privacy Policy.
- Local Input Statistics: Raw HID reports, typed text, and mapping contents are not transmitted as analytics. ControllerKeys does store aggregate button/action counts, movement distances, and session totals locally for recommendations and Controller Wrapped; these remain on your Mac.
- Signed & Notarized: Official releases are signed with Kevin Tang's Apple Developer ID, notarized by Apple, and verified by Gatekeeper. This establishes publisher identity and release integrity; it does not prove byte-for-byte equivalence with the public source.
What the Accessibility permission is used for:
- Simulating keyboard key presses (when you press controller buttons)
- Simulating mouse movement (when you move the left joystick)
- Simulating scroll wheel events (when you move the right joystick)
CGEvent API to generate these input events. This is the same API used by accessibility tools, automation software, and other input remapping utilities.
Architecture
Architecture overview — useful for security review and contributors. The app source is public for transparency; if ControllerKeys is useful to you, please support its development by buying a license on Gumroad.
flowchart LR
subgraph group_app["macOS app"]
node_app_entry["App entry<br/>SwiftUI app"]
node_main_window["Main window<br/>SwiftUI shell<br/>[ContentView.swift]"]
node_menu_bar["Menu bar<br/>popover UI<br/>[MenuBarView.swift]"]
node_controller_service["Controller service<br/>input boundary"]
node_hid_parsing["HID parsing<br/>controller IO"]
node_motion_touch["Motion & touch<br/>controller adapters"]
node_mapping_engine["Mapping engine<br/>decision core"]
node_gesture_seq["Gestures & chords<br/>input semantics"]
node_action_exec["Action executor<br/>output boundary"]
node_input_sim["Input simulator<br/>accessibility output"]
node_profile_manager["Profile manager<br/>config lifecycle"]
node_profile_load["Profile load<br/>config pipeline"]
node_app_monitor["App monitor<br/>context service<br/>[AppMonitor.swift]"]
node_ui_services["UI services<br/>overlay utilities"]
node_macro_script["Macros & scripts<br/>[MacroExecutor.swift]"]
node_integration["Integrations<br/>OBS · webhooks"]
node_security["Security guards<br/>safety boundary"]
node_swipe_runtime["Swipe typing<br/>runtime model"]
end
subgraph group_pipeline["ML pipeline"]
node_swipe_pipeline["Swipe training<br/>ML pipeline<br/>[train.py]"]
end
node_community_profiles["Community profiles<br/>profile library"]
node_app_entry -->|"launches"| node_main_window
node_app_entry -->|"shows"| node_menu_bar
node_main_window -->|"observes"| node_controller_service
node_main_window -->|"edits"| node_profile_manager
node_main_window -->|"drives"| node_ui_services
node_menu_bar -->|"uses"| node_app_monitor
node_controller_service -->|"parses"| node_hid_parsing
node_controller_service -->|"adapts"| node_motion_touch
node_controller_service -->|"feeds"| node_mapping_engine
node_hid_parsing -->|"normalizes"| node_mapping_engine
node_motion_touch -->|"specializes"| node_gesture_seq
node_mapping_engine -->|"resolves"| node_gesture_seq
node_mapping_engine -->|"emits"| node_action_exec
node_profile_manager -->|"loads"| node_profile_load
node_profile_manager -->|"configures"| node_mapping_engine
node_profile_manager -->|"imports"| node_community_profiles
node_app_monitor -->|"switches"| node_profile_manager
node_action_exec -->|"synthesizes"| node_input_sim
node_action_exec -->|"invokes"| node_macro_script
node_action_exec -->|"controls"| node_integration
node_action_exec -->|"updates"| node_ui_services
node_macro_script -->|"consults"| node_security
node_security -->|"guards"| node_integration
node_security -->|"audits"| node_profile_load
node_swipe_pipeline -->|"exports"| node_swipe_runtime
node_swipe_runtime -->|"feeds"| node_action_exec
click node_app_entry "https://github.com/NSEvent/xbox-controller-mapper/blob/main/XboxControllerMapper/XboxControllerMapper/XboxControllerMapperApp.swift"
click node_main_window "https://github.com/NSEvent/xbox-controller-mapper/blob/main/XboxControllerMapper/XboxControllerMapper/Views/MainWindow/ContentView.swift"
click node_menu_bar "https://github.com/NSEvent/xbox-controller-mapper/blob/main/XboxControllerMapper/XboxControllerMapper/Views/MenuBar/MenuBarView.swift"
click node_controller_service "https://github.com/NSEvent/xbox-controller-mapper/blob/main/XboxControllerMapper/XboxControllerMapper/Services/Controller/ControllerService.swift"
click node_hid_parsing "https://github.com/NSEvent/xbox-controller-mapper/blob/main/XboxControllerMapper/XboxControllerMapper/Services/Controller/HIDReportParser.swift"
click node_motion_touch "https://github.com/NSEvent/xbox-controller-mapper/blob/main/XboxControllerMapper/XboxControllerMapper/Services/Controller/ControllerService+Touchpad.swift"
click node_mapping_engine "https://github.com/NSEvent/xbox-controller-mapper/blob/main/XboxControllerMapper/XboxControllerMapper/Services/Mapping/MappingEngine.swift"
click node_gesture_seq "https://github.com/NSEvent/xbox-controller-mapper/blob/main/XboxControllerMapper/XboxControllerMapper/Services/Mapping/GestureDetector.swift"
click node_action_exec "https://github.com/NSEvent/xbox-controller-mapper/blob/main/XboxControllerMapper/XboxControllerMapper/Services/Mapping/MappingActionExecutor.swift"
click node_input_sim "https://github.com/NSEvent/xbox-controller-mapper/blob/main/XboxControllerMapper/XboxControllerMapper/Services/Input/InputSimulator.swift"
click node_profile_manager "https://github.com/NSEvent/xbox-controller-mapper/blob/main/XboxControllerMapper/XboxControllerMapper/Services/Profile/ProfileManager.swift"
click node_profile_load "https://github.com/NSEvent/xbox-controller-mapper/blob/main/XboxControllerMapper/XboxControllerMapper/Services/Profile/ProfileConfigurationLoadCoordinator.swift"
click node_app_monitor "https://github.com/NSEvent/xbox-controller-mapper/blob/main/XboxControllerMapper/XboxControllerMapper/Services/UI/AppMonitor.swift"
click node_ui_services "https://github.com/NSEvent/xbox-controller-mapper/blob/main/XboxControllerMapper/XboxControllerMapper/Services/UI/CommandWheelManager.swift"
click node_macro_script "https://github.com/NSEvent/xbox-controller-mapper/blob/main/XboxControllerMapper/XboxControllerMapper/Services/Macros/MacroExecutor.swift"
click node_integration "https://github.com/NSEvent/xbox-controller-mapper/blob/main/XboxControllerMapper/XboxControllerMapper/Services/Integration/OBSWebSocketClient.swift"
click node_security "https://github.com/NSEvent/xbox-controller-mapper/blob/main/XboxControllerMapper/XboxControllerMapper/Services/Input/UniversalControlRelaySecurity.swift"
click node_swipe_runtime "https://github.com/NSEvent/xbox-controller-mapper/blob/main/XboxControllerMapper/XboxControllerMapper/Services/Input/SwipeTypingEngine.swift"
click node_swipe_pipeline "https://github.com/NSEvent/xbox-controller-mapper/blob/main/SwipeModel/train.py"
click node_community_profiles "https://github.com/NSEvent/xbox-controller-mapper/blob/main/community-profiles/OBS%20Live%20Streaming.json"
classDef toneLavender fill:#ede9fe,stroke:#7c3aed,stroke-width:1.5px,color:#2e1065
classDef toneBlue fill:#bfdbfe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneTeal fill:#99f6e4,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
classDef toneIndigo fill:#c7d2fe,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneNeutral fill:#f1f5f9,stroke:#94a3b8,stroke-width:1.5px,color:#334155
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
class node_app_entry,node_main_window,node_menu_bar toneLavender
class node_controller_service,node_hid_parsing,node_motion_touch toneBlue
class node_profile_manager,node_profile_load,node_app_monitor,node_community_profiles toneTeal
class node_mapping_engine,node_gesture_seq,node_action_exec toneIndigo
class node_input_sim,node_ui_services,node_macro_script,node_integration,node_swipe_runtime toneNeutral
class node_security toneRose
class node_swipe_pipeline toneAmber
For a deeper technical walkthrough of each component — service responsibilities, threading model, the SDL HID fallback, performance profile — see ARCHITECTURE.md.
Project Structure
XboxControllerMapper/XboxControllerMapper/
├── XboxControllerMapperApp.swift # App entry point & service container
├── Config.swift # Constants & UserDefaults keys
├── Models/ # Profiles, mappings, chords, sequences, gestures, LED settings
├── Services/
│ ├── Controller/ # GameController + raw HID input (PlayStation, Steam, Apple TV Remote, Elite, SDL fallback)
│ ├── Mapping/ # Mapping engine, chord/sequence/gesture detection, action execution
│ ├── Input/ # CGEvent input simulation, swipe typing, Mac-to-Mac relay
│ ├── Profile/ # Config persistence, snapshots, community profile import
│ ├── Scripting/ # JavaScriptCore script engine
│ ├── Macros/ # Macro execution
│ ├── Integration/ # OBS WebSocket, webhooks
│ └── UI/ # Overlays: command wheel, on-screen keyboard, cursor hints, stream overlay
├── Views/
│ ├── MainWindow/ # Main window tabs & mapping sheets
│ ├── MenuBar/ # Menu bar popover
│ └── Components/ # Shared SwiftUI components
└── Resources/ # SDL controller database, swipe-typing model
See ARCHITECTURE.md for service responsibilities, the input pipeline, and the threading model.
Default Mappings
| Button | Default Action | |--------|---------------| | A | Left mouse click (hold to drag) | | B | Return — long-hold: ⌘Return | | X | Delete (repeats while held) | | Y | Escape | | LB | ⌥ (hold) | | RB | ⌃ (hold) | | LT | F13 | | RT | ⌘ (hold) | | D-pad | Arrow keys (repeat while held) | | Menu | ⌘V — double-tap: ⇧⌘V, long-hold: ⌘L | | View | ⌘C — double-tap: ⌘A | | Xbox | Space | | Share | ⌘⌥ (hold) | | L-Stick Click | ⌥A — long-hold: ⌘Tab | | R-Stick Click | ⌃C — double-tap: ⌘W | | Left Joystick | Mouse movement | | Right Joystick | Scroll |
Usage
- Connect your controller via Bluetooth or USB (System Settings → Bluetooth)
- Launch ControllerKeys
- Grant Accessibility permissions when prompted
- Click any button on the controller visualization to configure its mapping
- Use the menu bar icon for quick access to enable/disable and profile switching
Contributing
Contributions are welcome! See CONTRIBUTING.md for the full guide. To get started:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Run
make test-regressions, and test with a physical controller when your change touches input handling - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Community
Join the ControllerKeys Discord to share profiles, get help, and connect with other users.
Feature Requests
Have an idea for a new feature? I'd love to hear it!
- Open an issue on GitHub with the
feature requestlabel - Describe the feature and the problem it solves
- Include any mockups or examples if applicable
Issues & Bug Reports
Found a bug? Please help by reporting it:
- Check existing issues to avoid duplicates
- Open a new issue with:
The more detail you provide, the easier it is to diagnose and fix the issue.
License
Source Available - See LICENSE for details.
The app source is public for transparency and security auditing under the PolyForm Noncommercial 1.0.0 license. ControllerKeys is free to try for 14 days; a license on Gumroad unlocks continued use.