Cross-platform gamepad tooling for anyone.
Ultimate multi-platform, multi-language controller tool for remapping and compability layering, Any input device in, any virtual controller out: Xbox, PlayStation or Nintendo. third-party gamepad, keyboard or/and mouse.
v1.0.1 Beta · Built with .NET 10 · Avalonia UI · SDL3 · HIDMaestro · ViGemBus
No pressure — using and sharing the project already helps a lot. Thank you!
GameFlow sits between your physical input — a gamepad, keyboard, mouse, or a phone on your Wi-Fi — and any game or application. It reads that input, transforms it through a saved profile (a stack of mapping rules), and writes the result to one or more virtual controllers the game sees.
This project started as a script to spare players from repetitive inputs that were damaging both their controllers and their hands. It was later rebuilt in Python as Autofire, then in C#/.NET as the architecture you're looking at now, since renamed GameFlow to separate the product from autofire the individual feature.
For the full feature reference, per-rule mechanics, and platform-by-platform verification notes, see WIKI.md.
| Category | Details |
|---|---|
| Autofire | Button and stick autofire, hysteresis-shaped so it doesn't jitter at the trigger threshold |
| Remapping | Button-to-button remap with optional source suppression; toggle rules to flip other rules on/off at runtime |
| Shift layers | "Caps Lock for your controller" — 6 activation modes (Hold, Toggle, Latch, Cycle, Sticky, No Button), long-press-to-engage, idle auto-cancel |
| SOCD cleaning | Opposite-direction key resolution — Last-wins (Snap Tap), First-wins, or Neutral, per button pair |
| Stick Trim | Hold a digital button, a stick modulates it into an analog trigger press — deadzone, ramp rate, and reset-on-release all configurable |
| Multi-source rows | Many inputs → one output. Six combine modes (Maximum, Minimum, Sum, Average, Multiply, FirstActive) or a formula — a small dependency-free expression compiler (s1 - s2, if(s1>0.5, s2, 0), clamp(...)), with 10 starter recipes |
| Gyro aiming | Local / Player / World reference frames, dual-threshold smoothing (kills tremor, keeps flicks sharp), 4 engage modes including a raw-stick gate, drift bias calibration, output to a stick or the mouse |
| Touchpad mapping | Finger-anchored virtual stick, 8-way wedge D-pad, frame-to-frame mouse mode — works on any pad with a touch surface |
| Custom scripting | Sandboxed Lua per control (MoonSharp), for logic the built-in rule types don't cover |
| Button combos | One press → a timed sequence of virtual presses |
| Freeze macro | Captures the stick vector on the rising edge of a button; optional pulse-while-frozen |
| Per-device tuning | Deadzone, anti-deadzone, full-at, sensitivity, response curve, and invert — per stick, per trigger, per slot AND per device, so the same pad feels different on two different slots. Rumble/lighting/adaptive-trigger settings save and reload too (see Known Limitations for what's live vs. saved-only) |
| Keyboard & mouse as gamepad | Full keyboard state (not just a handful of buttons) synthesized into a gamepad snapshot — works on Windows, Linux, and macOS |
| Phone as controller | No app install — open a URL in any phone browser on the LAN. Dual anchored sticks, 8-way D-pad, analog triggers, rumble via the Vibration API, and the phone's own gyroscope/accelerometer feed the same gyro pipeline a DualSense uses. Up to 16 phones at once |
| Multiple virtual controllers | Each "slot" is an independent input → profile → output pipeline, with its own device assignment, output kind, and output provider |
| Profile system | Create, duplicate, rename, import, export — JSON, human-editable |
| Live dashboard | Physical and virtual controllers rendered side by side, per slot |
| Requirement | Version |
|---|---|
| .NET SDK | 10.0 or later |
| Git | Any recent version |
Gamepad/joystick input goes through SDL3 on every platform:
| Platform | File | Location |
|---|---|---|
| Windows | SDL3.dll |
Next to GameFlow.App.exe |
| Linux | libSDL3.so |
System library path, or the app directory |
| macOS | libSDL3.dylib |
System library path (e.g. /usr/local/lib), or the app directory |
An optional gamecontrollerdb.txt (SDL_GameControllerDB) in the application directory extends the built-in mapping database.
GameFlow uses HIDMaestro exclusively: user-mode, no kernel driver, no reboot. Drop HIDMaestro.Core.dll next to GameFlow.App.exe and it activates on next launch. If it isn't available, that slot has no output — GameFlow won't silently substitute something else, and the log and the slot's displayed name both say why. The app runs fine either way; the slot falls back to Preview (dashboard-only, no real device).
Windows also has the fullest platform support: keyboard/mouse-as-gamepad, mouse output (SendInput), and the only real virtual-gamepad output backend.
- Gamepad input: SDL3, works out of the box.
- Keyboard/mouse-as-gamepad: real, via direct
evdevreads (/dev/input/eventN) — not a Windows-only feature anymore. - Mouse output: real, via
uinput(creates an actual virtual input device). - Virtual gamepad output: not built yet — Preview only (see Known Limitations).
Needs the input group for device access without root:
sudo usermod -aG input $USER
# then log out and back in- Gamepad input: SDL3, works out of the box.
- Keyboard/mouse-as-gamepad: real, via
CGEventTap— reads system-wide (no per-device distinction; see the wiki). - Mouse output: real, via
CGEventPost. - Virtual gamepad output: not built yet — Preview only.
Keyboard/mouse capture needs Input Monitoring permission (System Settings → Privacy & Security → Input Monitoring) — without it the app runs fine, that one source just reads as empty.
---
git clone https://github.com/Prooxie/GameFlow.git
cd GameFlow
dotnet build GameFlow.sln
dotnet run --project src/GameFlow.AppFirst launch creates a default profile under:
| Platform | Location |
|---|---|
| Windows | %LOCALAPPDATA%/AutofireNext/ |
| Linux | \~/.local/share/AutofireNext/ |
| macOS | \~/Library/Application Support/AutofireNext/ |
Still named
AutofireNexton purpose — it predates the rename, and changing it would orphan existing installs. Nobody types or sees this path; the app itself always says GameFlow.
---
- Profiles tab → create a profile, or start from the default.
- Devices tab → confirm your physical device is listed.
- Virtual controllers panel → Add controller to create a slot, assign a device, pick an output kind and provider, enable it.
- Dashboard → confirm the virtual side (marked with a VIRTUAL badge) mirrors your input through the mapping.
A slot's own virtual output is hidden from every input picker — you can't feed a virtual controller back in as a source.
Click any virtual controller panel on the Dashboard. The panel outlines on hover, and clicking opens that slot's tuning editor — five tabs: Sticks, Triggers, Rumble, Lighting, Adaptive. Stick and trigger changes are live immediately. Rumble/lighting/adaptive settings save per slot per device but don't reach physical hardware yet (the panel says so).
- Dashboard → Web Controller → enable it.
- Open the shown URL (e.g.
http://10.0.0.5:8080) on a phone on the same Wi-Fi. - Pick a layout, tap Enable motion for gyro aiming.
- The phone appears in the device list as an ordinary gamepad — assign it to a slot like any other device.
**Windows firewall note:** binding to all interfaces needs an admin URL ACL, or the server falls back to localhost-only and phones can't reach it (the log says which mode it's in):
netsh http add urlacl url=http://+:8080/ user=Everyone
Profiles tab → + Add rule against any control. Rules apply in declared order, every polling tick, live — no separate "apply" step.
src/GameFlow.App/appsettings.json:
{
"Runtime": {
"DashboardRefreshHz": 165,
"StartRuntimeOnLaunch": true,
"DefaultCulture": "en",
"Updates": {
"RepoOwner": "Prooxie",
"RepoName": "GameFlow",
"AssetNamePattern": "GameFlow-{tag}-{rid}.zip",
"UserAgent": "GameFlow-UpdateChecker"
}
}
}| Key | Default | Description |
|---|---|---|
DashboardRefreshHz |
165 |
UI refresh rate for the live dashboard |
StartRuntimeOnLaunch |
true |
Whether the input/output runtime starts automatically |
DefaultCulture |
en |
Fallback UI language |
Updates:RepoOwner / RepoName |
Prooxie / GameFlow |
GitHub-releases update checker |
Profiles are JSON in the application data directory (see Quick Start), editable by hand or entirely through the Profiles tab.
Input: sdl (all platforms — gamepads/joysticks everywhere, plus keyboard/mouse synthesis via platform-native raw input), web (phone browsers over the LAN), demo (animated preview, no hardware), none.
Output: hidmaestro (Windows, real virtual gamepad), preview (all platforms, dashboard-only). Mouse-cursor output (separate from gamepad output) is real on all three platforms via SendInput / uinput / CGEventPost.
Older profiles referencing retired providers (XInput, GameInput, x360ce, ViGEm, PS3, and similar) migrate to sdl/hidmaestro automatically.
---
GameFlow/
├── src/
│ ├── GameFlow.App # Avalonia UI, ViewModels, Views, Localization
│ ├── GameFlow.Core # Domain models, mapping pipeline, rule types, formula engine
│ └── GameFlow.Infrastructure # SDL3, HIDMaestro, platform input/output, web controller, profiles
├── tests/
│ ├── GameFlow.Core.Tests # Pipeline, rules, formula compiler, gyro maths
│ └── GameFlow.Infrastructure.Tests # Platform interop, protocol, device catalog
└── .github/workflows/ # CI: build, test, and tag-triggered release packaging
Physical input Keyboard/mouse Phone browser
(SDL3 gamepad/joystick) (Win Raw Input / (WebSocket)
│ Linux evdev / │
│ macOS CGEventTap) │
▼ ▼ ▼
InputSource.ReadAsync() / ReadDevice() ─────────────────────┘
│
▼
DeviceSettingsProcessor (per-device deadzone/curve, before merge)
│
▼
ControllerMappingPipeline (one per active slot, isolated)
├── RuleToggleRule ├── StickTrimRule
├── SocdCleanRule ├── GyroMapRule
├── ButtonRemapRule ├── TouchpadMapRule
├── ButtonAutofireRule ├── MultiSourceMapRule (+ Formula)
├── MultiButtonAutofireRule ├── ControlScriptRule (Lua)
├── ButtonComboRule └── StickThresholdRule
├── StickAutofireRule
└── FreezeLastDirectionRule
│
▼
OutputSink.WriteAsync() (HIDMaestro · Preview)
+ IMouseOutputWriter (SendInput · uinput · CGEventPost, independent of gamepad output)
│
▼
Virtual controller seen by games — hidden from GameFlow's own
input list, so it can never be selected back in as a source.
Shift layers resolve first each tick, gating which rules are active. Every slot's rules live in its profile's JSON, applied in order, live. The dashboard reads from lock-free snapshot stores on its own timer, independent of the mapping pipeline's tick rate.
Documented here rather than discovered by surprise:
- Rumble, RGB/lighting, and adaptive triggers save and reload correctly, but don't reach physical hardware yet. They need a dedicated effects thread — direct SDL writes were found to block the runtime tick over Bluetooth, so this is deliberately not wired in until that thread exists.
- No virtual gamepad output on Linux or macOS. Mouse output is real on both; a real virtual controller (via
uinput's gamepad mode, or DriverKit on macOS) is future work. - Bundled controller theme placement is known-imperfect on some skins — several were generated from asset-pack sprites without authoritative layout data. Fixing this properly needs template-matching each sprite against its base image; tracked, not yet done.
- CGEventTap (macOS) has no per-device keyboard/mouse distinction — one aggregate stream for the whole system, not per-physical-device like Windows/Linux.
- Remote Link, DSU/Cemuhook motion server, per-app profile switching — on the roadmap, not started.
🇬🇧 English · 🇨🇿 Czech · 🇩🇪 German · 🇪🇸 Spanish · 🇫🇷 French · 🇮🇹 Italian · 🇵🇱 Polish · 🇷🇺 Russian — switchable live from Settings.
- Fork the repository
git checkout -b feature/my-feature- Commit your changes
- Open a Pull Request
Keep commits focused with a short description of what changed and why. See WIKI.md for how the mapping pipeline and rule system fit together before adding a new rule type.
MIT — see LICENSE.
- HIDMaestro by hifihedgehog — driver-less, user-mode virtual controller platform.
- SDL_GameControllerDB — the community gamepad mapping database SDL3 input builds on.
- VSCView THEMEENGINE by Nielk1 — the controller theme format GameFlow's controller surfaces are compatible with.
- AL2009man's Gamepad Asset Pack & Prompt Asset Pack — controller and button-prompt art.
- MoonSharp — the sandboxed Lua interpreter behind custom scripting.
Check out my other projects on GitHub, YouTube, and Twitch.
Nazzareno96 — keeping me sane during development, beta testing twitch.tv/nazzareno96
NoobKillerRoof — voicing real hardware/software pain points that inspired this project, beta testing twitch.tv/noobkillerroof