Haptic Feedback

The Xbox One controller and some other USB gamepad controllers have motors built in to provide haptic feedback.
Adding rumbles and vibrations can greatly enhance a game’s experience and provide subtle feedback that is hard to convey through visuals or audio.

ROBLOX provides a service to developers, called the UserInputService.pngHapticService, to handle haptic feedback.

Checking for Vibration Support[edit]

Not all controllers support vibration so it is important to check if the plugged-in controllers have support before attempting to use the UserInputService.pngHapticService. To check if a given controller has rumble support at all, you can call IsVibrationSupported

local HapticService = game:GetService("HapticService")
local isVibrationSupported = HapticService:IsVibrationSupported(Enum.UserInputType.Gamepad1)

Once you have checked if a gamepad supports vibration, you should also check if it supports the motors you intend to use. On XBox One the controller has 4 motors:

  • Large: in the left side of the controller. Good for generic rumble.
  • Small: in the right side of the controller. Good for more subtle rumbles(tire slipping, electric shock, etc.)
  • Left Trigger: underneath the left trigger. Good for braking, gun reloading, etc.
  • Right Trigger: underneath the right trigger. Good for recoil, acceleration, etc.

On PC you will have less guarantee of what kind of controller the user has. Many only support the Large and Small motors (no triggers). You can use IsMotorSupported to see if the user’s controller supports the motor you want to use.

local HapticService = game:GetService("HapticService")
local isVibrationSupported = HapticService:IsVibrationSupported(Enum.UserInputType.Gamepad1)
local largeSupported = false
if isVibrationSupported then
	largeSupported = HapticService:IsMotorSupported(Enum.UserInputType.Gamepad1, Enum.VibrationMotor.Large)
end

Turning on Motors[edit]

Once you have confirmed that a user’s gamepad supports vibration you can start using the gamepad motors. You can use SetMotor to turn on a specific motor on a gamepad. This function takes the gamepad and the amplitude of the vibration as arguments. The amplitude can be any value between 0 and 1 (0 being no vibration, 1 being max vibration).

local HapticService = game:GetService("HapticService")
HapticService:SetMotor(Enum.UserInputType.Gamepad1, Enum.VibrationMotor.Large, .5)

You can also use GetMotor to get the current vibration amplitude of a given motor.

local HapticService = game:GetService("HapticService")
print(HapticService:GetMotor(Enum.UserInputType.Gamepad1, Enum.VibrationMotor.Large)

Example[edit]

This example shows how to integrate vibration feedback to a simple ROBLOX vehicle. It assumes there is a vehicle Model in the Workspace called Car, and that the Model has a VehicleSeat called Seat in it.

local HapticService = game:GetService("HapticService")
 
local Car = workspace:WaitForChild("Car")
local Seat = Car:WaitForChild("VehicleSeat")
local driving = false
 
wait()
 
local function onOccupantChanged()
	-- Check whether someone is getting in the seat or out of it
	if Seat.Occupant then		
		-- Get the player that sat in the seat and check if it's the local player
		local player = game.Players:GetPlayerFromCharacter(Seat.Occupant.Parent)
		if player == game.Players.LocalPlayer then
			-- Local player is sitting in the seat, start low rumble
			driving = true
			HapticService:SetMotor(Enum.UserInputType.Gamepad1, Enum.VibrationMotor.Large, .05)
		end
	else
		-- No one is in the seat anymore, make sure the rumble stops
		driving = false
		HapticService:SetMotor(Enum.UserInputType.Gamepad1, Enum.VibrationMotor.Large, 0)	
	end
end
 
local function update()
	-- Check if the local player is driving
	if driving then
		-- Get the speed of the seat so we can set the small rumble proportionally
		local velocity = Seat.Velocity.magnitude
		local speedPercentage = velocity / Seat.MaxSpeed
		HapticService:SetMotor(Enum.UserInputType.Gamepad1, Enum.VibrationMotor.Small, speedPercentage)
	else
		-- Player isn't driving, make sure the small motor is off
		HapticService:SetMotor(Enum.UserInputType.Gamepad1, Enum.VibrationMotor.Small, 0)
	end
end
 
-- Bind Seat's Changed event
Seat.Changed:connect(function(property)
	if property == "Occupant" then
		onOccupantChanged()
	end
end)
 
-- Setup update loop
while wait() do
	update()
end

See Also[edit]