A Vencord plugin for navigating Discord with a game controller.
Discord lets you bind push-to-talk to a gamepad button, but you cannot move around the client with one. This adds a focus ring you drive with the d-pad, and a virtual cursor for anything the ring cannot reach.
I play from bed, streaming the PC across the room, so most evenings the only thing in my hands is a controller. Discord is open the whole time and every part of it wants a mouse. Discord will happily bind push-to-talk to a gamepad button, which tells you somebody there thought about controllers and then stopped, so switching channel, answering a call or muting someone still meant reaching for a keyboard I was nowhere near.
| Input | Action |
|---|---|
| START | Toggle on and off |
| D-pad | Move the focus ring |
| A | Activate |
| B | Leave a submenu, or close the current menu |
| X | Go to the call |
| Y | Disconnect from voice |
| LB / RB | Jump one column left or right |
| LT / RT | Back and forward |
| Select | Toggle the virtual cursor |
| Right stick | Scroll |
X handles three cases, because the call is not always on screen: it moves to the nearest tile if the call view is open, navigates to the channel you are connected to if you are looking somewhere else, and otherwise lands on the call controls.
In cursor mode the left stick moves the pointer, RT left-clicks, LT right-clicks.
Vencord compiles plugins into its bundle, so there is no file you can drop in. You build Vencord yourself with this plugin included. You will need Node, pnpm and git.
git clone https://github.com/Vendicated/Vencord
cd Vencord
pnpm install
mkdir -p src/userplugins
cp -r /path/to/controllernav/plugin src/userplugins/controllerNav
pnpm build
pnpm injectRestart Discord, then enable ControllerNav in Vencord's plugin list.
Two things to watch for:
- Turn off auto-update in Vencord's settings. It will otherwise replace your build with the official one, which does not include your plugins.
- If you already have userplugins, build with all of them together. Shipping a bundle that is missing some will remove them, and Vencord may reset your enabled plugin list.
Every interactive element on screen is a target. Moving picks the nearest one in the direction you pressed, favouring targets that stay in line with you, so a row of buttons steps sideways rather than diagonally.
Discord marks its real controls with a zero-sized [role="button"] carrying an
aria-label, nested inside the visible element. Voice participants, soundboard
sounds and call tiles are all built that way and expose nothing on the visible
element itself, so those are found by taking the parent of each focus target.
That is language independent and does not depend on Discord's class names.
While a menu or dialog is open, only its contents are reachable. Discord nests submenus inside their parent menu, so a menu only counts as a separate layer when nothing else contains it.
Activating dispatches a full pointer sequence at whatever sits under the centre
of the target. Discord puts its handlers on inner overlays, and on the <video>
element for a stream, so clicking the target itself often does nothing.
- No text entry. You can focus the message box but not type into it. Steam's on-screen keyboard works if you are in Big Picture.
- Call tiles and the call view carry no role or label of their own, so a Vencord patch marks them, anchored on identifiers in Discord's source rather than on class names. If Discord restructures those components the patch stops matching and says so in the console instead of failing silently.
- The call view keeps a class-name fallback behind its marker, so it still works in the window between Discord changing and the patch being fixed. It is the only class name matched anywhere in the plugin.
- Steam Input can take the controller. If Steam applies a desktop layout it may hand Discord a virtual pad or convert it to mouse and keyboard. Give Discord a layout that passes the gamepad through.
- Chromium only reports gamepads to a focused window, so press any button once after clicking on Discord.
GPL-3.0-or-later, same as Vencord.
Client modifications are against Discord's terms of service.