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

react-native-haptic-music-player

v2.1.7

Published

A React Native music player with haptic feedback for iOS and Android

Readme

react-native-haptic-music-player

A feature-rich React Native package that delivers a complete music experience with a modern, production-ready UI.

Key highlights:

🎵 Music playback with advanced haptic feedback (not basic vibration) ⏭️ Full player controls: play, pause, skip forward, skip backward 🔁 Repeat & shuffle support 🎚️ Smooth progress tracking and seeking 🎨 Complete, customizable UI (ready to use, no extra setup) 📱 Works seamlessly on both Android and iOS ⚙️ Built to be fully compatible with React Native New Architecture 🧩 Clean, scalable, and performance-optimized implementation

Installation

NPM

npm install react-native-haptic-music-player

Yarn

yarn add react-native-haptic-music-player

Expo (Managed Workflow)

npx expo install react-native-haptic-music-player

Download from GitHub

# Clone the repository
git clone https://github.com/yourusername/react-native-haptic-music-player.git

# Navigate to the project
cd react-native-haptic-music-player

# Install dependencies
npm install

# Build the package
npm run prepare

iOS

cd ios && pod install

Android

Ensure your android/app/src/main/AndroidManifest.xml includes:

<uses-permission android:name="android.permission.VIBRATE" />

Features

  • 🎵 Full-featured audio playback with complete UI
  • 📳 Advanced haptic feedback (HapticGenerator on Android 12+, CoreHaptics on iOS 13+)
  • 🎯 Audio-coupled haptic feedback
  • 🎨 Complete customizable UI component
  • ⏭️ Skip forward/backward controls (10-second intervals)
  • 🔁 Repeat and shuffle modes
  • 🎚️ Interactive progress bar with seeking
  • 📱 iOS and Android support
  • ⚡ TypeScript support
  • 🔄 Real-time playback events
  • 🌙 Light/Dark theme support
  • 🔄 Automatic fallback for older devices
  • 🎨 Custom button icons (text/emoji or images)
  • 🎯 Fully customizable colors and styles
  • 📦 Compact mode for smaller screens

Usage

Complete UI Component (Recommended)

import React from 'react';
import { View } from 'react-native';
import { HapticMusicPlayerUI } from 'react-native-haptic-music-player';

const tracks = [
  {
    id: '1',
    title: 'Song Title',
    artist: 'Artist Name',
    uri: 'https://example.com/song1.mp3',
    artwork: 'https://example.com/artwork1.jpg',
    ahapUri: 'https://example.com/haptics/song1.ahap', // Optional AHAP file for iOS
  },
  {
    id: '2',
    title: 'Another Song',
    artist: 'Another Artist',
    uri: 'https://example.com/song2.mp3',
    artwork: 'https://example.com/artwork2.jpg',
  },
];

export default function App() {
  return (
    <View style={{ flex: 1, justifyContent: 'center', padding: 20 }}>
      <HapticMusicPlayerUI
        tracks={tracks}
        theme="dark" // or "light"
        enableHaptics={true}
        hapticIntensity={0.8}
        showSeekButtons={true}
        seekInterval={10}
        compactMode={false}
        customColors={{
          primary: '#FF6B6B',
          accent: '#4ECDC4',
        }}
        customButtonIcons={{
          play: { type: 'text', source: '▶️' },
          pause: { type: 'text', source: '⏸️' },
          // Or use images:
          // play: { type: 'image', source: require('./assets/play.png') },
        }}
        onTrackChange={(track, index) => {
          console.log('Track changed:', track.title);
        }}
      />
    </View>
  );
}

UI Component Props

interface HapticMusicPlayerUIProps {
  tracks: Track[];                    // Array of tracks to play
  initialTrackIndex?: number;         // Starting track index (default: 0)
  onTrackChange?: (track, index) => void; // Track change callback
  style?: ViewStyle;                  // Custom container style
  theme?: 'light' | 'dark';          // Theme (default: 'dark')
  enableHaptics?: boolean;            // Enable haptic feedback (default: true)
  hapticIntensity?: number;           // Haptic intensity 0-1 (default: 0.8)
  showSeekButtons?: boolean;          // Show seek forward/backward buttons (default: true)
  seekInterval?: number;              // Seek interval in seconds (default: 10)
  showArtwork?: boolean;              // Show artwork (default: true)
  compactMode?: boolean;              // Compact UI mode (default: false)
  animationDuration?: number;         // Animation duration in ms (default: 200)
  customColors?: {                    // Custom color scheme
    primary?: string;
    background?: string;
    text?: string;
    accent?: string;
  };
  customButtonIcons?: {               // Custom button icons
    play?: ButtonIcon;
    pause?: ButtonIcon;
    next?: ButtonIcon;
    previous?: ButtonIcon;
    shuffle?: ButtonIcon;
    repeat?: ButtonIcon;
    seekForward?: ButtonIcon;
    seekBackward?: ButtonIcon;
    haptic?: ButtonIcon;
  };
  seekButtonStyles?: {                // Custom seek button styles
    container?: ViewStyle;
    button?: ViewStyle;
    icon?: TextStyle;
    text?: TextStyle;
  };
}

interface ButtonIcon {
  type: 'text' | 'image';            // Icon type
  source: string | ImageSourcePropType; // Text/emoji or image source
}

interface Track {
  id: string;
  title: string;
  artist: string;
  uri: string;
  artwork?: string;                   // Optional artwork URL
  duration?: number;                  // Optional duration in seconds
  ahapUri?: string;                   // Optional AHAP file URL for iOS haptic feedback
}

Custom Button Icons

// Text/Emoji Icons
customButtonIcons={{
  play: { type: 'text', source: '▶️' },
  pause: { type: 'text', source: '⏸️' },
  next: { type: 'text', source: '⏭️' },
  previous: { type: 'text', source: '⏮️' },
}}

// Local Images
customButtonIcons={{
  play: { type: 'image', source: require('./assets/play-icon.png') },
  pause: { type: 'image', source: require('./assets/pause-icon.png') },
}}

// Remote Images
customButtonIcons={{
  play: { type: 'image', source: { uri: 'https://example.com/play.png' } },
  pause: { type: 'image', source: { uri: 'https://example.com/pause.png' } },
}}

Seek Button Customization

<HapticMusicPlayerUI
  showSeekButtons={true}
  seekInterval={15} // 15-second intervals
  seekButtonStyles={{
    button: {
      backgroundColor: '#2a2a2a',
      paddingVertical: 15,
      borderRadius: 20,
    },
    icon: {
      fontSize: 24,
      color: '#FF6B6B',
    },
    text: {
      color: '#4ECDC4',
      fontWeight: 'bold',
    },
  }}
  customButtonIcons={{
    seekForward: { type: 'image', source: require('./assets/forward.png') },
    seekBackward: { type: 'image', source: require('./assets/backward.png') },
  }}
/>

Basic API Usage

import HapticMusicPlayer, {
  HapticFeedbackType,
  PlaybackState,
} from 'react-native-haptic-music-player';

// Load a track with optional AHAP file
await HapticMusicPlayer.loadTrack('https://example.com/song.mp3', 'https://example.com/haptics/song.ahap');

// Load only AHAP file (for existing loaded track)
await HapticMusicPlayer.loadAhapFile('https://example.com/haptics/song.ahap');

// Play with synchronized AHAP haptics (iOS only)
await HapticMusicPlayer.playWithAhap();

// Regular play (without AHAP)
await HapticMusicPlayer.play();

// Pause
await HapticMusicPlayer.pause();

// Stop
await HapticMusicPlayer.stop();

// Seek to position (in seconds)
await HapticMusicPlayer.seekTo(30);

// Seek forward/backward (10 seconds by default)
await HapticMusicPlayer.seekForward(10);
await HapticMusicPlayer.seekBackward(10);

// Set volume (0 to 1)
await HapticMusicPlayer.setVolume(0.8);

Haptic Feedback

// Trigger basic haptic feedback
await HapticMusicPlayer.triggerHaptic(HapticFeedbackType.MEDIUM);

// Available haptic types:
// LIGHT, MEDIUM, HEAVY, RIGID, SOFT, SUCCESS, WARNING, ERROR, SELECTION

// Create custom haptic pattern
const pattern = [
  { intensity: 0.8, duration: 100, delay: 0 },
  { intensity: 0.5, duration: 50, delay: 100 },
  { intensity: 1.0, duration: 200, delay: 50 },
];

await HapticMusicPlayer.triggerHapticPattern(pattern);

AHAP File Support (iOS)

// Load track with AHAP file for synchronized haptics
const trackWithHaptics = {
  id: '1',
  title: 'Song with Haptics',
  artist: 'Artist',
  uri: 'https://example.com/song.mp3',
  ahapUri: 'https://example.com/haptics/song.ahap', // AHAP file URL
};

// The UI component will automatically use AHAP playback when available
<HapticMusicPlayerUI
  tracks={[trackWithHaptics]}
  enableHaptics={true}
/>

// Manual AHAP control
await HapticMusicPlayer.loadTrack(trackWithHaptics.uri, trackWithHaptics.ahapUri);
await HapticMusicPlayer.playWithAhap(); // Synchronized audio + haptic playback

Audio-Coupled Haptics

// Enable audio-synchronized haptics (uses HapticGenerator on Android 12+)
await HapticMusicPlayer.enableBeatHaptics(true);

// Adjust haptic intensity (0 to 1)
await HapticMusicPlayer.setHapticIntensity(0.8);

// Listen to beat events (when available)
const unsubscribe = HapticMusicPlayer.onBeatDetected((beat) => {
  console.log('Beat detected:', beat.timestamp, beat.intensity);
});

// Clean up
unsubscribe();

Event Listeners

// Playback state changes
const unsubscribeState = HapticMusicPlayer.onPlaybackStateChange((state) => {
  console.log('Playback state:', state);
  // states: 'playing', 'paused', 'stopped', 'loading', 'error'
});

// Progress updates
const unsubscribeProgress = HapticMusicPlayer.onProgressUpdate((progress) => {
  console.log('Current time:', progress.currentTime);
  console.log('Duration:', progress.duration);
});

// Errors
const unsubscribeError = HapticMusicPlayer.onError((error) => {
  console.error('Player error:', error.code, error.message);
});

// Remove all listeners
HapticMusicPlayer.removeAllListeners();

Configuration

// Configure player settings
await HapticMusicPlayer.configure({
  enableHaptics: true,
  hapticIntensity: 0.8,
});

Get Playback Info

const info = await HapticMusicPlayer.getPlaybackInfo();
console.log(info.currentTime);
console.log(info.duration);
console.log(info.state);
console.log(info.isLoading);

Complete Custom Example

import React, { useEffect, useState } from 'react';
import { View, Button, Text } from 'react-native';
import HapticMusicPlayer, {
  HapticFeedbackType,
  PlaybackState,
} from 'react-native-haptic-music-player';

export default function CustomMusicPlayer() {
  const [state, setState] = useState<PlaybackState>(PlaybackState.STOPPED);
  const [progress, setProgress] = useState({ currentTime: 0, duration: 0 });

  useEffect(() => {
    HapticMusicPlayer.configure({
      enableHaptics: true,
      hapticIntensity: 0.8,
    });

    const unsubscribeState = HapticMusicPlayer.onPlaybackStateChange(setState);
    const unsubscribeProgress = HapticMusicPlayer.onProgressUpdate(setProgress);
    const unsubscribeBeat = HapticMusicPlayer.onBeatDetected((beat) => {
      console.log('Beat!', beat.intensity);
    });

    return () => {
      unsubscribeState();
      unsubscribeProgress();
      unsubscribeBeat();
    };
  }, []);

  const loadAndPlay = async () => {
    await HapticMusicPlayer.loadTrack('https://example.com/song.mp3');
    await HapticMusicPlayer.enableBeatHaptics(true);
    await HapticMusicPlayer.play();
  };

  return (
    <View>
      <Text>State: {state}</Text>
      <Text>
        Time: {Math.floor(progress.currentTime)}s / {Math.floor(progress.duration)}s
      </Text>
      <Button title="Load & Play" onPress={loadAndPlay} />
      <Button title="Pause" onPress={() => HapticMusicPlayer.pause()} />
      <Button
        title="Haptic Feedback"
        onPress={() => HapticMusicPlayer.triggerHaptic(HapticFeedbackType.HEAVY)}
      />
    </View>
  );
}

API Reference

Methods

| Method | Parameters | Returns | Description | |--------|-----------|---------|-------------| | loadTrack | uri: string, ahapUri?: string | Promise<void> | Load audio from URI with optional AHAP file | | loadAhapFile | ahapUri: string | Promise<void> | Load AHAP file for current track | | play | - | Promise<void> | Start playback | | playWithAhap | - | Promise<void> | Start playback with AHAP haptics (iOS) | | pause | - | Promise<void> | Pause playback | | stop | - | Promise<void> | Stop playback | | seekTo | position: number | Promise<void> | Seek to position (seconds) | | seekForward | seconds?: number | Promise<void> | Seek forward by seconds (default: 10) | | seekBackward | seconds?: number | Promise<void> | Seek backward by seconds (default: 10) | | setVolume | volume: number | Promise<void> | Set volume (0-1) | | getCurrentPosition | - | Promise<number> | Get current position | | getDuration | - | Promise<number> | Get total duration | | getPlaybackInfo | - | Promise<PlaybackInfo> | Get full playback info | | triggerHaptic | type: HapticFeedbackType | Promise<void> | Trigger haptic feedback | | triggerHapticPattern | pattern: HapticPattern[] | Promise<void> | Play custom pattern | | enableBeatHaptics | enabled: boolean | Promise<void> | Enable audio-coupled haptics | | setHapticIntensity | intensity: number | Promise<void> | Set haptic intensity (0-1) | | configure | config: HapticMusicPlayerConfig | Promise<void> | Configure player |

Event Listeners

  • onPlaybackStateChange(callback) - Playback state changes
  • onProgressUpdate(callback) - Progress updates (every 100ms)
  • onBeatDetected(callback) - Beat detection events
  • onError(callback) - Error events

Native Implementation Notes

This package requires native implementation for both iOS and Android:

iOS Implementation

  • Uses AVAudioEngine for audio playback
  • Uses CoreHaptics API for haptic feedback (iOS 13+)
  • AHAP file support: Load and play Apple Haptic and Audio Pattern files for synchronized haptic feedback
  • Automatic download and parsing of AHAP files from URLs
  • Fallback to basic vibration for older devices

Android Implementation

  • Uses MediaPlayer for audio playback
  • Uses HapticGenerator for audio-coupled haptics (Android 12+/API 31+)
  • Falls back to VibrationEffect API for older devices (API 26+)
  • Basic vibration for legacy devices

License

MIT

Contributing

Contributions are welcome! Please open an issue or submit a pull request.