react-native-haptic-music-player
v2.1.7
Published
A React Native music player with haptic feedback for iOS and Android
Maintainers
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-playerYarn
yarn add react-native-haptic-music-playerExpo (Managed Workflow)
npx expo install react-native-haptic-music-playerDownload 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 prepareiOS
cd ios && pod installAndroid
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 playbackAudio-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 changesonProgressUpdate(callback)- Progress updates (every 100ms)onBeatDetected(callback)- Beat detection eventsonError(callback)- Error events
Native Implementation Notes
This package requires native implementation for both iOS and Android:
iOS Implementation
- Uses
AVAudioEnginefor audio playback - Uses
CoreHapticsAPI 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
MediaPlayerfor audio playback - Uses
HapticGeneratorfor audio-coupled haptics (Android 12+/API 31+) - Falls back to
VibrationEffectAPI 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.
