Controller Haptics Feedback System Programming
Haptics in VR—difference between "holding something" and "feeling what you're holding." Correct intensity and pattern tactile feedback strengthens immersion more than many graphics improvements. Yet typical haptics implementation—single vibration on object touch—uses 10% of controller potential.
What controllers actually support
Meta Quest 2/3 and Touch Pro controllers work via OVRInput.SetControllerVibration(frequency, amplitude, controller). Parameters: frequency 0.0 to 1.0 (maps roughly to 160–320 Hz), amplitude 0.0 to 1.0. Touch Pro additionally supports TriggerHaptics via OVRInput.SetControllerHapticsState()—separate trigger vibration independent of main controller.
Valve Index controllers—richest in haptics. Support PCM haptics via SteamVR Skeletal Input API: can send arbitrary waveform as float[] array. Developer literally describes vibration shape as sample buffer. Allows simulating specific surface textures, impacts with decay, rhythmic patterns.
Pico 4—standard haptics via OpenXR xrApplyHapticFeedback with XrHapticVibration: frequency, amplitude, duration. No PCM, but nanosecond duration control—sufficient for detailed patterns.
Haptic patterns: from simple to detailed
Simple vibration: OVRInput.SetControllerVibration(0.5f, 0.5f, OVRInput.Controller.RTouch) for 100 ms—foundation. Works for general interactions: grab, button press, hit.
Ramping vibration with coroutine:
IEnumerator HapticRamp(OVRInput.Controller controller, float duration) {
float elapsed = 0f;
while (elapsed < duration) {
float t = elapsed / duration;
OVRInput.SetControllerVibration(0.3f + t * 0.5f, t, controller);
elapsed += Time.deltaTime;
yield return null;
}
OVRInput.SetControllerVibration(0f, 0f, controller);
}
Rising tremor effect—e.g., player grabs vibrating mechanism or charges weapon.
Impact pattern with decay—simulates surface impact. Short high-amplitude pulse first (80 ms, amplitude 1.0), then several decreasing echoes (60 ms → 40 ms with decreasing amplitude). Feels like real strike, not just vibration.
Texture simulation: moving controller along surface—vibration where frequency and amplitude depend on movement speed and material type. Rough surface: random noise in amplitude at 20 ms update rate. Smooth: low constant amplitude. Implemented via Update() checking controller velocity via OVRInput.GetLocalControllerVelocity().
OpenXR Haptics for cross-platform projects
For projects via OpenXR (Unity XR Interaction Toolkit), haptics via XRBaseController.SendHapticImpulse(amplitude, duration)—standard path. Limitation: no direct frequency access at OpenXR base API level.
For frequency control via OpenXR—UnityEngine.XR.HapticCapabilities lets check frequency support on specific device, then via native binding (Oculus XR Plugin or OpenXR Plugin with vendor extension) get extended parameters.
Important: haptics only works with active application focus. On focus loss (overlay appeared, pause), vibrations auto-stop by platform. Code must account—don't try resuming haptics after focus loss without explicit trigger.
Typical haptics implementation mistakes
Haptic spam: every object contact generates vibration without throttle. With dense geometry, controller vibrates constantly. Solution: HapticCooldown component with minimum interval between events (100–200 ms).
Symmetric by default: both hands always get identical haptic. In real interactions, left and right hands usually do different things. Independent control via OVRInput.Controller.LTouch / RTouch—basic requirement.
| Task scope | Estimated timeline |
|---|---|
| Basic haptics system for 5–10 interaction types | 2–4 business days |
| Extended system with patterns and TextureHaptics | 1–2 weeks |
| Cross-platform haptics layer (Quest + Index + Pico) | 2–3 weeks |
Cost is determined after analyzing project and interactions requiring haptic feedback.





