Playing PCM Audio Streams with expo-dev-client in React Native
Introduction & Background
Pulse-code modulation (PCM) is the raw digital representation of audio, crucial for features such as voice chat, low-latency alerts, radio apps, or real-time audio feedback in mobile apps. In React Native, standard packages (e.g. expo-av) don’t support “raw” PCM streaming. Managed Expo Go apps cannot use arbitrary native modules, but expo-dev-client removes this limitation, letting you add native code and bridge native audio libraries—including those that support PCM buffers.
For practical use cases like:
- Live voice chat streaming
- Receiving and playing server-pushed PCM alerts
- Audio processing or instrumentation apps
…it is essential to handle low-level PCM playback directly. This tutorial will walk you through setting up, coding, and troubleshooting PCM streaming using expo-dev-client and the react-native-pcm-audio package. You’ll find actionable instructions, code samples, and best practices, drawn from open-source documentation and real case studies.
Preparing Your React Native Project for PCM Audio Streaming
1. Setting Up the Project
Start by creating a new Expo project and setting it up for native development, which is required for PCM playback using native modules.
Bash
Note: expo prebuild is necessary if your project is in the managed workflow. It adds the
/iosand/androidfolders you need for native code.
2. Installing Native Dependencies
Next, add the PCM audio module:
Bash
Link and Configure
- On iOS: run
npx pod-install - On Android: open Android Studio and ensure the module appears under native dependencies
3. Building Your Custom Dev Client
expo-dev-client allows you to run your app with any native code. You must always launch your app using your custom client, not Expo Go:
Bash
4. Troubleshooting Native Integration
If you encounter errors:
- Double-check you’re running your built dev client, not Expo Go
- Remove node_modules, clean CocoaPods/Gradle:
For Android, runBash./gradlew cleaninside theandroidfolder
For more troubleshooting steps, refer to:
Integrating and Using react-native-pcm-audio: Initialization, Playback, and Stream Management
PCM Session Options
PCM playback sessions have plenty of configurable parameters. You will primarily set:
- encoding: e.g.,
'16bit','8bit','float'(see docs) - usage: e.g.,
'alarm','music','voicecall'(used by underlying OS audio APIs) - sampleRate: e.g.,
16000for 16kHz - channels: number of channels,
1(mono) or2(stereo) - mode:
'streaming'(real-time) or'static'(buffered, play once)
Example: Initializing the Audio Session
Javascript
Writing PCM Buffers
Audio data should be written in base64-encoded binary form. Here’s how to convert and send audio frames (assuming a Int16Array buffer):
Javascript
Playing and Stopping Audio
Javascript
Example: Full Streaming Session
Javascript
Key Notes:
- Always match PCM format to session options
- Call
release(sessionId)when done to free resources
Advanced PCM Streaming Scenarios and Real-World Examples
Case Study: Live Voice Streaming via WebSocket
Suppose your server sends Int16 PCM buffers via WebSocket (e.g., for a push-to-talk app). Here’s how you’d wire that up:
Javascript
Tips:
- Ensure network buffers and audio playback are synchronized (avoid underruns)
- Consider queueing or pacing if frames arrive too quickly
Playback Mode Comparison
- Streaming Mode: Best for ongoing or real-time audio (voice chat, radio)
- Static Mode: Best for playing preloaded PCM clips (alerts, notifications)
Javascript
Buffer Conversion for Different Formats
Sometimes PCM arrives as an array buffer; convert as needed:
Javascript
Real Use: Emergency Broadcast Player
Multiple government apps use server-pushed PCM clips for alerts. A similar flow would:
- Open a session on notification
- Buffer and play PCM frames as they arrive until the alert ends
- Release the session to conserve device battery/memory
Conclusion and Additional Resources
Summary Checklist
- Use
expo-dev-clientfor custom native modules - Install and link
react-native-pcm-audio - Match PCM buffer format to session configuration (encoding, bit depth, sample rate, channels)
- Use streaming for continuous audio, static for alerts/clips
- Always release sessions on unmount/finish
- Troubleshoot using project logs and double-check custom client builds






