| Crates.io | ctrlassist |
| lib.rs | ctrlassist |
| version | 0.4.0 |
| created_at | 2025-12-07 18:33:45.24632+00 |
| updated_at | 2026-01-14 03:53:49.272504+00 |
| description | Controller Assist for gaming on Linux |
| homepage | https://github.com/ruffsl/CtrlAssist |
| repository | https://github.com/ruffsl/CtrlAssist |
| max_upload_size | |
| id | 1972048 |
| size | 231,304 |

CtrlAssist brings "controller assist" functionality to Linux gaming by allowing multiple physical controllers to operate as a single virtual input device. This enables collaborative play and customizable gamepad setups, making it easier for players of all ages and abilities to enjoy games together. While similar features exist on modern game consoles, CtrlAssist is an open source project that enhances accessibility for PC gaming, offering additional quality-of-life improvements through virtual input devices on Linux.

Combine Primary and Assist controllers into one virtual gamepad.
Screencast_20251230_070245.webm
Split one Primary controller into multiple virtual gamepads.
Screencast_20260108_173024.webm
The following installation methods are available:
PATH per Notes linked aboveInstall or upgrade to the latest version:
cargo install ctrlassist --force
Download latest bundle from releases page and install:
export VERSION=v0.4.0
wget https://github.com/ruffsl/ctrlassist/releases/download/$VERSION/ctrlassist.flatpak
flatpak install --user ctrlassist.flatpak
Run and test via Flatpak using the application ID:
flatpak run io.github.ruffsl.ctrlassist --help
Or launch the system tray via the installed desktop icon.
Use the --help flag for information on each CLI subcommand:
$ ctrlassist --help
Controller Assist for gaming on Linux
Usage: ctrlassist <COMMAND>
Commands:
list List all detected controllers and respective IDs
mux Multiplex connected controllers into virtual gamepad
demux Demultiplex one controller to multiple virtual gamepads
tray Launch system tray app for graphical control
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
-V, --version Print version
Launch the system tray app for graphical control:
$ ctrlassist tray
CtrlAssist system tray started
Configure and control the mux from your system tray
Press Ctrl+C to exit
The system tray provides:
Device invariant options can be altered while the mux is running; all other options are disabled (greyed out) until the mux is stopped.
List all detected controllers and respective IDs:
$ ctrlassist list
(0) Microsoft Xbox One
(1) PS4 Controller
Multiplex first two detected controllers by default:
$ ctrlassist mux
Primary: (0) Microsoft Xbox One
Assist: (1) PS4 Controller
...
Mux Active. Press Ctrl+C to exit.
Manually specify Primary and Assist controllers via IDs:
$ ctrlassist mux --primary 1 --assist 0
Primary: (1) PS4 Controller
Assist: (0) Microsoft Xbox One
...
Manually specify mode for merging controllers:
$ ctrlassist mux --mode priority
Mimic controller hardware for in-game layout recognition:
$ ctrlassist mux --spoof primary
Primary: (0) Microsoft Xbox One
Assist: (1) PS4 Controller
Virtual: (2) Microsoft X-Box One pad (Firmware 2015) [#]
[!WARNING] Combining spoofing with some hiding strategies may also hide the virtual device.
Target force feedback to either, none, or both physical controllers:
$ ctrlassist mux --rumble both
Some modes also support dynamically targeting active controllers:
$ ctrlassist mux --mode toggle --rumble active
Multiple hiding strategies are available to avoid input conflicts:
| Strategy | Access/Compatibility | Granularity | Restart Required |
|---|---|---|---|
| Steam | No root, Flatpak compatible | Vendor/Product ID | Steam only |
| System | Root required, no Flatpak | Per-device | Game/Launcher |
Use Steam hiding when running CtrlAssist via Flatpak. For 2v1 scenarios, where a third player not using CtrlAssist shares the same controller make and model, use System to avoid hiding the third player's gamepad.
Automatically configure Steam's controller blacklist:
ctrlassist mux --hide steam
[!NOTE] Restart Steam for blacklist to take effect; CtrlAssist reverts config on exit.
[!WARNING] Combining this hiding strategy with spoofing may also hide the virtual device.
Restrict device tree permissions system-wide:
sudo ctrlassist mux --hide system
[!NOTE] Restart game/launcher to force rediscovery; CtrlAssist reverts change on exit.
[!IMPORTANT] Not possible via Flatpak sandbox for security. Use
--hide steaminstead.
Demultiplex via unicast between two virtual gamepads by default:
$ ctrlassist demux
Primary: (0) Microsoft Xbox One
Virtual: (1) CtrlAssist Virtual Gamepad [0]
Virtual: (2) CtrlAssist Virtual Gamepad [1]
...
Demux Active. Press Ctrl+C to exit.
Specify Primary via ID and number of virtual controllers:
$ ctrlassist demux --primary 1 --virtuals 3
Primary: (1) PS4 Controller
Virtual: (2) CtrlAssist Virtual Gamepad [0]
Virtual: (3) CtrlAssist Virtual Gamepad [1]
Virtual: (4) CtrlAssist Virtual Gamepad [2]
...
Manually specify mode for virtual controller:
$ ctrlassist demux --mode multicast
Mimic controller hardware for in-game layout recognition:
$ ctrlassist demux --spoof primary
Primary: (0) Microsoft Xbox One
Virtual: (1) Microsoft X-Box One pad (Firmware 2015) [0]
Virtual: (2) Microsoft X-Box One pad (Firmware 2015) [1]
[!WARNING] Combining spoofing with some hiding strategies may also hide the virtual device.
[!NOTE] Hiding virtual devices used by other mux instances of CtrlAssist may be desirable.
Forward force feedback from either none, or active virtual gamepad:
$ ctrlassist demux --rumble active
Same as mux command; see other for details.
The system tray saves settings to $XDG_CONFIG_HOME/ctrlassist/config.toml:
# Mux configuration
[mux]
primary_name = "Microsoft Xbox One"
assist_name = "PS4 Controller"
mode = "Priority"
hide = "Steam"
spoof = "None"
rumble = "Both"
# Demux configuration
[demux]
primary_name = "Microsoft Xbox One"
virtuals = 2
mode = "Unicast"
hide = "Steam"
spoof = "None"
rumble = "Active"
Settings are loaded on startup and saved when using the mux. Controllers are matched by name (best-effort) if IDs change between sessions.
Frequently Asked Questions about the project.
CtrlAssist is designed for anyone who wants to combine multiple controllers into one, enabling collaborative play, real-time assistance, or better gamepad ergonomics. Ideal for accessibility and partial asynchronous input, i.e. offloading camera angle management, movements requiring speed and precision, or on standby backup during difficult combat encounters. CtrlAssist is especially useful for:
While playing "hot potato" with a gamepad may be sufficient for some scenarios, like turn-based games, divvying up level progression, menu navigation, the approach falls short for many reasons:
Accessibility features such as control assist address these issues by enabling simultaneous/partial input from multiple controllers, resulting in more fluid and engaging gameplay.
CtrlAssist was first created out of personal necessity. After migrating household gaming to Linux, including the family living room, the lack of controller assist features found on older consoles like Xbox and PlayStation became clear. CtrlAssist was developed as an open source solution to make group gaming sessions on PC more inclusive and accessible for players of all ages and abilities.
Following its initial release and personal household success, as well as the broader trend of Linux adoption, CtrlAssist evolved from a simple CLI tool into a desktop-friendly utility. This category of accessibility features has significantly enhanced family gaming time, transforming passive spectators into active participants. From helping grandparents experience new immersive and interactive single player stories, to leveling age gaps across nieces and nephews in multiplayer PvPs, to rescuing friends from critical damage and finally overcoming a challenging boss, assistive players may expect as much enjoyment as primary players.
CtrlAssist works with most Linux games that support standard gamepad input. Some games or launchers may require restarting after changing controller visibility or virtual device settings. Note that many games have no explicit setting for controller selection, thus the motivation for various hiding strategies to avoid input conflicts between physical and virtual devices. For best compatibility, use the appropriate hiding strategy as described above.
Even in games that natively support multiple controllers, simultaneous input from multiple devices is often not handled. Most games prioritize one controller at a time, only switching after a period of inactivity. CtrlAssist overcomes this limitation by merging inputs into a single virtual device and providing advanced multiplexing modes for input events, going beyond simple first-come, first-served behavior.
CtrlAssist supports most standard gamepads, such as those with a conventional Xbox or PlayStation layout, including those with strong and weak force feedback (rumble) capabilities. Under the hood, the gilrs crate is used for gamepad input detection and event handling, requiring that controllers have at least 1 button and 2 axes.
However, specialized controller features such as tactile triggers, gyroscopic and accelerometer motion tracking, or more exotic force feedback waveforms are not yet supported. If you have device driver expertise and would like to contribute support for additional controller features, please consider opening a pull request!
Not directly, as CtrlAssist is focused on gamepad input multiplexing. However, it is possible to combine CtrlAssist with more advanced utilities such as InputPlumber to route keyboard and mouse events to virtual gamepads and into CtrlAssist, or vice versa taking virtual gamepads from CtrlAssist to keyboard and mouse events.
Note that mouse and keyboard inputs are typically handled differently from gamepad inputs, as they are core interfaces for operating systems and display managers. Merging events from multiple mice and keyboards is often managed by the OS already, negating the need for simpler multiplexing software.
Yes! For scenarios where multiple primary players would like assistance, such as true split-screen multiplayer, multiple instances of CtrlAssist can be run simultaneously. Each instance will create its own virtual gamepad device, with the tray command also creating multiple separate system tray icons and menus.
Additionally, each instance can use different hiding strategies, spoofing options, and rumble targets to suit the needs of each player. Just be mindful that selected hiding strategies do not conflict between instances, causing one virtual device to be hidden by another instance.
Other basic examples of how else CtrlAssist can be used include:
However, because running multiple instances is possible, more complex setups can be achieved by chaining multiple mux and demux commands together.
Two players can take turns assisting each other using toggle mode:
$ ctrlassist list
(0) PS4 Controller
(1) PS5 Controller
#!/usr/bin/env bash
ctrlassist mux --primary 0 --assist 1 --mode toggle --hide steam &
sleep 1 # wait to ensure virtual devices are discoverable
ctrlassist mux --primary 1 --assist 0 --mode toggle --hide steam &
wait
flowchart LR
A[Assist 1 <br> Controller] --> C[Mux <br> Toggle]
B[Assist 2 <br> Controller] --> C
A --> D[Mux <br> Toggle]
B --> D
C --> E[Virtual 1 <br> Gamepad]
D --> F[Virtual 2 <br> Gamepad]
Or specify a single assist controller by toggling once before duplicating first mux.
Assist multiple Primary players using demux outputs as mux Assist inputs:
$ ctrlassist list
(0) PS4 Controller
(1) PS5 Controller
(2) Xbox One Controller
#!/usr/bin/env bash
ctrlassist demux --primary 2 --virtuals 2 --mode unicast \
--hide steam --spoof primary & # spoof to not also hide virtual 1 & 2
sleep 1 # wait to ensure virtual devices are discoverable
ctrlassist mux --primary 0 --assist 3 --mode priority --hide steam &
sleep 1 # wait to ensure virtual devices are discoverable
ctrlassist mux --primary 1 --assist 4 --mode priority --hide steam &
wait
flowchart LR
A[Assist <br> Controller] --> B[Demux <br> Unicast]
B --> C[Assist 1]
B --> D[Assist 2]
G[Primary 1 <br> Controller] --> E[Mux <br> Priority]
C[Assist 1 <br> Virtual] --> E
D[Assist 2 <br> Virtual] --> F[Mux <br> Priority]
H[Primary 2 <br> Controller] --> F
E --> I[Virtual 1 <br> Gamepad]
F --> J[Virtual 2 <br> Gamepad]
Simply scale with number of Primary players by adjusting the --virtuals count.