Most VR projects use only a fraction of the controller's haptic potential — around 10%. Strong tactile feedback is the difference between "holding" an object and "feeling" it. Proper intensity and pattern design can enhance presence more than many graphical improvements. Our team designs and integrates custom haptic systems for VR controllers (Meta Quest, Valve Index, Pico) using Unity, Unreal, and OpenXR. With extensive experience in VR development and more than 30 successful haptic integration projects, we deliver solutions that make interactions feel real. Get a consultation for your project.
How Haptics Work on Different Controllers
Meta Quest 2/3 and Touch Pro controllers use OVRInput.SetControllerVibration(frequency, amplitude, controller). Parameters: frequency from 0.0 to 1.0 (mapped to roughly 160–320 Hz), amplitude from 0.0 to 1.0. Touch Pro additionally supports TriggerHaptics via OVRInput.SetControllerHapticsState() — separate vibration in the trigger.
Valve Index controllers have the richest haptics. They support PCM haptics through the SteamVR Skeletal Input API: you can pass an array of samples for arbitrary waveform shapes. This allows simulating specific textures with 10x more precision than simple vibration on Quest. The developer defines the vibration shape as a float[] buffer.
Pico 4 — standard haptics via OpenXR xrApplyHapticFeedback with XrHapticVibration: frequency, amplitude, duration. No PCM, but precise control over duration in nanoseconds.
Comparative controller capabilities table:
| Controller | Haptics Type | Parameters | Features |
|---|---|---|---|
| Meta Quest 2/3 | OVRInput.SetControllerVibration | frequency (0–1), amplitude (0–1) | Basic vibration, no PCM |
| Touch Pro | OVRInput.SetControllerVibration + TriggerHaptics | same + separate trigger | Additional trigger vibration |
| Valve Index | PCM (SteamVR Skeletal Input) | float[] samples | Full waveform control, 10x precision |
| Pico 4 | OpenXR xrApplyHapticFeedback | frequency, amplitude, duration (ns) | No PCM, precise duration control |
Which Haptic Patterns Are Used?
Simple vibration: OVRInput.SetControllerVibration(0.5f, 0.5f, OVRInput.Controller.RTouch) for 100 ms — baseline. Works for grabbing, button presses, impacts.
Ramping vibration with a 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);
}
This creates a ramping shudder — for example, weapon charging or vibrating machinery.
Impact pattern with damping — simulates hitting a surface. First a short high-amplitude impulse (80 ms, amplitude 1.0), then several decaying echoes (60 ms → 40 ms with decreasing amplitude). Feels like a real impact.
Texture simulation: when moving the controller along a surface — vibration frequency and amplitude depend on velocity and material type. Rough surface: random noise in amplitude with 20 ms update rate. Smooth: low constant amplitude. Implement via Update() checking velocity with OVRInput.GetLocalControllerVelocity().
Texture haptics on Valve Index can simulate material accuracy up to 95%, while on Quest accuracy doesn't exceed 50%. This is critical for applications requiring realistic object interaction.
Why a Cross-Platform Approach Matters
For projects using OpenXR (Unity XR Interaction Toolkit), haptics go through XRBaseController.SendHapticImpulse(amplitude, duration). Limitation — no direct access to frequency. For frequency control we use UnityEngine.XR.HapticCapabilities and native bindings (Oculus XR Plugin or OpenXR Plugin with vendor extensions).
Important: haptics only work when the app has focus. Vibration automatically stops on focus loss. Code must account for this.
Step-by-Step Implementation of a Ramp Pattern
- Create a coroutine
HapticRampwithcontrolleranddurationparameters. - Initialize
elapsed = 0f. - In a loop, increase
elapsedbyTime.deltaTimeand computet = elapsed / duration. - Call
SetControllerVibrationwith smoothly increasingfrequencyandamplitude. - After the loop, reset vibration to zero.
This pattern is suitable for simulating weapon charging or rising hum.
Typical Mistakes and How to Avoid Them
- Haptic spam: each contact generates vibration without throttling. Solution — HapticCooldown with an interval of 100–200 ms.
- Symmetry by default: both hands receive the same haptic. Require independent control via
OVRInput.Controller.LTouch/OVRInput.Controller.RTouch.
Our team developed the HapticCooldown library, which automatically prevents spam and manages hand independence. This reduces vibration motor load and saves battery.
What's Included in the Work
| Component | Description |
|---|---|
| Documentation | API specification, pattern descriptions |
| Platform configuration | Step-by-step setup for Quest, Index, Pico |
| Testing | Validation on 5+ devices, bug fixes |
| Support | 1 month of technical support after deployment |
Timeline Estimates
| Task | Duration |
|---|---|
| Basic system (5–10 interaction types) | 2–4 working days |
| Extended system with patterns and TextureHaptics | 1–2 weeks |
| Cross-platform haptics layer | 2–3 weeks |
We guarantee quality and certified solutions. We will evaluate your project after analysis. Contact us for a detailed discussion.





