UX Foundation for Haptic Framework

All Android framework improvements built around haptics are driven by a set of UX principles that are evolving at an equal rate. The current principles involve replacing buzzy vibration with clear haptics, and exploring rich haptics.

UX Principles

Figure 1. Current principles

APIs by Year

Figure 2. Haptics APIs by year

Buzzy vibration

Dating back to pagers and feature phones, low-quality but power-efficient ERM buzzer-based vibrations have been used as a substitute for auditory ringing in silent mode. The legacy hardware components that produce loud and unpleasant audible noises can harm haptic UX by delivering low-quality impressions (for example, a cheap, broken phone).

Clear haptics

Clear haptics support the sensation of discrete state changes (for example, binary changes during the power on/off process). Due to the nature of the discrete affordance, clear haptics are generated as a single entity (for example, one haptic effect per one input event).

Android aims to deliver clear haptics with strong, yet sharp sensations rather than sensations that are buzzy or mushy.

Predefined haptic constants that are created to support clear haptics include the following.

In HapticFeedbackConstants:

  • CLOCK_TICK
  • CONFIRM
  • CONTEXT_CLICK
  • GESTURE_END
  • GESTURE_START
  • KEYBOARD_PRESS
  • KEYBOARD_RELEASE
  • KEYBOARD_TAP
  • LONG_PRESS
  • REJECT
  • TEXT_HANDLE_MOVE
  • VIRTUAL_KEY
  • VIRTUAL_KEY_RELEASE

In VibrationEffect:

  • EFFECT_CLICK
  • EFFECT_DOUBLE_CLICK
  • EFFECT_HEAVY_CLICK
  • EFFECT_TICK

Building common knowledge between device manufacturers and developers is key to raising the overall quality of haptics in the Android ecosystem. Use the basic checklist, hardware assessment, and CDD. to learn more about haptic implementation.

Press and Release

Figure 3. Pressing and releasing.

Rich haptics

Rich haptics is a growing haptics category that goes beyond single impulse-based effects. Android aims to support rich haptics with high composability and adjustability with a fine level of granularity. The following use cases are supported in Android 11 or lower.

Rich Haptics

Figure 4. Rich haptics with sliding texture

Dragging and Swiping

Figure 5. Dragging and swiping

Use case 1: Sliding texture

If a haptic effect is repeated while the finger slides over a touch surface (for example, dragging, swiping, exploring the surface with phantom haptic texture), the repeating haptic effects are preferably crisp and subtle.

If the individual effect is buzzy rather than crisp, then the intervals between the repetitions are likely to be wiped out. The outcome is one long buzz, rather than multiple discrete signals.

If the amplitude isn't subtle enough, then the perceived haptic energy builds up through the repetition, resulting in overwhelmingly strong haptics at the end of the repetition.

Implementing a simple surface haptic texture for swipe and drag gestures

Use CLOCK_TICK and TEXT_HANDLE_MOVE in HapticFeedbackConstants. These constants predefine characteristics of repetition and amplitude.

Creating your own effect

To make your own effect, compose a design by stringing together sequences of PRIMITIVE_CLICK and PRIMITIVE_TICK in VibrationEffect.Composition. You can adjust the characteristics of the repetition and amplitude scale using addPrimitive(int primitiveID, float scale, int delay). Support relies on the CAP_COMPOSE_EFFECTS capability of the Vibrator HAL Interface.

Use case 2: Long vibration with ease-in effect

Long vibration is a smooth amplitude vibration that transitions from 0 to the target amplitude. Long vibration can generate easily perceivable attentional haptics. However, a sudden long vibration can startle users in a quiet environment, and often produces audible buzzing noises. To generate a more pleasant long vibration, apply the ease-in effect at the beginning of the long vibration. This produces a smooth amplitude transition that builds toward the target amplitude.

Applying the ease-in effect

  1. Check hardware capabilities of amplitude control with android.os.Vibrator.hasAmplitudeControl().

    • The result has to be true to produce ease-in effect with varying amplitude.
  2. Use VibrationEffect.createWaveform(timings[], amplitudes[], int repeat).

  3. Adjust the series of timings[] and amplitudes[] to generate the ease-in curve, as shown in Figure 6.

Long Vibration

Figure 6. Long vibration ease-in curve

Use case 3: Audio-coupled haptics

Audio-coupled haptics are haptic patterns coupled with the rhythm of the audio to get the user's attention.

Audio-coupled haptics: Benefits

To implement audio-coupled haptics, combine clear haptics with long vibrations. The strong but short haptic sensations from clear haptics deliver discrete rhythmic patterns. When combined with the high levels of stimuli that long vibration provides, this does a great job of getting a user's attention.

It's important to consider the sensation rhythmic patterns. If there's no sense of rhythm, the user perceives the haptic sensations as random buzzes, and tends to ignore them.

Audio Couple

Figure 7. Audio couple haptics example

Audio-coupled haptics: Tips for implementing

Implementing audio-coupled haptics requires a basic understanding of content playback of both audio and haptic channels. Keep the following things in mind.

  • Use the MediaPlayer or SoundPool classes.

    • Assets in OGG format with a special metadata key (ANDROID_HAPTIC followed by a number of haptic channels) indicate the presence of haptics data and playback with MediaPlayer and SoundPool.
  • Indicate support of haptics and audio playback in audio_policy_configuration.xml.

    • Use an output profile with haptics channel AUDIO_CHANNEL_OUT_HAPTIC_A|B.
    • For an output stream with haptic channels, remember that haptic channels are presented as extra channels in the data.

    Example

    If the channel mask for the output stream looks like this:

    AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A

    Then every sample should look like this:

    AUDIO_LEFT_CHANNEL,AUDIO_RIGHT_CHANNEL,HAPTIC_CHANNEL_A

  • Change AudioAttributes.Builder( ).setHapticChannelsMuted(boolean muted)

    to false to play the haptic channel.

    • By default, haptic channels are muted (true).
    • Use cases include ringtones and UI sounds with synchronous haptics and feedback.
  • The Vibrator HAL must implement external control support.

Audio Coupled Haptics

Figure 8. Implementing audio-coupled haptics

Audio-coupled haptics: Haptic Generator

The HapticGenerator is an Audio Effect introduced in Android 12 that can generate haptic data from an audio channel and play it in real time as audio-coupled haptics. The effect is applied to the AudioTrack as described in Figure 9.

Haptic Generator architecture

Figure 9. Haptic Generator architecture

To make sure your haptic generator algorithm generates high-quality haptics, tune the generation algorithm to the device vibrator motor by adjusting the parameters that configure the chain of filters it applies to audio waveforms. This section describes these parameters in detail, and explains how to tune them to your hardware specification.

  1. Resonant frequency for band-pass filter

    The vibrator resonant frequency is the frequency at which a haptic actuator has maximum output. This parameter adjusts an anti-resonator to partially flatten the response transfer function, in order to get a wider bandwidth. The Android framework automatically links this value to the output of the Vibrator HAL method IVibrator.getResonantFrequency.

    The default value for this parameter is 150Hz. This can be modified in the code here.

  2. Normalization power for slow envelope

    This parameter determines the exponent in the partial normalization (automatic gain control). Its default value is -0.8, which means that 80% of the dynamic range variation is removed by this gain control step. This can be modified in the code here.

  3. Q factor for band-stop filter

    The vibrator quality factor (Q factor) is determined by two parameters:

    • The Zero Q, the quality factor of the zeros in the band-stop filter that partially cancels the resonance.

    • The Pole Q, the quality factor of the poles in the band-stop filter.

    The ratio of those two values limits the suppression of resonance in order to boost lower frequencies and broaden the algorithm response. For example, the default values of 8 for the Zero Q and 4 for the Pole Q produce a ratio of 2, limiting resonance suppression by a factor of 2 (6 dB). The Android framework links both values to the output of the Vibrator HAL method IVibrator.getQFactor.

    If the default values don’t account for the dampening of the motor strength in your device, we recommend modifying both values at the same time, and either increasing both or decreasing both. The ratio of Zero Q to Pole Q should be greater than 1. This can be modified in the code here.

  4. Corner frequency for distortion

    The corner frequency is applied by a low-pass filter that suppresses low-level vibration and enhances higher levels using a cubic distortion. It defaults to 300Hz. This can be modified in the code here.

  5. Input gain and cube threshold for distortion

    These parameters are used by a nonlinear distortion filter applied to the input waveform that dampens the lower frequency signals’ amplitude and increases the higher frequency ones.

    • The default value for the input gain factor is 0.3.
    • The default value for the cube threshold is 0.1.

    We recommend modifying both values together. They can be found in the code here.

    For more information about the function applied by this filter, refer to the implementation available here. To learn more about how these two parameters influence the output, we recommend plotting the frequency responses of the filters and observing how the frequency responses change with different parameter values.

  6. Output gain for distortion

    This parameter controls the final vibration amplitude. It’s a final gain applied after a soft limiter that limits vibration amplitudes to less than 1. Its default value is 1.5, and it can be modified in the code here. If the vibration is too subtle, increase the value. If you can hear the actuator hardware rattling, decrease the value.