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 🙏

© 2026 – Pkg Stats / Ryan Hefner

expo-audio-stream-pcm

v0.2.0

Published

Real-time PCM audio streaming for Expo (New Architecture)

Readme

expo-audio-stream-pcm

Real-time 16-bit PCM capture from the device microphone on Android and iOS, delivered to JavaScript as Base64-encoded chunks via Expo’s event system. The module is implemented with AudioRecord (Android) and AVAudioEngine (iOS), targets Expo SDK 55+ and React Native’s New Architecture (JSI / Turbo Modules), and works in managed and bare Expo workflows.

Platform testing: This library is validated on Android right now. The iOS implementation is included and intended to work, but it has not been exercised in the same depth; if you hit problems on iOS, please open an issue—they will be fixed.

Many older audio packages still assume the legacy bridge or lack first-class New Architecture support. This library exists to fill that gap for streaming-style use cases (e.g. live speech APIs) without pulling in extra native audio SDKs.

Installation

In your app (Expo SDK 55+):

npx expo install expo-audio-stream-pcm

For a bare React Native app using Expo modules, follow Expo’s native setup so expo-modules-core is linked, then install the package as above.

Permissions (app responsibility)

The native module does not request permissions. Your app must declare platform permissions and request them before calling start().

Android

Add to your application AndroidManifest.xml (or rely on this library’s merged manifest, which includes RECORD_AUDIO):

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

At runtime, request the permission with your preferred API (e.g. PermissionsAndroid, expo-audio’s requestRecordingPermissionsAsync(), or another permission helper).

iOS

Set a microphone usage string in Info.plist (or app.jsonexpo.ios.infoPlist):

<key>NSMicrophoneUsageDescription</key>
<string>Describe why you need the microphone.</string>

API

TypeScript types

export type AudioStreamOptions = {
  sampleRate?: number; // default: 16000
  channels?: number; // default: 1
  bitDepth?: number; // default: 16
};

export type AudioDataEvent = {
  data: string; // Base64-encoded PCM chunk (16-bit little-endian frames)
};

Methods

| Export | Description | | --- | --- | | start(options?: AudioStreamOptions): void | Begins capture with optional sample rate, channel count, and bit depth (see limitations). | | stop(): void | Stops capture and releases native resources. | | addListener('onAudioData', callback): EventSubscription | Listens for Base64 PCM chunks. Call subscription.remove() to unsubscribe. |

Default export

The default export is an object { start, stop, addListener } for convenience.

Example

import ExpoAudioStream from 'expo-audio-stream-pcm';

ExpoAudioStream.start({ sampleRate: 16000, channels: 1, bitDepth: 16 });

const subscription = ExpoAudioStream.addListener('onAudioData', (event) => {
  console.log(event.data); // Base64 PCM
});

ExpoAudioStream.stop();
subscription.remove();

Deepgram live streaming example

Below is a minimal pattern: request the mic in your app, open a Deepgram WebSocket live endpoint, then forward each Base64 chunk as binary after decoding. Adjust URLs, model names, and auth to match Deepgram’s current API.

import * as FileSystem from 'expo-file-system';
import ExpoAudioStream from 'expo-audio-stream-pcm';

const DG_KEY = process.env.EXPO_PUBLIC_DEEPGRAM_KEY!; // never ship production keys in client apps

function base64ToUint8Array(b64: string): Uint8Array {
  const binary = atob(b64);
  const bytes = new Uint8Array(binary.length);
  for (let i = 0; i < binary.length; i++) {
    bytes[i] = binary.charCodeAt(i);
  }
  return bytes;
}

export async function streamMicToDeepgram(wsUrl: string) {
  const ws = new WebSocket(wsUrl, undefined, {
    headers: { Authorization: `Token ${DG_KEY}` },
  });

  await new Promise<void>((resolve, reject) => {
    ws.onopen = () => resolve();
    ws.onerror = (e) => reject(e);
  });

  const sub = ExpoAudioStream.addListener('onAudioData', (e) => {
    const pcm = base64ToUint8Array(e.data);
    ws.send(pcm);
  });

  ExpoAudioStream.start({ sampleRate: 16000, channels: 1, bitDepth: 16 });

  return () => {
    sub.remove();
    ExpoAudioStream.stop();
    ws.close();
  };
}

Security note: embedding API keys in a client app is unsafe for production. Prefer a backend that issues short-lived tokens or proxies audio to Deepgram.

Limitations

  • 16-bit PCM only — other bitDepth values are ignored with a native warning; output is always 16-bit PCM in the event payload.
  • Permissions are not handled inside the module; the hosting app must declare and request them.
  • Web — the JS API is stubbed; no microphone capture on web.
  • Testing — developed against Expo SDK 55 and React Native 0.77+ (New Architecture). Only Android has been tested end-to-end so far; treat iOS as best-effort until reported issues are resolved. Validate on your target OS versions and devices.

Example app

From the repository root:

cd example
npm install
npx expo run:android
# iOS is supported in code but not the primary tested path yet:
npx expo run:ios

The example uses expo-audio’s requestRecordingPermissionsAsync() before starting the stream.

License

MIT