Gamepad Rumble and Controller Lighting
A single feedback service that drives dual-motor rumble, LED lightbar color and Switch Pro HD vibration across PS5, PS4, Nintendo Switch Pro and generic gamepads.
What the system is for
Gamepad output in Unity looks straightforward until you ship on a second platform. SetMotorSpeeds wipes the lightbar color. SetLightBarColor cancels the motor output. Bluetooth output is silently inert on PlayStation controllers. Switch Pro HD vibration and the Home-button LED have no Unity API at all. Every team that has shipped a controller-driven game has hit at least one of these bugs.
Serenity's Feedback system owns the full HID output report for each controller family and exposes everything through a single IFeedbackService. Play effects by id, address specific players or devices, scale intensity globally from settings, and trigger anything without code through the Event Dispatcher — all without touching platform-specific driver details.
The Unity problem
Unity's Gamepad API exposes SetMotorSpeeds and SetLightBarColor as two separate calls, but the controller actually receives a single shared output report. Writing motor speeds clobbers the lightbar and vice versa. On top of that, Unity's output path is inert over Bluetooth for DualSense and DualShock 4, and Switch Pro HD vibration is entirely absent from the Unity API. Projects that need both rumble and LED feedback on more than one platform end up writing fragile per-platform patches that break on every Unity upgrade.
Multiplayer games face a second problem: Gamepad.current is ambiguous when multiple controllers are connected. Knowing which physical pad triggered the last action — and therefore which pad should rumble — requires tracking acting devices explicitly. Without that, rumble either plays on the wrong controller or is disabled entirely to avoid the bug.
How Serenity approaches it
IFeedbackService is the single entry point: Play(id) triggers an effect by id on the acting device, PlayForPlayer(playerIndex, id) and PlayForDevice(deviceId, id) address specific targets, PlayForPlayerInput(playerInput, id) integrates with Unity's PlayerInput component, StopAll() cancels active effects, SetGlobalEnabled(bool) and SetGlobalIntensity(float) apply accessibility settings live. Effects are dispatched to IFeedbackChannel implementations — VibrationChannel handles dual-motor rumble via VibrationEffect (low and high amplitude independently), LedChannel handles lightbar color via LedEffect.
Multiplayer addressing is handled through FeedbackTarget, which can represent the acting pad, a player index, or a device id. UnityGamepadTargetResolver maps those targets to physical devices, and IActingDeviceTracker records which connected gamepad fired the last action — eliminating the Gamepad.current ambiguity. FeedbackScaler reads vibration_enabled and vibration_intensity from GameSettings and applies them live so accessibility preferences take effect without restarting effects.
How it fits into Serenity
The Feedback system lives in the Serenity.Feedback namespace and follows the foundation's layered structure. The Business layer defines IFeedbackService, IFeedbackChannel, IFeedbackEffect, FeedbackTarget, VibrationEffect, LedEffect and FeedbackScaler behind stable interfaces. The Unity layer provides the HID backends: DualSenseOutputWriter for PS5, DualShock4OutputWriter for PS4, and SwitchProVibrationService for Nintendo Switch Pro HD vibration streamed on a background thread at approximately 100 Hz. Generic gamepads fall back to Unity's Gamepad.SetMotorSpeeds. The HID layer includes RawHidDevice and GamepadHidrawTransport for raw device access, plus GamepadHardwareIds identifying each supported controller family, with the extra handling PlayStation controllers require over Bluetooth. This system is output only — gyro and accelerometer motion input on the same DualSense, DualShock 4 and Switch Pro pads lives in the Player Input system.
Platform support is Windows (USB via ExecuteCommand, Bluetooth via raw HID), Linux (all output via hidraw with udev rules required) and macOS (USB only). Authoring uses FeedbackDefinition ScriptableObjects that bundle effects and are referenced by id. No-code triggering is available through PlayFeedbackSignal via the Event Dispatcher. The system cooperates with PlayerInput for player and device resolution, with GameSettings for live intensity scaling, and with the Event Dispatcher for signal-based triggers.
Practical workflow
- Create a FeedbackDefinition ScriptableObject and add VibrationEffect and LedEffect entries with the amplitudes and colors you need.
- Assign the definition to the project and reference it by its id wherever you want to trigger feedback.
- Let the feedback installer register IFeedbackService and all channels through the initialization pipeline.
- Use IActingDeviceTracker to know which gamepad fired the last action and resolve it through FeedbackTarget.
- Call IFeedbackService.Play(id) from gameplay code, or dispatch PlayFeedbackSignal from the Event Dispatcher for no-code triggers.
- Wire vibration_enabled and vibration_intensity in GameSettings so FeedbackScaler applies accessibility preferences live.
What you get
- Single entry point IFeedbackService with Play, PlayForPlayer, PlayForDevice, PlayForPlayerInput, StopAll, SetGlobalEnabled and SetGlobalIntensity
- VibrationEffect with independent low-amplitude and high-amplitude motor control dispatched through VibrationChannel
- LedEffect for lightbar RGB color dispatched through LedChannel
- Full HID output ownership: DualSenseOutputWriter (PS5), DualShock4OutputWriter (PS4), SwitchProVibrationService (Switch Pro)
- Bluetooth support via a raw HID layer for PlayStation controllers
- Switch Pro HD vibration streamed on a background thread at ~100 Hz plus monochrome Home-button LED
- Multiplayer addressing through FeedbackTarget, UnityGamepadTargetResolver and IActingDeviceTracker
- FeedbackScaler wired to GameSettings vibration_enabled and vibration_intensity for live accessibility control
- No-code trigger via PlayFeedbackSignal through the Event Dispatcher
When to use this
- Projects that need rumble and LED output on PS5, PS4 or Nintendo Switch Pro and have hit Unity's motor/lightbar clobber bug.
- Games shipping on Linux or needing Bluetooth gamepad output that Unity's default path does not deliver.
- Multiplayer games that must rumble the correct controller when several gamepads are connected.
- Codebases that want accessibility-ready global intensity and enable control without per-site conditionals.
Related systems
Use Serenity's Feedback system when you need reliable gamepad output across controller families and platforms, with a single service call as the only integration point your gameplay code ever touches.
English
Español
Català