Unity haptics setup
How HEXR haptics work, how to add them with or without code, and how to design them so they feel right.
How haptics work
Every haptic takes the same path, whatever starts it:
- Six channels per glove — the five fingers and the palm. Each one either presses (a steady squeeze) or vibrates.
- The hand-near check stops most haptics unless the hand is actually at something: grabbing it, about to poke it, or inside a
ProximityCheck. Your rig’s interactors set it for you. Details in The hand-near check.
Add haptics without code
Three components cover most objects. Add them in the Inspector like any other component.
Feel an object you grab — HexRInteractableHaptics
The place to start. It presses the fingers while your XR SDK grabs or hovers the object.
Add it next to your interactable
On an object that already has XR Grab Interactable (OpenXR) or Meta’s Grabbable and Hand Grab Interactable. Leave Backend on Auto.
Choose when it fires
Set Fire On to Select (while grabbed), Hover (while the hand is close) or Select And Hover.
Choose fingers and strength
Tick the fingers to press and set Strength, from 10 (barely there) to 60 (firm). Turn on Follow Grip to scale it by how far the fingers close, between Min Strength and Strength.
Make a surface feel like something — SpecialHaptics
Put it on a trigger collider and press Auto Find Hand Physics. Any finger that touches the collider feels the effect you pick in Type Of Haptics — no grab or hand-near check needed.
| Effect | Feels like |
|---|---|
CustomHaptics | Constant pressure while touched |
CustomVibrations | Vibration at a set frequency and strength |
FountainEffect | Running water |
RainDropEffect | Raindrops on the palm |
HeartBeatEffect | A beating heart, regular or irregular |
HandSqueezeEffect | Fires an event when the hand closes on the zone |
For the two custom effects, set Haptic Pressure and, for vibration, Vibrations Frequency Value.
Objects your interactors don’t cover — ProximityCheck
It fires nothing itself: it tells HEXR the hand is near, so the object’s other haptics are let through. Add it with a trigger collider and press Auto Set Up.
Buttons pressed by a fingertip, finger-curl events and more: Which component do I need?
Trigger haptics from code
For game events, sequences, or anything the components don’t cover, call the hand’s Pressure Controller directly. Give your script a PressureTrackerMain field and drag in the scene’s Left or Right Pressure Controller.
One finger
// using HaptGlove; using HexR;
// hand: a PressureTrackerMain field with a Pressure Controller dragged in
// Pressure: finger, on/off, intensity 0.1-1, ramp speed 0.1-1, skip hand-near check
hand.CustomSingleHaptics(Haptics.Finger.Index, true, 0.5f, 1f, false);
// Vibration: finger, on/off, intensity 0.1-1, frequency 0.1-40 Hz, skip hand-near check
hand.CustomSingleVibrations(Haptics.Finger.Palm, true, 0.7f, 12f, false);
// The components' 10-60 strength scale, as the 0.1-1 intensity above
float intensity = PressureTrackerMain.StrengthToIntensity(30f);The last argument skips the hand-near check. Pass true for presses that answer your own events rather than a touch.
Several fingers, same setting
Pass six on/off values, one per channel, always in the order Thumb, Index, Middle, Ring, Pinky, Palm. These calls have no skip argument, so they only fire while the hand is near something.
// One entry per channel: Thumb, Index, Middle, Ring, Pinky, Palm
bool[] grip = { true, true, true, false, false, false }; // thumb, index and middle
hand.TriggerCustomHapticsIncrease(grip, 0.6f, 1f); // pressure 0.1-1, ramp speed 0.1-1
hand.TriggerCustomlVibrations(grip, 0.7f, 12f); // intensity 0.1-1, frequency 0.1-40 Hz
// (the stray "l" is in the real name)
hand.TriggerAllHapticsIncrease(0.5f); // every channel, pressure 0.1-1
hand.TriggerAllVibrations(12f); // every channel, frequency
hand.TriggerPinchPressure(0.6f); // thumb and index togetherEach finger its own setting
Build one command for all six channels and send it straight to the glove: one Bluetooth write, with no hand-near check. Every array needs one entry per finger, in the same order, or the call throws an error.
HaptGloveHandler glove = HexRManager.Instance.rightHand; // or leftHand
// All six channels, always in this order
Haptics.Finger[] fingers = { Haptics.Finger.Thumb, Haptics.Finger.Index,
Haptics.Finger.Middle, Haptics.Finger.Ring,
Haptics.Finger.Pinky, Haptics.Finger.Palm };
bool[] on = { true, true, true, true, true, true }; // false leaves a channel out
float[] intensities = { 0.3f, 0.9f, 0.9f, 0.6f, 0.4f, 0.8f };
float[] speeds = { 1f, 1f, 1f, 1f, 1f, 1f };
glove.BTSend(glove.haptics.HEXRPressure(fingers, on, intensities, speeds));
// Vibration: here frequency comes BEFORE intensity. 0 Hz is clamped to 0.1 Hz, not off.
float[] freqs = { 3f, 12f, 12f, 6f, 3f, 3f };
glove.BTSend(glove.haptics.HEXRVibration(fingers, on, freqs, intensities));Letting go
hand.CustomSingleHaptics(Haptics.Finger.Index, false, 0f, 1f, true); // one finger's pressure
hand.CustomSingleVibrations(Haptics.Finger.Palm, false, 0f, 12f, true); // one finger's vibration
hand.RemoveIndexHaptics(); // one finger: RemoveThumbHaptics() ... RemovePalmHaptics()
hand.RemoveAllHaptics(); // every channel's pressure on this hand
hand.RemoveAllVibrations(); // every channel's vibration on this hand
glove.ReleaseAllChannels(); // everything on the glove, in one writeThe glove keeps applying the last pressure it was sent until it receives a release; nothing on the glove lets go by itself. From 0.9.4 the Unity package releases every channel when your app quits, pauses or disconnects the glove, but nothing can release it if the app crashes or the Bluetooth link drops, and the Python library releases nothing for you. If a glove is left pressing, switch the arm module off.
Release with true as the last argument, a Remove… call or ReleaseAllChannels(). A release that goes through the hand-near check is dropped once the hand has moved away — RemovePinchPressure() is one, so follow it with RemoveAllHaptics().
Whole scripts to copy: Press a finger on your own event and Press the finger that touches an object. Every method is listed under PressureTrackerMain.
Designing haptics
Two modes, and a few settings that change how they feel:
| Mode | Setting | What it changes |
|---|---|---|
| Pressure | Intensity 0.1–1 | How hard it presses: about 15 to 50 kPa |
| Pressure | Speed 0.1–1 | How fast it gets there. At 1, about 0.3 s to the firmest press; at 0.1, ten times slower |
| Vibration | 0.1–5 Hz | Separate pulses you can count. Intensity sets how hard each one presses |
| Vibration | 5–40 Hz | A continuous buzz. Intensity sets how long each pulse lasts |
For shaped rhythms there are also slow pulses and counted pulses — see Channels and parameters.
Tips
- Feel it before you code it. HEXR Tester drives any finger with pressure or vibration, so you can find the values you like first.
- Press only the fingers that touch. Each fingertip and the palm has its own touch point, so HEXR knows which one made contact.
- Use the ramp for character. A slow ramp suits soft or squashy things; a fast one suits hard contact.
- Scale with the grip. Follow Grip on
HexRInteractableHapticspresses harder the tighter the hand closes. - Use vibration for texture and rhythm. A buzz for running water or a rough surface, slow pulses for a heartbeat. The
SpecialHapticspresets are good starting points. - Send on events, not every frame. On Quest and PICO, commands are collected and sent at most every 100 ms, so calling every frame gains nothing. See Latency.
- Check what was sent. The HexR menu’s HAPTICS readout shows the last value sent to each finger, and Show Colliders draws the touch points.