🚀 Cross-platform audio playback for React Native — Play sound clips on iOS and Android with full TypeScript support and modern React Native architecture compatibility.
- 🎯 Cross-platform: Works on iOS and Android
- 📱 Modern Architecture: Full support for React Native's New Architecture (TurboModules)
- 🔤 TypeScript: Complete TypeScript definitions included
- 🎛️ Rich Controls: Play, pause, stop, seek, volume, pan, and looping
- 📁 Flexible Loading: Load from app bundle, local files, or network URLs
- 🔄 Multiple Players: Play multiple sounds simultaneously
- ⚡ Optimized: Minimal latency with preloading support
- 🛡️ Reliable: Battle-tested in production apps
📝 Note: This library focuses on audio clips playback, not streaming. For streaming audio, consider react-native-video or other dedicated streaming solutions.
iOS Implementation: Uses AVAudioPlayer for optimal performance and compatibility.
Android Implementation: Uses MediaPlayer with proper audio focus handling.
| Feature | iOS | Android |
|---|---|---|
| Loading | ||
| Load from app bundle | ✅ | ✅ |
| Load from local files | ✅ | ✅ |
| Load from network URLs | ✅ | ✅ |
| Playback | ||
| Play/Pause/Stop | ✅ | ✅ |
| Playback completion callback | ✅ | ✅ |
| Resume playback | ✅ | ✅ |
| Reset to beginning | ❌ | ✅ |
| Audio Control | ||
| Volume control | ✅ | ✅ |
| Pan (L/R stereo) | ✅ | ❌ |
| Playback speed | ✅ | ✅ |
| System Integration | ||
| Get system volume | ✅ | ✅ |
| Set system volume | ❌ | ✅ |
| Advanced Features | ||
| Loop control | ✅ | ✅ |
| Exact loop count | ✅ | ❌ |
| Seek to time position | ✅ | ✅ |
| Get current position | ✅ | ✅ |
| Get duration | ✅ | ✅ |
| Get channel count | ✅ | ❌ |
| Resource Management | ||
| Explicit resource cleanup | ✅ | ✅ |
npm install react-native-soundyarn add react-native-soundThis library supports both the old and new React Native architecture:
- ✅ Old Architecture: Uses traditional NativeModules
- ✅ New Architecture: Uses TurboModules for better performance
- ✅ Expo: Compatible with custom development builds (not Expo Go)
This usually indicates a linking issue. Try:
-
Clear build cache:
cd android && ./gradlew cleanBuildCache
-
Reset Metro cache:
npx react-native start --reset-cache
-
Clean and rebuild:
# iOS cd ios && rm -rf build && cd .. && npx react-native run-ios # Android cd android && ./gradlew clean && cd .. && npx react-native run-android
- Ensure audio files are added to Xcode project bundle
- Check that AVFoundation framework is linked (automatically handled by CocoaPods)
- Place audio files in
android/app/src/main/res/raw/ - Use lowercase filenames without spaces or special characters
- Clear build cache if encountering linking issues
Check out our enhanced example app with both remote and local audio playback:
- 📁
/example- Full-featured demo application - 🎯 Remote URL audio playback
- 📱 Local bundled audio files
- 🎨 Modern UI with TypeScript
- 🎵 react-native-sound-playerview - Advanced audio player UI component
Save audio files in android/app/src/main/res/raw/:
android/app/src/main/res/raw/ ├── whoosh.mp3 ✅ Correct ├── button_click.wav ✅ Correct └── my-sound.mp3 ❌ Use underscores: my_sound.mp3 Note: Use lowercase, underscored filenames. No subdirectories allowed.
- Open your project in Xcode
- Right-click your project → "Add Files to [PROJECT]"
- Select your audio files and ensure they're added to the app target
import Sound from "react-native-sound"; // Enable playback in silence mode (important for iOS) Sound.setCategory("Playback"); // Load a sound file from the app bundle const whoosh = new Sound("whoosh.mp3", Sound.MAIN_BUNDLE, (error) => { if (error) { console.log("Failed to load the sound", error); return; } // Sound loaded successfully console.log("Duration:", whoosh.getDuration(), "seconds"); console.log("Channels:", whoosh.getNumberOfChannels()); // Play the sound whoosh.play((success) => { if (success) { console.log("Successfully finished playing"); } else { console.log("Playback failed due to audio decoding errors"); } }); }); // Audio controls whoosh.setVolume(0.5); // 50% volume whoosh.setPan(1); // Full right stereo whoosh.setNumberOfLoops(-1); // Loop indefinitely // Get current properties console.log("Volume:", whoosh.getVolume()); console.log("Pan:", whoosh.getPan()); console.log("Loops:", whoosh.getNumberOfLoops()); // Seek to specific time whoosh.setCurrentTime(2.5); // Get current playback position whoosh.getCurrentTime((seconds) => { console.log("Current time:", seconds); }); // Control playback whoosh.pause(); // Pause playback whoosh.stop(() => { // Stop and rewind whoosh.play(); // Play from beginning }); // Always release resources when done whoosh.release();// From app bundle (most common) const bundleSound = new Sound("sound.mp3", Sound.MAIN_BUNDLE, callback); // From documents directory const docSound = new Sound("sound.mp3", Sound.DOCUMENT, callback); // From library directory const libSound = new Sound("sound.mp3", Sound.LIBRARY, callback); // From absolute path const pathSound = new Sound("/path/to/sound.mp3", "", callback); // From remote URL (iOS/Android only) const urlSound = new Sound("https://example.com/sound.mp3", "", callback);import { useEffect, useRef, useState } from "react"; import Sound from "react-native-sound"; const useSound = (filename: string) => { const sound = useRef<Sound | null>(null); const [isLoaded, setIsLoaded] = useState(false); const [isPlaying, setIsPlaying] = useState(false); useEffect(() => { sound.current = new Sound(filename, Sound.MAIN_BUNDLE, (error) => { if (error) { console.log("Error loading sound:", error); return; } setIsLoaded(true); }); return () => { sound.current?.release(); }; }, [filename]); const play = () => { if (sound.current && isLoaded) { sound.current.play((success) => { setIsPlaying(false); }); setIsPlaying(true); } }; const stop = () => { if (sound.current) { sound.current.stop(); setIsPlaying(false); } }; return { play, stop, isLoaded, isPlaying }; };- Preload sounds during app initialization to minimize playback delay
- Reuse Sound instances for multiple playbacks of the same file
- Avoid race conditions by ensuring sounds are loaded before calling
play()
- iOS: Uses
AVAudioSessionCategoryAmbientto mix multiple sounds - Multiple playback: You can play several sound files simultaneously
- Background audio: Configure audio categories for background playback
Supports: AAC, AIFF, CAF, MP3, WAV, and more
Supports: 3GPP, MP4, MP3, AAC, OGG, FLAC, WAV, and more
- Android absolute paths: Use
/sdcard/prefix (e.g.,/sdcard/Downloads/sound.mp3) - Method chaining: Supported for setters (e.g.,
sound.setVolume(0.5).setPan(0.5).play())
| Library | Purpose | Best For |
|---|---|---|
| react-native-video | Video & audio streaming | Streaming audio/video |
| react-native-audio-toolkit | Advanced audio features | Recording & complex audio |
| Expo Audio | Expo audio solution | Expo managed workflow |
| @react-native-async-storage/async-storage | Storage | Persisting audio preferences |
- ✅ Playing sound effects and short audio clips
- ✅ Background music with simple controls
- ✅ Audio feedback for user interactions
- ✅ Cross-platform compatibility requirements
- ✅ TypeScript projects requiring type safety
- ❌ Audio streaming or long-form content
- ❌ Advanced audio processing or effects
- ❌ Audio recording capabilities
- ❌ Complex playlist management
We welcome contributions! Here's how you can help:
- 🐛 Bug fixes and stability improvements
- 📚 Documentation improvements and examples
- 🧪 Test coverage expansion
- 🚀 Performance optimizations
- 🆕 New platform support
- Fork and clone the repository
- Install dependencies:
npm install - Run the example app:
cd example && npm install && npm run android - Make your changes and test thoroughly
- Add tests for new features
- Update documentation as needed
- 🔍 Open an issue first for major changes to discuss the approach
- ✅ Include tests for new functionality
- 📝 Update documentation including TypeScript definitions
- 🧪 Test on multiple platforms (iOS and Android)
- 📱 Test with both architectures (old and new React Native architecture)
-
Follow existing TypeScript/JavaScript patterns
-
Use meaningful commit messages
-
Keep changes focused and atomic
-
To minimize playback delay, you may want to preload a sound file without calling
play()(e.g.var s = new Sound(...);) during app initialization. This also helps avoid a race condition whereplay()may be called before loading of the sound is complete, which results in no sound but no error because loading is still being processed. -
You can play multiple sound files at the same time. Under the hood, this module uses
AVAudioSessionCategoryAmbientto mix sounds on iOS. -
You may reuse a
Soundinstance for multiple playbacks. -
On iOS, the module wraps
AVAudioPlayerthat supports aac, aiff, mp3, wav etc. The full list of supported formats can be found at https://developer.apple.com/library/content/documentation/MusicAudio/Conceptual/CoreAudioOverview/SupportedAudioFormatsMacOSX/SupportedAudioFormatsMacOSX.html -
On Android, the module wraps
android.media.MediaPlayer. The full list of supported formats can be found at https://developer.android.com/guide/topics/media/media-formats.html -
On Android, the absolute path can start with '/sdcard/'. So, if you want to access a sound called "my_sound.mp3" on Downloads folder, the absolute path will be: '/sdcard/Downloads/my_sound.mp3'.
-
You may chain non-getter calls, for example,
sound.setVolume(.5).setPan(.5).play().
MIT License - see LICENSE file for details.
If this library helps your project, consider:
- ⭐ Starring the repository
- 🐛 Reporting bugs and issues
- 📝 Contributing improvements
- 💬 Helping others in discussions
- 📢 Sharing with the community
Made with ❤️ by the React Native community