Davidobot/BetterJoy
Allows the Nintendo Switch Pro Controller, Joycons and SNES controller to be used with CEMU, Citra, Dolphin, Yuzu and as generic XInput
Stale — last commit 2y ago
worst of 4 axesnon-standard license (Other); last commit was 2y ago…
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
last commit was 2y ago; no CI workflows detected
- ✓23+ active contributors
- ✓Other licensed
- ⚠Stale — last commit 2y ago
Show 4 more →Show less
- ⚠Concentrated ownership — top contributor handles 60% of recent commits
- ⚠Non-standard license (Other) — review terms
- ⚠No CI workflows detected
- ⚠No test directory detected
What would change the summary?
- →Use as dependency Concerns → Mixed if: clarify license terms
- →Deploy as-is Mixed → Healthy if: 1 commit in the last 180 days
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Forkable" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/davidobot/betterjoy)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/davidobot/betterjoy on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: Davidobot/BetterJoy
Generated by RepoPilot · 2026-05-10 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/Davidobot/BetterJoy shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
WAIT — Stale — last commit 2y ago
- 23+ active contributors
- Other licensed
- ⚠ Stale — last commit 2y ago
- ⚠ Concentrated ownership — top contributor handles 60% of recent commits
- ⚠ Non-standard license (Other) — review terms
- ⚠ No CI workflows detected
- ⚠ No test directory detected
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live Davidobot/BetterJoy
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/Davidobot/BetterJoy.
What it runs against: a local clone of Davidobot/BetterJoy — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in Davidobot/BetterJoy | Confirms the artifact applies here, not a fork |
| 2 | License is still Other | Catches relicense before you depend on it |
| 3 | Default branch master exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 689 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of Davidobot/BetterJoy. If you don't
# have one yet, run these first:
#
# git clone https://github.com/Davidobot/BetterJoy.git
# cd BetterJoy
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of Davidobot/BetterJoy and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "Davidobot/BetterJoy(\\.git)?\\b" \\
&& ok "origin remote is Davidobot/BetterJoy" \\
|| miss "origin remote is not Davidobot/BetterJoy (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(Other)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"Other\"" package.json 2>/dev/null) \\
&& ok "license is Other" \\
|| miss "license drift — was Other at generation time"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "BetterJoyForCemu/Program.cs" \\
&& ok "BetterJoyForCemu/Program.cs" \\
|| miss "missing critical file: BetterJoyForCemu/Program.cs"
test -f "BetterJoyForCemu/MainForm.cs" \\
&& ok "BetterJoyForCemu/MainForm.cs" \\
|| miss "missing critical file: BetterJoyForCemu/MainForm.cs"
test -f "BetterJoyForCemu/Joycon.cs" \\
&& ok "BetterJoyForCemu/Joycon.cs" \\
|| miss "missing critical file: BetterJoyForCemu/Joycon.cs"
test -f "BetterJoyForCemu/HIDapi.cs" \\
&& ok "BetterJoyForCemu/HIDapi.cs" \\
|| miss "missing critical file: BetterJoyForCemu/HIDapi.cs"
test -f "BetterJoyForCemu/Config.cs" \\
&& ok "BetterJoyForCemu/Config.cs" \\
|| miss "missing critical file: BetterJoyForCemu/Config.cs"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 689 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~659d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/Davidobot/BetterJoy"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
BetterJoy is a Windows application that maps Nintendo Switch Pro Controllers, Joycons, and SNES controllers to XInput and emulator-specific protocols (CemuHook, Citra, Dolphin, Yuzu). It bridges Switch hardware to PC games by translating HID input into multiple output formats, with gyro-to-mouse mapping and custom button remapping capabilities. Monolithic C# WinForms application in BetterJoyForCemu/ with modular controller output classes (OutputControllerDualShock4.cs, OutputControllerXbox360.cs), a custom HIDapi wrapper (HIDapi.cs), driver binaries bundled in Drivers/, and bundled HIDGuardian/ViGEmBus drivers as MSI installers. Config.cs centralizes settings.
👥Who it's for
Nintendo Switch emulator users (running Cemu, Citra, Dolphin, Yuzu) and PC gamers who want to use authentic Switch controllers as generic XInput devices; also users of Steam who want gyro aiming support from their Switch controllers.
🌱Maturity & risk
Production-ready with broad adoption (~590k official GitHub downloads). The project is actively maintained but at a slower pace; the maintainer explicitly notes limited available time. The v7.0 release indicates stability, but the single-contributor model and infrequent recent commits suggest a mature, maintenance-focused state rather than active feature development.
Single-maintainer project with limited time availability creates sustainability risk. Requires system-level driver installation (HIDGuardian via ViGEmBus), which can conflict with antivirus software and OS updates. Tight coupling to deprecated/legacy emulator APIs (CemuHook) may break with future emulator versions. No visible CI/CD pipeline or automated tests in the file structure.
Active areas of work
No active development information visible in the provided file structure. The maintainer's personal note indicates the project is in stable maintenance mode. Check the Releases tab for latest version (v7.0) and the wiki for the changelog.
🚀Get running
Clone the repo: git clone https://github.com/Davidobot/BetterJoy.git. Open BetterJoy.sln in Visual Studio. Install driver prerequisites from BetterJoyForCemu/Drivers/ (run ViGEmBusSetup_x64.msi or _x86.msi depending on architecture). Build the solution in Visual Studio, then run BetterJoyForCemu.exe as Administrator.
Daily commands:
Build in Visual Studio (BetterJoy.sln), then execute BetterJoyForCemu.exe as Administrator from the output directory. Ensure drivers are pre-installed via the Drivers/ folder installers.
🗺️Map of the codebase
BetterJoyForCemu/Program.cs— Application entry point that initializes the main form and Windows Forms application lifecycle.BetterJoyForCemu/MainForm.cs— Core UI controller managing controller detection, button remapping UI, and gyro-to-mouse integration—the main loop orchestrator.BetterJoyForCemu/Joycon.cs— Central abstraction for all controller hardware (Switch Pro, Joycons, SNES) including HID communication and sensor fusion.BetterJoyForCemu/HIDapi.cs— P/Invoke bridge to native hidapi.dll, enabling low-level HID device enumeration and I/O.BetterJoyForCemu/Config.cs— Persists and loads user configuration (button remaps, gyro settings, controller assignments) to/from disk.BetterJoyForCemu/MadgwickAHRS.cs— IMU sensor fusion algorithm converting raw accelerometer/gyroscope data into stable orientation quaternions for gyro aiming.BetterJoyForCemu/Controller/OutputControllerXbox360.cs— Translates Nintendo controller state into Xbox 360 XInput format via ViGEm virtual bus for system-wide compatibility.
🛠️How to make changes
Add support for a new Nintendo controller variant
- Update Joycon.cs to recognize the new device VID/PID and implement packet parsing for its unique HID report format. (
BetterJoyForCemu/Joycon.cs) - Extend Joycon.cs to handle the new controller's button layout, sensor calibration, and any custom features. (
BetterJoyForCemu/Joycon.cs) - Add a new icon asset for the controller in the Icons folder. (
BetterJoyForCemu/Icons) - Update MainForm.cs to display the new controller type in the UI and assign it to the correct emulator output handler. (
BetterJoyForCemu/MainForm.cs)
Add a new button remapping feature (e.g., macro support)
- Extend Config.cs to serialize/deserialize the new macro definition (key sequence, timing, conditions). (
BetterJoyForCemu/Config.cs) - Update Reassign.cs dialog to allow users to define and test macros for individual buttons. (
BetterJoyForCemu/Reassign.cs) - Modify Joycon.cs or MainForm.cs to execute the macro when the mapped button is pressed. (
BetterJoyForCemu/Joycon.cs)
Support a new emulator protocol (e.g., custom Cemuhook extension)
- Create a new OutputController subclass (e.g., OutputControllerCemuhookExtended.cs) that implements the protocol. (
BetterJoyForCemu/Controller) - In MainForm.cs, add a configuration option to select the new output protocol and instantiate it for connected controllers. (
BetterJoyForCemu/MainForm.cs) - Update Config.cs to persist the user's protocol choice. (
BetterJoyForCemu/Config.cs)
🔧Why these technologies
- Windows Forms (System.Windows.Forms) — Lightweight, native Windows desktop UI framework requiring no external runtime; directly accessible for hardware control (HID, elevated permissions).
- P/Invoke + hidapi.dll — Cross-platform HID library abstraction; allows uniform access to USB devices (Switch Pro, Joycons) without reinventing low-level HID protocol handling.
- ViGEm (Virtual Gamepad Emulation) — Kernel-mode virtual bus for XInput/DualShock4 emulation; enables system-wide controller compatibility without game-specific patching.
- HIDGuardian (optional driver) — Prevents raw hardware controller from being detected by games, forcing them to use the virtualized XInput output instead.
- Madgwick AHRS (IMU Fusion Algorithm) — Stable sensor fusion for gyroscope aiming; converts noisy 6-DoF IMU to reliable 3D orientation without drift.
⚖️Trade-offs already made
-
Windows Forms instead of WPF or modern UWP
- Why: Simpler dependency tree, faster startup, direct HID access compatibility, minimal runtime overhead.
- Consequence: Limited modern styling/XAML features; must hand-code UI layouts; harder to achieve polished animations.
-
Polling-based input loop (16ms ticks) vs. interrupt-driven
- Why: Simpler synchronization with emulator frame timing; consistent latency; easier multi-controller management.
- Consequence: ~16ms input latency floor; higher CPU usage than event-driven architecture.
-
HIDGuardian as optional install
- Why: Prevents game interference with raw hardware; some users skip it if willing to accept dual-controller conflicts.
- Consequence: Adds complexity (optional driver management); potential driver compatibility issues across Windows versions.
-
In-memory config serialization + file storage
- Why: Simple, fast config reload without external databases; human-readable text files for manual editing.
- Consequence: No concurrent multi-process config safety; config corruption possible on crash during write.
🚫Non-goals (don't propose these)
- Does not support Android/iOS or macOS controllers natively (Windows-only).
- Does not implement real-time network multiplayer synchronization.
- Does not provide graphical remapping of analog stick curves or DeadZone tuning beyond on/off toggles.
- Does not offer persistent logging
🪤Traps & gotchas
Administrator privileges required for keyboard/mouse remapping to work reliably (noted in README). HIDGuardian driver installation can conflict with Windows Defender and antivirus software; users often skip this step causing controller detection failures. ViGEmBus version pinned to 1.16.116 (visible in filename); newer versions may have compatibility breaks. No graceful degradation if drivers fail to load—the app silently fails without clear error messages. Joycon pairing mode (hold sync button 5 seconds) is non-discoverable and must be documented externally.
🏗️Architecture
💡Concepts to learn
- HID (Human Interface Device) Protocol — BetterJoy reads raw HID reports from Switch controllers via HIDapi.cs; understanding HID report structure (buttons, axes, haptic data) is essential for input mapping and troubleshooting
- Virtual Device Driver (ViGEmBus) — The core output mechanism; BetterJoy uses ViGEmBus to create fake XInput gamepads seen by Windows and games, not passthrough of real hardware
- XInput API — Standard Windows gamepad protocol that BetterJoy outputs to; understanding button mapping (A/B/X/Y axes) is required for controller remapping features
- CemuHook Protocol (UDP-based emulator hook) — BetterJoy implements custom UDP protocol for Cemu motion control and button input; differs from XInput and requires separate socket management per-emulator
- Device Filtering (HIDGuardian) — HIDGuardian isolates Switch controllers from the OS to prevent conflicts; understanding driver-level device hijacking explains why admin mode and driver installation are mandatory
- Thread-Safe Collections (ConcurrentList) — BetterJoy manages multiple controllers concurrently; the custom ConcurrentList avoids .NET lock contention for the hot path of input polling from many devices
- Gyro-to-Mouse Mapping (6-DOF → Pointer) — BetterJoy's unique feature that converts Switch gyro accelerometer/gyroscope data into mouse movement; requires sensor fusion and calibration logic not present in standard controller protocols
🔗Related repos
samuelcust/switch-controller— Alternative Windows Switch controller mapper with different architecture; useful for comparing emulator protocol implementationsndeadly/MissionControl— Nintendo Switch controller support library for non-Windows platforms (Linux, macOS); complements BetterJoy for cross-platform use casesViGEm/ViGEmBus— The underlying virtual gamepad driver that BetterJoy depends on; understanding its API is essential for XInput output debuggingcemu-project/Cemu— Primary target emulator for CemuHook protocol; studying its input handling helps debug controller recognition issuesyuzu-emu/yuzu— Secondary target emulator; Yuzu's native controller input system is an alternative path vs. XInput passthrough
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Extract Controller Output Logic into Abstraction Layer
Currently, OutputControllerDualShock4.cs and OutputControllerXbox360.cs are separate implementations with likely duplicated logic. This creates maintenance burden and makes adding new controller types (e.g., Switch Pro native output) difficult. Creating an IOutputController interface and consolidating common logic would reduce code duplication and enable easier future controller support.
- [ ] Create IOutputController.cs interface in BetterJoyForCemu/Controller/ with common methods (Connect, Disconnect, SendInput, etc.)
- [ ] Refactor OutputControllerDualShock4.cs and OutputControllerXbox360.cs to implement the interface and extract shared logic to a base class
- [ ] Update MainForm.cs and Joycon.cs to use dependency injection with IOutputController instead of direct class references
- [ ] Add unit tests in a new BetterJoyForCemu.Tests project to verify both implementations work correctly
Add GitHub Actions Workflow for Building and Release Artifacts
The repo lacks automated CI/CD. There's no verification that the C# solution builds successfully, and manual release builds create inconsistency risk. A GitHub Actions workflow should compile BetterJoy.sln on Windows, run any tests, and automatically create release artifacts (.exe, driver installers).
- [ ] Create .github/workflows/build-and-release.yml with a Windows runner that restores NuGet packages and builds BetterJoy.sln
- [ ] Add a build step that compiles both x86 and x64 variants (evident from the two ViGEmBusSetup installers)
- [ ] Configure artifact upload for compiled .exe and collected driver files from BetterJoyForCemu/Drivers/
- [ ] Add conditional step to create GitHub Release with artifacts only on git tags matching v*.. pattern
Document HIDGuardian Driver Installation and Troubleshooting in README
The driver folder contains .bat scripts for HIDGuardian installation but the README lacks driver setup instructions, troubleshooting steps, or explanation of what HIDGuardian/ViGEmBus do. Contributors and users struggle with driver-related issues that could be prevented with clear documentation.
- [ ] Add 'Installation' section to README.md explaining the two required drivers: HIDGuardian (for controller interception) and ViGEmBus (for virtual XInput output)
- [ ] Document the prerequisites (Windows, admin rights, .NET Framework version from App.config)
- [ ] Add troubleshooting subsection addressing common driver installation failures with links to BetterJoyForCemu/Drivers/README.txt content
- [ ] Document the purpose of BetterJoyForCemu/! Install the drivers in the Drivers folder directory and how to run the batch scripts
🌿Good first issues
- Add unit tests for Config.cs serialization/deserialization; currently no test coverage visible in the repo structure, and config corruption is a common user complaint
- Create a troubleshooting wizard that checks driver installation status (HIDGuardian, ViGEmBus) and provides step-by-step remediation; many issues stem from incomplete driver setup
- Document the CemuHook UDP protocol implementation in code comments and the wiki; currently only one OutputController shows the pattern, making it hard to add new emulator support
⭐Top contributors
Click to expand
Top contributors
- @Davidobot — 60 commits
- @shukenmg — 10 commits
- @RobertBorg — 7 commits
- @ASleepyCat — 2 commits
- @OhBlihv — 2 commits
📝Recent commits
Click to expand
Recent commits
b671563— fix null exception when enableRumble setting to false (#1145) (finalgene)8890b05— Added n64 support (#1138) (GinoMoena)461f5f8— Scale XY coordinates according to new StickScalingFactor configuration option. Closes #630 (#954) (fabiosantoscode)5e3d6ac— - misc (Davidobot)96f5620— Float Parsing issue fix (#884) (BurndiL)95902a5— Update README.md (Davidobot)a685abd— mention rebooting (#648) (cobalt2727)9a9cf60— Add building instructions (#851) (ItachiSan)464eda0— - Fix ZL -> L2 and ZR -> R2 not working in many games (implement digital buttons) (#867) (IonBlade2K)f29a8aa— fix hidapi.dll loading (#816) (mercury233)
🔒Security observations
BetterJoy has several moderate to high-severity security concerns, primarily related to driver installation, binary dependency verification, and the embedded HidCerberus web service lacking proper authentication. The application requires administrative privileges and handles hardware-level operations, making security critical. Key recommendations: (1) Implement cryptographic verification for all binaries and drivers, (2) Secure
- High · Unsigned Driver Installation —
BetterJoyForCemu/Drivers/HIDGuardian/HIDGuardian Install (Run as Admin).bat. The codebase includes HIDGuardian driver files (HidGuardian.sys) that require admin execution. Driver installation scripts run as Administrator without signature verification visible in the repository. This could allow privilege escalation or system compromise if drivers are tampered with. Fix: Ensure all driver files are cryptographically signed with a valid certificate. Verify driver signatures before installation. Consider implementing a driver package signature verification mechanism in the installer. - High · Binary Dependencies Without Verification —
BetterJoyForCemu/x64/hidapi.dll, BetterJoyForCemu/x86/hidapi.dll, BetterJoyForCemu/Drivers/HIDGuardian/_drivers/. The project includes multiple binary DLL files (hidapi.dll, HidCerberus.Lib.dll) and executable files (HidCerberus.Srv.exe, devcon.exe) without visible checksum verification or integrity checking mechanisms. No package.config or NuGet references provided to verify supply chain. Fix: Implement integrity verification for all binary dependencies using cryptographic hashes. Pin dependency versions and verify checksums during installation. Consider migrating to NuGet packages where possible for better dependency management. - Medium · Exposed HID Driver Web Interface —
BetterJoyForCemu/Drivers/HIDGuardian/_drivers/HidCerberus.Srv/. HidCerberus.Srv includes a web interface (index.html) with JavaScript files (api-requests.js, viewmodels.js) that communicates with a service running locally. This could expose device control via HTTP without apparent authentication or HTTPS. Fix: Implement authentication for the HidCerberus web interface. Use HTTPS for all communications. Restrict access to localhost only. Validate all API inputs to prevent injection attacks. - Medium · Missing Security Headers in Web Assets —
BetterJoyForCemu/Drivers/HIDGuardian/_drivers/HidCerberus.Srv/Content/dep/. The HidCerberus web interface includes jQuery and Bootstrap libraries in the Content/dep folder. Without version pinning visible and no Content Security Policy setup evident, XSS vulnerabilities could arise from using older library versions. Fix: Update all JavaScript libraries to the latest secure versions. Implement Content Security Policy (CSP) headers. Use subresource integrity (SRI) checks for external dependencies. Remove jQuery and Bootstrap if not essential. - Medium · Unencrypted Configuration Storage —
BetterJoyForCemu/App.config. App.config file present but no indication of encrypted credential storage. Configuration files in .NET applications may contain sensitive connection strings or API keys in plain text. Fix: Implement encrypted configuration sections for sensitive data. Use Windows Data Protection API (DPAPI) or Azure Key Vault for storing credentials. Never commit secrets to version control. - Low · Missing Security-Related Metadata —
Repository root. No visible security policy file (SECURITY.md), no documented security review process, and no dependency vulnerability scanning configuration (.github/dependabot.yml or similar) detected. Fix: Create a SECURITY.md file with vulnerability disclosure policy. Enable GitHub Dependabot or similar tools for automated dependency vulnerability scanning. Implement signed releases. - Low · Potential Information Disclosure via Error Messages —
BetterJoyForCemu/MainForm.cs, BetterJoyForCemu/Program.cs. No visible error handling configuration in provided files. Windows Forms applications may expose stack traces and system information in error dialogs. Fix: Implement comprehensive try-catch blocks with user-friendly error messages. Log technical details to secure files instead of displaying to users. Sanitize error messages to prevent information disclosure.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.