npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

expo-better-haptics

v1.0.2

Published

expo-haptics but better (support for custom haptics and patterns)

Readme

Expo Better Haptics

A drop-in replacement for expo-haptics offering fine-grained control over haptic feedback on iOS and Android devices.

Features

  • 🤖 Better Android support using dedicated Haptics APIs
  • 💿 Drop-in replacement for expo-haptics
  • Continuous or transient impacts with multiple intensity levels
  • 🎵 Complex haptic patterns with precise timing and sequencing
  • 💥 Pre-built notification patterns (success, warning, error)
  • 📱 Cross-platform support for both iOS and Android
  • 🎼 AHAP file support for playing Apple Haptic Audio Patterns on iOS

Requirements

  • Expo SDK 52 or newer
  • iOS 13+ for CoreHaptics API (iPhone 8 or newer)
  • Android 8+ (API level 26) for enhanced vibration effects
  • Android 11+ (API level 30) for Haptic Compositions and Primitives

Installation

npx expo install expo-better-haptics

Usage

Basic Usage

import * as Haptics from 'expo-better-haptics'

// Play impact haptics
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium)
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy)
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Soft)
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Rigid)

// Play notification haptics
await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success)
await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning)
await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error)

// Play selection feedback
await Haptics.selectionAsync()

// Play a continuous vibration
await Haptics.vibrateAsync({
    intensity: 0.8, // 0-1, defaults to 0.8
    sharpness: 0.5, // 0-1, defaults to 0.5
    duration: 0.5, // in seconds, defaults to 0.5
})

Advanced Usage

Custom Transient Haptics

import * as Haptics from 'expo-better-haptics'

// Play a custom transient haptic with specific intensity and sharpness
await Haptics.playTransientAsync(0.7, 0.9)

Custom Continuous Haptics

import * as Haptics from 'expo-better-haptics'

// Play a continuous haptic with specific intensity, sharpness, and duration
await Haptics.playContinuousAsync(0.6, 0.3, 1.2) // 1.2 seconds

Custom Haptic Patterns

import * as Haptics from 'expo-better-haptics'
import { HapticEvent, HapticEventType, HapticEventParameterType } from 'expo-better-haptics'

// Create a custom pattern using helper methods
const customPattern = [
    // First tap at time 0
    Haptics.createTransientEvent({
        intensity: 0.8,
        sharpness: 0.5,
        time: 0,
    }),
    // Second tap after a short pause
    Haptics.createTransientEvent({
        intensity: 0.5,
        sharpness: 0.3,
        time: 0.15,
    }),
    // Longer continuous effect after another pause
    Haptics.createContinuousEvent({
        intensity: 1.0,
        sharpness: 0.7,
        time: 0.4,
        duration: 0.3,
    }),
]

// Play the custom pattern
await Haptics.playPatternAsync(customPattern)

// Or create patterns manually with full control
const manualPattern = [
    {
        type: HapticEventType.Transient,
        time: 0,
        parameters: [
            { id: HapticEventParameterType.Intensity, value: 0.8 },
            { id: HapticEventParameterType.Sharpness, value: 0.5 },
        ],
    },
    {
        type: HapticEventType.Continuous,
        time: 0.2,
        duration: 0.5,
        parameters: [
            { id: HapticEventParameterType.Intensity, value: 0.6 },
            { id: HapticEventParameterType.Sharpness, value: 0.3 },
        ],
    },
]

await Haptics.playPatternAsync(manualPattern)

Playing AHAP Files (iOS Only)

AHAP (Apple Haptic Audio Pattern) files allow you to create rich haptic experiences with synchronized audio on iOS:

import * as Haptics from 'expo-better-haptics'

// Play an AHAP pattern directly from JSON
const ahapPattern = {
    Pattern: [
        {
            Event: {
                Time: 0.0,
                EventType: "HapticContinuous",
                EventDuration: 0.6,
                EventParameters: [
                    { ParameterID: "HapticIntensity", ParameterValue: 1.0 },
                    { ParameterID: "HapticSharpness", ParameterValue: 0.5 },
                ],
            },
        },
        {
            Event: {
                Time: 0.601,
                EventType: "HapticTransient",
                EventParameters: [
                    { ParameterID: "HapticIntensity", ParameterValue: 1.0 },
                    { ParameterID: "HapticSharpness", ParameterValue: 0.7 },
                ],
            },
        },
    ],
}

await Haptics.playAHAPAsync(ahapPattern)

// Or load from an imported JSON file
import heartbeatPattern from './haptics/heartbeat.json'
await Haptics.playAHAPAsync(heartbeatPattern)

// You can also pass AHAP as a JSON string
const ahapString = JSON.stringify(ahapPattern)
await Haptics.playAHAPAsync(ahapString)

AHAP files support parameter curves for dynamic control:

const dynamicPattern = {
    Pattern: [
        {
            Event: {
                Time: 0.0,
                EventType: "HapticContinuous",
                EventDuration: 1.0,
                EventParameters: [
                    { ParameterID: "HapticIntensity", ParameterValue: 0.5 },
                    { ParameterID: "HapticSharpness", ParameterValue: 0.5 },
                ],
            },
        },
        {
            ParameterCurve: {
                ParameterID: "HapticIntensityControl",
                Time: 0.0,
                ParameterCurveControlPoints: [
                    { Time: 0, ParameterValue: 0.2 },
                    { Time: 0.5, ParameterValue: 1.0 },
                    { Time: 1.0, ParameterValue: 0.2 },
                ],
            },
        },
    ],
}

await Haptics.playAHAPAsync(dynamicPattern)

Note: AHAP patterns are only supported on iOS 13+ devices. On Android, the playAHAPAsync method will fail silently.

Checking Support and Managing Engine

import * as Haptics from 'expo-better-haptics'

// Check if haptics are supported on the device
const isSupported = Haptics.isSupported

if (isSupported) {
    // Explicitly initialize the haptics engine (optional - will auto-initialize when needed)
    await Haptics.initialize()

    // Do haptic operations...

    // When done, you can explicitly stop the engine (optional)
    await Haptics.stop()
}

API Reference

Constants

  • isSupported - Boolean indicating if haptics are supported on the device

Methods

Initialization

  • initialize() - Explicitly initializes the haptic engine (auto-initialized when needed)
  • start() - Explicitly starts the haptic engine
  • stop() - Explicitly stops the haptic engine

Standard Haptics

  • impactAsync(style?) - Plays an impact haptic with the specified style
    • style (ImpactFeedbackStyle) - Style of impact, defaults to Medium
  • notificationAsync(type?) - Plays a notification haptic with the specified type
    • type (NotificationFeedbackType) - Type of notification, defaults to Success
  • selectionAsync() - Plays a selection haptic

Advanced Haptics

  • vibrateAsync(options?) - Plays a customizable continuous vibration
    • options.intensity (number, 0-1) - Intensity of the vibration, defaults to 0.8
    • options.sharpness (number, 0-1) - Sharpness of the vibration, defaults to 0.5
    • options.duration (number) - Duration in seconds, defaults to 0.5
  • playTransientAsync(intensity, sharpness) - Plays a transient haptic with customized parameters
    • intensity (number, 0-1) - Required. Intensity of the haptic effect
    • sharpness (number, 0-1) - Required. Sharpness of the haptic effect
  • playContinuousAsync(intensity, sharpness, duration) - Plays a continuous haptic with customized parameters
    • intensity (number, 0-1) - Required. Intensity of the haptic effect
    • sharpness (number, 0-1) - Required. Sharpness of the haptic effect
    • duration (number) - Required. Duration in seconds
  • playPatternAsync(events) - Plays a custom haptic pattern defined by an array of haptic events
  • playAHAPAsync(ahapData) - Plays an AHAP (Apple Haptic Audio Pattern) on iOS devices
    • ahapData (Object or string) - AHAP pattern data as a JavaScript object or JSON string

Helper Methods

  • createTransientEvent(options) - Creates a transient haptic event object
    • options.intensity (number, 0-1) - Intensity of the haptic effect
    • options.sharpness (number, 0-1) - Sharpness of the haptic effect
    • options.time (number) - Time offset in seconds for when this event should occur
  • createContinuousEvent(options) - Creates a continuous haptic event object
    • options.intensity (number, 0-1) - Intensity of the haptic effect
    • options.sharpness (number, 0-1) - Sharpness of the haptic effect
    • options.time (number) - Time offset in seconds for when this event should occur
    • options.duration (number) - Duration of the continuous effect in seconds

Enums

  • ImpactFeedbackStyle - Enum for impact feedback styles

    • Light - A collision between small, light user interface elements
    • Medium - A collision between moderately sized user interface elements
    • Heavy - A collision between large, heavy user interface elements
    • Rigid - A collision between user interface elements that are rigid
    • Soft - A collision between user interface elements that are soft
  • NotificationFeedbackType - Enum for notification feedback types

    • Success - A notification feedback type indicating that a task has completed successfully
    • Warning - A notification feedback type indicating that a task has produced a warning
    • Error - A notification feedback type indicating that a task has failed

Types

  • HapticEventType - Enum for haptic event types
  • HapticEventParameterType - Enum for haptic event parameter types
  • HapticEvent - Interface for haptic events
  • HapticParameter - Interface for haptic parameters

Platform-Specific Implementation

iOS

On iOS, this module uses:

  • CoreHaptics API for fine-grained haptic control (iOS 13+)
  • UIImpactFeedbackGenerator for impact feedback
  • UINotificationFeedbackGenerator for notification feedback
  • UISelectionFeedbackGenerator for selection feedback

Android

On Android, this module uses:

  • Haptic Compositions API with primitives for rich haptic feedback (Android 11+)
  • VibrationEffect API for amplitude control and waveforms (Android 8+)
  • View.performHapticFeedback for simple haptic feedback
  • Intelligent fallbacks for older devices

Feature Compatibility by Platform

| Feature | iOS | Android 11+ | Android 8-10 | Android <8 | | ------------------------- | --- | ----------- | ------------ | ---------- | | Impact feedback | ✓ | ✓ | ✓ | Limited | | Notification patterns | ✓ | ✓ | ✓ | Limited | | Selection feedback | ✓ | ✓ | ✓ | ✓ | | Intensity control | ✓ | ✓ | ✓ | ✗ | | Sharpness control | ✓ | Limited | ✗ | ✗ | | Custom patterns | ✓ | ✓ | Limited | ✗ | | Continuous effects | ✓ | ✓ | ✓ | Limited | | Dynamic parameter control | ✓ | Limited | ✗ | ✗ |

Compared to Expo Haptics

expo-better-haptics offers more capabilities over the standard expo-haptics:

  1. Fine-grained control: Adjust intensity and sharpness parameters on both iOS and Android
  2. Continuous haptics: Standard haptics only offers preset impacts
  3. Complex patterns: Create sequences of haptic events with precise timing
  4. Longer effects: Create sustained haptic experiences of any duration
  5. Cross-platform: Full Android support with proper haptic implementations

API Compatibility with expo-haptics

This library is fully compatible with expo-haptics, making migration easy:

// Replace this:
import * as Haptics from 'expo-haptics'

// With this:
import * as Haptics from 'expo-better-haptics'

All existing code will continue to work:

// Standard expo-haptics API
await Haptics.impactAsync() // Default medium impact
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light)
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium)
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Heavy)
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Rigid)
await Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Soft)

await Haptics.notificationAsync() // Default success notification
await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success)
await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Warning)
await Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error)

await Haptics.selectionAsync()

In addition, you'll gain access to more powerful haptic features:

// Extended functionality
await Haptics.vibrateAsync({ intensity: 0.8, sharpness: 0.6, duration: 0.5 })
await Haptics.playTransientAsync(0.7, 0.9)
await Haptics.playContinuousAsync(0.6, 0.3, 1.2)
await Haptics.playPatternAsync(customPattern)

Example App

The module includes an example app that demonstrates all the haptic capabilities:

cd example
npx expo run:android --device  # For Android testing
npx expo run:ios --device      # For iOS testing

Note: Must be run on a physical device to feel the haptic feedback.

Android Implementation Details

The Android implementation uses a tiered approach to provide the best haptic experience based on the device's capabilities:

  1. Android 11+ (API 30+): Uses Haptic Composition API with primitives like TICK, CLICK, THUD, QUICK_RISE, etc.
  2. Android 8-10 (API 26-29): Uses VibrationEffect API with amplitude control and waveform patterns
  3. Older Android versions: Falls back to basic vibration patterns

The implementation intelligently maps iOS haptic concepts to their Android equivalents:

  • Intensity: Maps to vibration amplitude (0-255)
  • Sharpness: Maps to different primitive types or vibration duration
  • Patterns: Converted to appropriate waveform patterns

Limitations

  • iOS CoreHaptics requires iOS 13+ and iPhone 8 or newer
  • Android haptic primitives require Android 11+ (API level 30)
  • Must be run on a physical device to feel the haptics
  • Haptics may not work if device is in silent mode, battery saver mode, or has haptics disabled
  • Some Android devices have limited haptic capability hardware

Authors

This library was co-authored by:

  • Carter (@carter-0)
  • Claude Code

License

MIT