Quick answer: Check capability first (joystick.rumble(0, 0, 0) returns False on unsupported). Use pygame._sdl2.controller.Controller for better cross-pad rumble support.
A combat game triggers controller rumble on hit. Most players feel it; a few don’t. Generic third-party pads don’t support rumble at all; Pygame quietly does nothing.
Capability Check
js = pygame.joystick.Joystick(0)
js.init()
supports = js.rumble(0.0, 0.0, 0) # test call
if not supports:
print("Controller doesn't support rumble")
rumble() returns True if the device accepted the request, False otherwise. Use this to gate UI hints (“Vibration: unsupported”).
Triggering Rumble
low_freq = 0.6 # 0-1
high_freq = 0.4
duration_ms = 200
js.rumble(low_freq, high_freq, duration_ms)
Low/high split is for dualshock-style two-motor pads. Single-motor pads use the higher of the two.
SDL2 Controller API
from pygame._sdl2 import controller
controller.init()
if controller.is_controller(0):
pad = controller.Controller(0)
pad.rumble(0.6, 0.4, 200)
SDL2 controller wraps known pads (Xbox, DS4, DS5, Switch). Rumble works more reliably than the legacy Joystick API.
User Setting
Always have a settings toggle. Some users find rumble irritating; some controllers misreport support. Persist preference:
if settings.rumble_enabled and pad.rumble(...):
rumble_done = True
else:
# silent fallback
pass
Verifying
Test on Xbox, DS4, generic pad. Officially-supported feel rumble; unsupported get no feedback but no crash either. Setting toggles cleanly.
“Rumble support is per-device. Probe, fall back gracefully, give users a toggle.”
For Steam Deck specifically, rumble works via the Controller API but with subtle differences in motor curves — test on Deck explicitly before shipping.