JoyCon2Mac is a native macOS driver and companion app for Nintendo Switch 2 Joy-Cons. It connects to the Joy-Cons over Bluetooth Low Energy, then exposes virtual HID devices through a DriverKit system extension so macOS apps can see a real controller and mouse.
This release includes a local, unverified DriverKit system extension:
local.joycon2mac.driver
The extension is not notarized and is not distributed through Apple's normal DriverKit approval flow. On a stock Mac, macOS can block it even if the app launches.
For this current development release, install and testing require:
- SIP disabled
- AMFI disabled
- A Mac you are comfortable using for driver development
Do not treat this as a normal consumer install yet. Re-enable SIP/AMFI when you are done testing other software. A future production release should use proper signing, notarization, and Apple-granted DriverKit entitlements instead of this local development setup.
- Download
JoyCon2Mac.app.zipfrom the GitHub release. - Unzip it.
- Move
JoyCon2Mac.appto/Applications. - Launch the app. If Gatekeeper blocks the first launch, right-click the app and choose
Open. - Approve the DriverKit extension in
System Settings -> Privacy & Security. - If macOS asks for a restart, restart.
- Open JoyCon2Mac again.
- Hold
SYNCon each Joy-Con until the LEDs flash, then let the app connect.
If the app opens but no controller or mouse appears in Chrome, SDL apps, or macOS, the system extension is probably not loaded or macOS blocked the unverified driver.
./build_all.sh
open build/JoyCon2Mac.appbuild_all.sh builds the daemon, SwiftUI app, DriverKit extension, and embeds the .dext into the app bundle at:
build/JoyCon2Mac.app/Contents/Library/SystemExtensions/
- Connects left and right Joy-Con 2 controllers over BLE.
- Exposes a virtual DualSense controller for Chrome, macOS GameController clients, SDL, RPCS3, Ryujinx, Game Pass, GeForce NOW, and other cloud gaming apps.
- Supports face buttons, D-pad, shoulders, ZL/ZR, sticks, stick clicks, Plus/Minus, Home, Capture, Chat/C, and rail buttons.
- Routes DualSense rumble output back to both Joy-Cons.
- Provides
SDL Only Mode, which exposes only the DualSense-compatible HID path and hides the generic Joy-Con/mouse devices from strict clients. This fixes duplicate-controller and connect/disconnect churn in cloud apps. - Keeps stable HID identity fields, including serials and nonzero location IDs, so apps are less likely to treat the virtual devices as hotplug churn.
- Uses the Joy-Con 2 optical sensor as a real relative HID mouse.
- Auto-picks whichever Joy-Con is resting on a surface.
- Lets one Joy-Con act as the mouse while the other remains part of the controller pair.
- Defaults mouse mode to
Normal. - Supports
Off,Slow,Normal, andFast. - Uses HID mouse movement and HID wheel reports, so pointer and scroll input go through macOS mouse handling instead of Accessibility-only CGEvent injection.
- Suppresses mouse-owned buttons and stick input so they do not leak into the gamepad report.
- Parses DualSense rumble reports from apps and translates them into Joy-Con vibration packets.
- Supports left, right, and both Joy-Con rumble.
- Adds
Find Left,Find Right, andFind Bothcontrols. - Find My uses a pulsing rumble pattern and stops per Joy-Con after about one second of deliberate shaking from that same Joy-Con's IMU.
- Tracks accelerometer and gyroscope data per Joy-Con.
- Shows individual and fused IMU telemetry in the app.
- Displays a 3D orientation preview, pitch/roll/yaw values, and raw gyro/accelerometer values.
- Uses gravity for pitch/roll. Absolute yaw is gyro-only and will drift because Joy-Cons do not provide a magnetometer.
- Lets the four rail buttons be remapped:
- Left SL
- Left SR
- Right SL
- Right SR
- Bindings persist across restarts.
- SwiftUI menu bar app.
- Controller connection cards with battery, RSSI, packet counters, and telemetry.
- Gamepad tester with live buttons/sticks, SDL mode, Find My controls, and rail remapping.
- Mouse page with mode, source, surface state, sensitivity, and button mapping.
- Gyro page with live motion visualization.
- Settings page for daemon control, driver state, and logs.
- The DriverKit extension is currently local/unverified and needs SIP plus AMFI disabled.
- NFC UI exists, but the backend is not finished.
- True macOS trackpad multitouch gestures are not implemented. Mouse mode is a HID mouse with wheel scrolling, not a virtual trackpad.
- Joy-Con yaw cannot be absolute without a magnetometer. It can be integrated while moving, but it will drift.
- Automatic wake/reconnect depends on the Joy-Con's stored pairing state and macOS BLE behavior. If a Joy-Con stops reconnecting from normal button presses, hold
SYNCto re-enter pairing mode.
Use SDL Only Mode for:
- GeForce NOW desktop app
- Game Pass/cloud gaming desktop apps
- RPCS3 SDL handler
- Ryujinx SDL input
- Any app that shows duplicate controllers or reconnect notifications
Leave SDL Only Mode off when you want:
- The generic Joy-Con HID visible for raw HID/browser experiments
- The virtual HID mouse visible at the same time as the virtual controller
- More debugging visibility in hardware tester sites
- Confirm SIP and AMFI are disabled on the test Mac.
- Open
System Settings -> Privacy & Securityand approve the system extension. - Restart if macOS asks.
- Launch JoyCon2Mac again.
- Check whether the extension is loaded:
systemextensionsctl list | grep joycon2macTurn on SDL Only Mode in Settings and restart the daemon from the app. Chrome can keep stale Gamepad API slots until the page is refreshed, so refresh the tester page after toggling.
Enable SDL Only Mode. The cloud app should see only the DualSense-compatible virtual controller.
- Make sure mouse mode is not
Off. - Put one Joy-Con sensor-side down on a surface.
- Confirm the Mouse page says that side is
on surface. - Restart the daemon if the Joy-Cons were connected before changing modes.
Use an app that sends controller vibration through the DualSense output report path. If only one side vibrates, test with a game/action that emits both left and right motor commands; some games intentionally target one side.
- Hold
SYNCuntil the LEDs flash. - Keep the Joy-Con close to the Mac.
- Remove stale Joy-Con entries from macOS Bluetooth settings if needed.
- Restart the JoyCon2Mac daemon from Settings.
- macOS 13 or newer
- Xcode with DriverKit support
- Xcode command line tools
- CMake 3.20 or newer
- Local driver-development machine with SIP/AMFI disabled for this unverified build
# Full build: daemon, app, driver, embedded system extension
./build_all.sh
# App and daemon only
./build_gui.sh
# DriverKit extension only
./build_driver.shjoycon2-mac-driver/
├── README.md
├── CMakeLists.txt
├── build_all.sh
├── build_gui.sh
├── build_driver.sh
├── JoyCon2Mac/
│ ├── main.mm
│ ├── BLEManager.h/mm
│ ├── PairingManager.h/mm
│ ├── JoyConDecoder.h/cpp
│ ├── MouseEmitter.h/mm
│ └── DriverKitClient.h/mm
├── JoyCon2MacApp/
│ ├── JoyCon2MacApp.swift
│ ├── DaemonBridge.swift
│ ├── ControllersView.swift
│ ├── GamepadView.swift
│ ├── MouseView.swift
│ ├── GyroView.swift
│ ├── NFCView.swift
│ ├── SettingsView.swift
│ ├── DriverExtensionInstaller.swift
│ └── LiquidGlassSupport.swift
└── VirtualJoyConDriver/
├── VirtualJoyConDriver.iig
├── VirtualJoyConDriver.cpp
└── Info.plist
JoyCon2Mac has three pieces:
JoyCon2Mac.app: SwiftUI app, menu bar UI, telemetry, settings, and system extension activation.joycon2mac: native daemon that owns BLE, decodes Joy-Con packets, handles haptics, pairing, mouse mode, and telemetry.local.joycon2mac.driver.dext: DriverKit extension that publishes virtual HID devices and receives reports from the daemon.
The app talks to the daemon through JSON control and telemetry files. The daemon talks to the DriverKit extension through an IOKit user client.
- Keep the virtual DualSense path stable. SDL/cloud apps depend on the Sony VID/PID, report shape, stable serial, and stable location ID.
- Keep
SDL Only Modeas the compatibility path for strict clients. - Do not reintroduce CGEvent-based mouse or gesture output for core mouse behavior. HID mouse reports work without Accessibility permissions.
- The generic Joy-Con HID path is useful for experiments, but it can confuse apps that expect only one controller.
MIT. See LICENSE.