Every thread about using a controller for music eventually produces a link to an open-source gamepad-to-MIDI project on GitHub, usually posted without a date. Some of those projects are fifteen-year-old Objective-C that cannot be built with a current toolchain. One of them is a few weeks old. This piece goes through the three that keep coming up — swisspol/MidiHID, quotepilgrim/midibox360 and taiga696/dualsense-midi — with their archive status, last activity, dependency list and the limitations their own authors documented. Then it covers the forty-line version you can write yourself, and the point at which writing it yourself stops being the cheap option.
- MidiHID — macOS, Objective-C, GPL-3.0. Archived October 2020, last push 2017, targets Mac OS X Leopard and later.
- midibox360 — Xbox 360 pads, Python, MIT. Archived July 2022. Needs loopMIDI on Windows.
- dualsense-midi — macOS, Python, MIT. Created October 2026, active, but its own README says learning-use rather than professional gigs.
- Roll your own — pygame plus mido is about forty lines for one axis. Deadzones and hot-plug are the actual work.
- Do not put an archived repo on a stage. There is nobody to escalate to at 7pm.
Are there open-source gamepad to MIDI projects on GitHub?
Yes, several — the three that dominate search results and forum answers are MidiHID for macOS, midibox360 for Xbox 360 pads, and the much newer dualsense-midi for the PS5 pad on macOS.
Here they are side by side, with every field taken from the repositories themselves.
| swisspol/MidiHID | quotepilgrim/midibox360 | taiga696/dualsense-midi | |
|---|---|---|---|
| What it does | Any USB HID device to MIDI | Xbox 360 pad to MIDI notes and chords | DualSense to MIDI on a Core MIDI port |
| Platform | Mac OS X | Linux and Windows | macOS 11 or later |
| Language | Objective-C, Lua config | Python 3 | Python 3.9+ |
| Dependencies | Built as a Mac app | toml, mido, pygame | pygame, python-rtmidi |
| Licence | GPL-3.0 | MIT | MIT |
| Repository created | December 2013 | October 2018 | October 2026 |
| Last code push | June 2017 | June 2019 | October 2026 |
| Archived | Yes — October 2020 | Yes — July 2022 | No |
| Stars at time of writing | 33 | 8 | 0 |
| Reads motion sensors | No | No | No — explicitly ignored |
| Reads touchpad | No | N/A | Not documented |
| Needs a separate port driver | No — Core MIDI | Yes on Windows (loopMIDI) | No — Core MIDI |
Fields read from swisspol/MidiHID, quotepilgrim/midibox360 and taiga696/dualsense-midi at time of writing. Star counts move; archive dates do not.
Is MidiHID still maintained?
No — the repository was archived in October 2020, is read-only, and its last code push was in 2017.
It is worth being fair about what MidiHID was, because it was ambitious and it worked. Its description is "Allows you to use your favorite USB devices as MIDI controllers on Mac OS X", and the README's scope is broader than any commercial gamepad tool today: any USB HID device, including keyboards, joysticks, game controllers and Guitar Hero peripherals, mapped to any MIDI-compatible Mac software, with Lua configuration files. That is a genuinely lovely design, and the Lua config approach is more flexible than most mapping editors.
It also targets Mac OS X Leopard and later. In practice that means a project predating Apple Silicon, predating the notarisation and hardened-runtime requirements, predating several generations of permission prompts around input monitoring, and predating the DualSense by roughly a decade. Building it today is a research project. We cover the current native route for the same job in reading any HID controller on macOS and the GameController framework deep dive.
Does midibox360 still work?
Possibly — it is plain Python with three dependencies, so it has a better survival profile than a compiled Mac app, but it was archived in July 2022 and has documented quirks.
What it does, per its own README: "midiBox360 turns an Xbox 360 controller into a MIDI controller that can be used on any DAW", mapping inputs to notes, chords and scale adjustments via the face buttons and triggers. It is Python 3 with toml, mido and pygame, officially supports Linux and Windows, and the author notes it may run elsewhere untested.
Two limitations the author documents, both worth knowing before you spend an hour:
- Holding LT and RT together may not work on Windows, because of how pygame reports the 360 pad's triggers. On the original Xbox 360 controller the two triggers share a single axis, so pressing both cancels out — a hardware-era quirk, not a bug in the code.
- It was written for the Xbox 360 pad specifically, so other controllers may not map correctly. That is honest and correct; button index ordering varies wildly between pads.
On Windows it also needs a virtual MIDI cable such as loopMIDI, named in its settings file. On Linux it writes to an available ALSA port directly. If you want the background on why that difference exists, our virtual port explainer covers all three platforms.
What does taiga696/dualsense-midi do?
It reads a DualSense on macOS using pygame and python-rtmidi and re-emits buttons, sticks and triggers as MIDI on a Core MIDI virtual port, with motion sensors explicitly ignored.
This is the newest of the three — the repository was created in October 2026 — and it is the only one that is not archived. Its README is unusually candid about scope, which we respect: it is macOS 11 or later on Apple Silicon or Intel only, it has no jog-wheel support and the author notes that "scratching does not feel right", it targets minimal two-deck DJ setups, the PS button can trigger system UI, iOS and iPadOS are unsupported, and it is recommended for learning rather than professional gigs.
Installation is the standard Python shape:
git clone https://github.com/taiga696/dualsense-midi
cd dualsense-midi
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
python3 dualsense_midi.py What it does not do is read the touchpad as an XY surface, the gyroscope, the adaptive triggers or the haptics — the inputs that make a DualSense different from a 2013 gamepad. Those are the hard parts, they require going below SDL's game-controller abstraction to the raw HID report, and no small project in this space has taken them on. If that is what you came for, the touchpad XY guide and the adaptive trigger guide describe what the hardware is actually capable of.
Can I write my own gamepad to MIDI script?
Yes — pygame's joystick module reads the controller and mido or python-rtmidi writes the MIDI, and a single-axis bridge is about forty lines.
import pygame, mido
pygame.init()
pygame.joystick.init()
pad = pygame.joystick.Joystick(0)
pad.init()
print("found:", pad.get_name())
out = mido.open_output('My Gamepad Bridge', virtual=True)
DEADZONE = 0.08
last = -1
clock = pygame.time.Clock()
while True:
pygame.event.pump()
raw = pad.get_axis(0) # left stick X, -1.0 .. 1.0
if abs(raw) < DEADZONE:
raw = 0.0
value = int((raw + 1.0) * 63.5) # scale to 0 .. 127
if value != last:
out.send(mido.Message('control_change', control=1, value=value))
last = value
clock.tick(120) # poll at 120 Hz That works. It is also where the project begins rather than ends, because the next six things are all real:
- Deadzones per axis, because a worn stick does not rest at zero. See stick drift compensation.
- Response curves, because linear is wrong for a trigger driving a filter. See velocity curves.
- Debouncing buttons, so one press is one note.
- Hot-plug, so unplugging the pad does not take the script down. See hot-swapping mid-song.
- Hanging notes on disconnect — a held note with no note-off is the classic live failure.
- Poll rate versus CPU, which is a real trade and not a constant. See the latency comparison.
On Windows, note that virtual=True is not supported by the underlying backend the way it is on macOS and Linux, so you will need loopMIDI and an existing port name. On Linux you may prefer evdev directly over SDL — the evdev guide covers why.
Should I use an archived project for a live gig?
No — an archived repository has no maintainer, so an OS update, a controller firmware revision or a dependency bump will eventually break it with nobody to fix it.
For a studio experiment, an archived project is a fine way to spend a Sunday and you may learn more from reading the source than from any product. For anything with a start time and an audience, the calculus is different: you want software that someone is accountable for, that handles disconnects deliberately, and that has been tested against the OS you are running this month.
This is the honest case for a maintained tool, and it is not a feature-list argument. Our tour rider piece and club residency guide are both written around the same assumption: the input layer must be the boring, dependable part of the rig.
What are these projects genuinely good for?
Reading. The source of a small gamepad bridge is the clearest available explanation of how this whole category works.
A forty-line Python script shows you the entire pipeline with nothing hidden: open the device, poll it, scale the value, send the message. Every commercial tool in this space is doing the same four things with more care around the edges, and once you have seen it written down, feature lists become much easier to read critically. You will immediately understand why a tool that reads through SDL cannot see a touchpad, and why poll rate is a trade rather than a setting to maximise.
They are also the right starting point if your requirement is genuinely unusual — an arcade stick with a non-standard board, a flight yoke, a sensor you built. No commercial tool will cover that, and a script you own will. The arcade stick guide and the Eurorack CV bridge both sit in that territory.
How do you evaluate a gamepad MIDI repository in two minutes?
Read five fields before the README, in this order: archive banner, last push date, language, dependency list, and open issue count.
Each one tells you something specific about how your evening is going to go:
- Archive banner. Read-only, no issues, no pull requests. Whatever is broken stays broken.
- Last push date. Anything older than a major OS release is a gamble. Anything older than the controller you own cannot know about it.
- Language. Interpreted beats compiled for longevity. A Python script from 2019 usually still runs; a Mac app from 2017 usually does not build.
- Dependencies. Three well-known packages is fine. A pinned fork of something obscure is a red flag, because that fork has to still exist.
- Open issues. On an active project, issues are health. On an archived one, they are a list of the problems you are about to inherit — and worth reading precisely for that.
A sixth check that costs nothing: search the repository for your controller's name. If "DualSense" appears nowhere in a project that claims gamepad support, it predates the hardware and will treat your pad as a generic device.
What does a production-grade bridge do that a script does not?
The unglamorous things that only announce themselves when something goes wrong in front of people.
Having built one, this is the honest list of what separates forty lines from a shipping tool, and none of it is features you would put on a landing page:
- Hot-plug and reconnect. A pad that drops and comes back should resume, not require a restart. See disconnect handling for live sets.
- All-notes-off on disconnect. A held note with no note-off is a drone that outlasts the song.
- Per-axis calibration persisted between sessions. Every controller is slightly different and every worn controller is more different.
- Rate limiting on CC output. A stick polled at 500 Hz will flood a MIDI stream and a DAW's automation lane. See the latency and poll-rate comparison.
- Correct behaviour on OS permission prompts. On macOS, input monitoring is a permission; a tool that fails silently when it is denied is indistinguishable from broken hardware.
- Code signing and notarisation. Not a feature, but the difference between an install and a forty-minute detour.
- Multiple controllers without index collisions. Pad ordering is not stable across reboots on any platform.
None of that is a criticism of the projects above — they were written to solve one person's problem and they did. It is an argument about what you are signing up for when you adopt one for a rig other people depend on. If you enjoy maintaining it, that is a real and valid answer. If you do not, the maintenance is the product you are actually buying.
FAQ
Are there open-source gamepad to MIDI projects on GitHub?
Yes, several — the best known are swisspol/MidiHID for macOS, quotepilgrim/midibox360 for Xbox 360 pads, and the newer taiga696/dualsense-midi for the PS5 DualSense on macOS.
Is MidiHID still maintained?
No — the swisspol/MidiHID repository was archived in October 2020 and is read-only, its last code push was in 2017, and it targets Mac OS X Leopard and later, which is far older than any currently supported macOS.
Does midibox360 still work?
It may, because it is plain Python using toml, mido and pygame, but the repository was archived in July 2022 and its README notes that holding both triggers together can fail on Windows due to how pygame reports them.
What does taiga696/dualsense-midi do?
It reads a PS5 DualSense on macOS using Python with pygame and python-rtmidi and re-emits buttons, sticks and triggers as MIDI on a Core MIDI virtual port, while explicitly ignoring the motion sensors.
Can I write my own gamepad to MIDI script?
Yes — pygame reads the controller and mido or python-rtmidi writes the MIDI, and a working single-axis bridge is roughly forty lines, though deadzones, curves, debouncing and hot-plug handling are what turn forty lines into a project.
Should I use an archived open-source project for a live gig?
No — an archived repository has no maintainer, so an OS update, a controller firmware revision or a dependency change will break it with nobody to fix it, which is an unacceptable risk on stage.
The short version
Two of the three canonical open-source answers in this space are archived and the third is a new, honestly-scoped personal project. All three are worth reading. None of them read a touchpad, a gyroscope or an adaptive trigger, which is most of the reason to use a modern controller in the first place.
If you want to learn how this works, clone one and read it — that is a genuinely good use of an afternoon. If you want it to work tonight, download Universal Controller MIDI free: one controller, one virtual port and three saved presets on the free tier, AUD $89 one-time for Pro with the mapping editor, multi-controller routing, MPE and OSC. Next: the full field comparison.