whisper.rn
CLAUDE.md809 starsChanged 8 months agoMITView on GitHub
whisper.rn is a React Native binding for whisper.cpp, enabling high-performance inference of OpenAI's Whisper automatic speech recognition (ASR) model on iOS and Android devices. Key Features: - Native speech-to-text transcription via whisper.cpp - NVIDIA Parakeet TDT transcription with multilingual model support - Voice Activity Detection (VAD) on vad.cpp (Silero / FireRedVAD), offline and stateful streaming - Realtime transcription with auto-slicing and memory management - Core ML support for iOS (encoder acceleration) - Metal/GPU acceleration support - JSI (JavaScript Interface) bindings…
- Deletes or force-pushes
- Installs packages
In this repository2 other files it gives its agents.
- AGENTS.mdAGENTS.md
- upgrade-react-nativeSkill
The file
We split this CLAUDE.md into the rules it gives an agent
12 rules. About 0 are common, 8 are distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈8
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 19
Add comments appropriately, no need to explain the obvious.
- Distinctive: line 120
Always on the CPU (vad.cpp kernels).
- Very specific: line 174
Must be co-located with GGML model (e.g., ggml-tiny.en.bin → ggml-tiny.en-encoder.mlmodelc/)
- Distinctive: line 239
Use quantized models (q8, q5) to reduce size and improve speed on mobile
- Distinctive: line 240
Default thread count: 2 for 4-core devices, 4 for more cores
- Distinctive: line 241
Test in Release mode for accurate performance measurement
- Very specific: line 245
Use require() for bundled models/audio
- Very specific: line 246
Add .bin and .mil to Metro config assetExts
- Very specific: line 264
Use PermissionsAndroid to request runtime permission
- Distinctive: line 269
Follow standard React Native library integration
- Distinctive: line 282
Test realtime: Requires microphone permissions
- Distinctive: line 365
Use Release mode for testing
≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
whisper.rn - Development Guidelines
Project Overview
whisper.rn is a React Native binding for whisper.cpp, enabling high-performance inference of OpenAI's Whisper automatic speech recognition (ASR) model on iOS and Android devices.
Key Features:
- Native speech-to-text transcription via whisper.cpp
- NVIDIA Parakeet TDT transcription with multilingual model support
- Voice Activity Detection (VAD) on vad.cpp (Silero / FireRedVAD), offline and stateful streaming
- Realtime transcription with auto-slicing and memory management
- Core ML support for iOS (encoder acceleration)
- Metal/GPU acceleration support
- JSI (JavaScript Interface) bindings for efficient ArrayBuffer transfers
General
- Pay attention to code readability.
- Add comments appropriately, no need to explain the obvious.
- Apply first-principles thinking when appropriate.
Development Commands
Setup and Bootstrap
npm install # Install dependencies
npm run bootstrap # Setup example app deps + download example model assets
npm run sync:vendor # Re-vendor vendor/whisper.cpp from vendor/VERSIONS + scripts/patchesCode Quality
npm run typecheck # Type-check TypeScript files
npm run lint # Lint files with ESLint
npm run lint -- --fix # Fix lint errors
npm test # Run Jest unit testsBuild Commands
npm run build # Build TypeScript library with react-native-builder-bob
# Prebuilt native libraries (release assets, see "Prebuilt native libraries")
npm run build:ios-frameworks # Build ios/rnwhisper.xcframework for iOS/tvOS
npm run build:android-libs # Build android/src/main/jniLibs (librnwhisper*.so per ABI/variant)
./scripts/setup-hexagon-sdk.sh # Install the Hexagon SDK (needed for the *_hexagon variant)
npm run build:hexagon-htp # Build the Hexagon DSP-side libggml-htp-*.so into bin/ (Docker)
npm run download:native-artifacts # Re-run the postinstall download (--force to re-download)
# Example app builds
npm run example run start # Start Metro bundler for example app
npm run example run android # Run example on Android
npm run example run ios # Run example on iOS
npm run example run pods # Install iOS pods for example
# Release builds for testing performance
npm run example run ios -- --mode Release
npm run example run android -- --mode releaseDocumentation
npm run docgen # Generate API documentation with typedocRelease
npm run release # Publish new version with release-itArchitecture
Multi-Layer Architecture
whisper.rn has a 4-layer architecture:
- JavaScript/TypeScript Layer (
src/):index.ts: Main API exports,WhisperContext,ParakeetContext, andWhisperVadContextclassesNativeRNWhisper.ts: TurboModule spec (New Architecture compatible)realtime-transcription/: Enhanced realtime transcription frameworkRealtimeTranscriber.ts: Main transcriber with VAD integrationStreamVad.ts+StreamingVadSegmenter.ts: stateful VAD on a native vad.cpp stream (RingBufferVadis the deprecated windowed one)SliceManager.ts: Audio slice memory managementadapters/: Audio stream adapters (e.g., AudioPcmStreamAdapter)
- JSI Bindings (
cpp/jsi/):RNWhisperJSI.cpp/h: High-performance JSI functions for ArrayBuffer operations- Direct memory transfer for audio data (bypasses JSON serialization)
- Thread pool for async processing
- Functions:
whisperTranscribeData,parakeetTranscribeData,whisperVadDetectSpeech,whisperVadStream{Create,Push,Flush,Reset,Release}
- Native Modules (
ios/,android/src/main/java/):- iOS:
RNWhisper.mm,RNWhisperContext.mm,RNWhisperVadContext.mm - Android:
RNWhisper.java,WhisperContext.java,WhisperVadContext.java - React Native bridge methods for initialization, file transcription
- Audio session management (iOS)
- Realtime recording (deprecated, use RealtimeTranscriber instead)
- iOS:
- C++ Core (
cpp/+vendor/):cpp/holds only whisper.rn's own code:rn-whisper.cpp/h(job management, transcription orchestration),rn-whisper-log.h, andjsi/- vad.cpp (VAD models, segmentation and its
vad-cpuggml backend) is vendored undervendor/vad.cpp/, built against the same ggml; its pin invendor/VERSIONSmust track the ggml whisper.cpp carries (vad-cpu uses ggml's internal backend headers). Patches inscripts/patches/vad.cpp/. - whisper.cpp is vendored under
vendor/whisper.cpp/in its upstream layout (include/,src/withwhisper.cpp,parakeet.cppandcoreml/,ggml/{include,src}with the CPU and Metal backends), pinned byvendor/VERSIONS, with whisper.rn changes kept as-p1patches inscripts/patches/whisper.cpp/. No git submodule, no symbol renaming. Seevendor/README.md. vendor/whisper.cppis also consumed by whisper.node via upstream's CMake project, so it carries upstream's build files,examples/common-whisper.*(withminiaudio.h/stb_vorbis.c) and the BLAS/CUDA/Vulkan/WebGPU backends. whisper.rn's builds never compile those;cmake/rnwhisper-sources.cmakeandwhisper-rn.podspecfilter them out (keep the two in sync).cmake/rnwhisper-sources.cmakeis the single source/include list every CMake build uses;whisper-rn.podspecmirrors it for CocoaPods
Context Management
The library uses a context-based model:
- WhisperContext: Main transcription context, initialized with a GGML model file
- Methods:
transcribe(),transcribeData(),transcribeRealtime()(deprecated),bench(),release() - Supports file paths, base64 WAV, ArrayBuffer (via JSI), and asset URIs
- Methods:
- ParakeetContext: Parakeet TDT transcription context, initialized with a Parakeet GGUF model
- Methods:
transcribe(),transcribeData(),release() - Supports WAV file paths, base64 WAV, raw PCM16 ArrayBuffer data, and asset URIs
- Methods:
- WhisperVadContext: VAD context (vad.cpp), initialized with a speech model (whisper.cpp
ggml-silero-*.binor vad.cpp GGUF)- Methods:
detectSpeech(),detectSpeechData()(offline),createStream()(statefulWhisperVadStream:push/flush/reset/release),release() - Always on the CPU (vad.cpp kernels); Android copies asset/resource models to a temp file because vad.cpp only loads from paths
- Methods:
All contexts maintain:
id: Unique context identifiergpu: Whether GPU/Metal acceleration is activereasonNoGPU: Explanation if GPU is not available
JSI Installation
JSI bindings are lazily installed on first use:
- Called automatically by
initWhisper(),initParakeet(), andinitWhisperVad() - Installs global functions including
whisperTranscribeData,parakeetTranscribeData, andwhisperVadDetectSpeech - These functions are then captured and removed from global scope
Realtime Transcription Architecture
The modern RealtimeTranscriber (in src/realtime-transcription/) provides:
- SliceManager: Manages audio slicing with circular buffer strategy
- Auto-slices at configurable duration (default: 30s)
- Keeps limited slices in memory (default: 3)
- Provides memory usage stats
- VAD Integration (
StreamVad):- One native stream per session: each chunk is pushed once, the model state carries over, one probability per hop (32 ms for Silero)
StreamingVadSegmenteris the incremental form of silero-vad's offline segmentation (parity-tested against vad.cpp'svad_segments_from_probs); events carry the segment's audio and sample positions, not wall-clock time- Triggers auto-slice on speech_end events
- Configurable thresholds and presets
- Queue-based Processing:
- Transcription queue for sequential processing
- One transcription at a time
- Results stored by slice index
- Prompt Chaining:
- Optional initial prompt
- Can chain previous slice results as context
- Improves continuity in long transcriptions
- Audio Stream Adapters:
- Interface-based design for different audio sources
- Built-in:
AudioPcmStreamAdapter(uses @fugood/react-native-audio-pcm-stream) - Custom adapters can be implemented
iOS-Specific Features
Pre-built Framework:
- By default, uses
ios/rnwhisper.xcframework(pre-built, downloaded by postinstall from the GitHub release; see "Prebuilt native libraries") - Set
RNWHISPER_BUILD_FROM_SOURCE=1in Podfile to build from source - Framework includes Metal shaders (
.metallib)
Core ML Support:
- Accelerates encoder on iOS 15.0+
- Model files:
.mlmodelcdirectories (model.mil, coremldata.bin, weights/weight.bin) - Must be co-located with GGML model (e.g.,
ggml-tiny.en.bin→ggml-tiny.en-encoder.mlmodelc/) - Set
useCoreMLIos: falseto disable even if files exist
Build Flags:
RNWHISPER_DISABLE_COREML=1: Disable Core ML compilationRNWHISPER_DISABLE_METAL=1: Disable Metal GPU acceleration
Android-Specific Features
JNI Bridge: android/src/main/jni.cpp connects Java to C++ whisper core
Two-layer native build (android/src/main/CMakeLists.txt):
- Core
librnwhisper<variant>.so(whisper.cpp + ggml +cpp/rn-whisper.cpp, no RN dependency), built byandroid/src/main/rnwhisper/CMakeLists.txt. Prebuilt by default:scripts/build-android.sh->android/src/main/jniLibs/, shipped as a GitHub release asset and downloaded by postinstall. Compiled with hidden visibility; only theWHISPER_API/GGML_API/PARAKEET_API/RNWHISPER_APIsurface is exported. - Wrapper
librnwhisper_jni<variant>.so(jni.cpp+cpp/jsi/RNWhisperJSI.cpp), always compiled by the consumer's Gradle build because it links the app's React Native (fbjni/jsi prefab). It links the matching core by SONAME;RNWhisper.javaloads the wrapper and the dynamic linker pulls the core in. rnwhisperBuildFromSource=true(gradle property) builds the core as a subdirectory instead of using jniLibs;rnwhisperVariants=a,bnarrows the variants. The example app'sgradle.propertiessets from-source; CI passes-PrnwhisperBuildFromSource=falseto exercise the prebuilt path.- Variant list lives once in
android/src/main/cmake/rnwhisper-build-options.cmake(shared by both CMake entry points).
Build Configuration:
- NDK version 24.0.8215888+ recommended for Apple Silicon Macs
- Supports 16KB page sizes (Android 15+)
- Proguard rule required:
-keep class com.rnwhisper.** { *; }
Hexagon (NPU): the rnwhisper_v8fp16_va_2_hexagon variant needs the Hexagon SDK on the build host (scripts/setup-hexagon-sdk.sh) plus the FastRPC stub and DSP-side bin/arm64-v8a/libggml-htp-*.so from scripts/build-hexagon-htp.sh (Docker). The prebuilt jniLibs include the variant; bin/ ships inside the npm package and android/build.gradle syncs it into the host app's assets.
File Patterns and Conventions
Code Organization
- TypeScript:
src/(library),example/src/(example app) - Native iOS:
ios/*.{h,m,mm}(Objective-C/C++) - Native Android:
android/src/main/java/com/rnwhisper/*.java - C++ Core:
cpp/*.{cpp,h} - JSI Bindings:
cpp/jsi/*.{cpp,h} - Native artifact download:
install/*.js+install/native-artifacts.json - Tests:
src/**/__tests__/*.test.ts
Naming Conventions
- Classes: PascalCase (
WhisperContext,RealtimeTranscriber) - Files: kebab-case for configs, PascalCase for classes
- Native modules: RNWhisper prefix (iOS/Android)
- C++ namespace:
rnwhisper - ggml/whisper.cpp symbols keep their upstream names (
ggml_*,whisper_*); the vendored sources are not renamed. Coexistence with other ggml-based libraries (e.g. llama.rn) relies on each library being its own dynamic image (two-level namespace on Apple,RTLD_LOCALplus-Bsymbolicon Android). The Objective-CGGMLMetalClassis process-global, so it is renamed per library with-DGGMLMetalClass=RNWhisperGGMLMetalClass.
Commit Messages
Follow Conventional Commits:
feat:new featuresfix:bug fixesrefactor:code refactoringdocs:documentationtest:testschore:tooling/build changes
Important Implementation Details
Audio Format Requirements
- Sample rate: 16kHz (whisper requirement)
- Channels: Mono (1 channel)
- Format: 16-bit PCM
- Input: WAV files, base64 WAV, or raw PCM data (base64 or ArrayBuffer)
Memory Management
- Contexts must be explicitly released:
context.release()orreleaseAllWhisper() - Realtime transcription: Use
maxSlicesInMemoryto control buffer size - Large models (medium/large) on iOS: Enable Extended Virtual Addressing entitlement
Performance Optimization
- Use quantized models (q8, q5) to reduce size and improve speed on mobile
- Default thread count: 2 for 4-core devices, 4 for more cores
- Test in Release mode for accurate performance measurement
- GPU/Metal acceleration: Set
useGpu: true(enabled by default)
Asset Handling
- Use
require()for bundled models/audio - Add
.binand.milto Metro configassetExts - Max file size: 2GB (RN packager limitation)
- Alternative: Download models at runtime
Deprecated APIs
transcribeRealtime(): UseRealtimeTranscriberinstead- Old API still works but lacks VAD auto-slicing and better memory management
Platform-Specific Notes
iOS
- Minimum iOS: 11.0, tvOS: 11.0
- Metal acceleration: iOS/tvOS only
- Audio Session management: Use
AudioSessionIosutilities oraudioSessionOnStartIos/audioSessionOnStopIosoptions - Microphone permission: Add
NSMicrophoneUsageDescriptionto Info.plist (tvOS: microphone not supported)
Android
- Microphone permission:
RECORD_AUDIOin AndroidManifest.xml - Use
PermissionsAndroidto request runtime permission - Supports both Old and New Architecture
Expo
- Requires prebuild:
npx expo prebuild - Follow standard React Native library integration
Testing
Unit Tests
- Jest with React Native preset
- Mock available:
jest.mock('whisper.rn', () => require('whisper.rn/jest-mock')) - Run:
npm test
Example App
- Located in
example/ - Uses tiny.en model and jfk.wav sample
- Demonstrates: Whisper and Parakeet transcription, VAD, realtime transcription
- Test realtime: Requires microphone permissions
Build System
Vendored sources (scripts/sync-vendor.sh)
- Clones/fetches the whisper.cpp commit pinned in
vendor/VERSIONSinto~/.cache/whisper.rn/ - Exports the subset whisper.rn builds into
vendor/whisper.cpp/, unchanged and in upstream layout - Applies
scripts/patches/whisper.cpp/*.patch - Regenerates
src/version.json(whisper/ggml versions + commit; the builds pass them asWHISPER_VERSION/PARAKEET_VERSION/GGML_VERSION/GGML_COMMITcompile definitions)
Its output is committed. Run it after changing vendor/VERSIONS or a patch; running it on a clean tree must produce no diff.
Patching whisper.cpp
- Edit the vendored file in place, e.g.
vendor/whisper.cpp/src/whisper.cpp - Regenerate its patch:
scripts/update-patch.sh src/whisper.cppwritesscripts/patches/whisper.cpp/whisper.cpp.patch(optional second argument names the patch; a file upstream lacks becomes a file-creating patch; text above the first---line survives regeneration, use it to say why) npm run sync:vendormust leavegit statusclean
Prebuilt native libraries
Both platforms' heavy native code is prebuilt in the release workflow (.github/workflows/release.yml) and attached to the GitHub release; the npm package only carries the sources and bin/ (Hexagon DSP libs):
whisper-rn-android-jni-libs.tar.gz->android/src/main/jniLibs/(scripts/build-android.sh, ubuntu runner with the Hexagon SDK + Docker HTP build)whisper-rn-ios-xcframework.tar.gz->ios/rnwhisper.xcframework/(scripts/build-ios.sh, macOS runner)
install/download-native-artifacts.js (the package's postinstall, also npx whisper-rn-download-artifacts) downloads them from <repository>/releases/download/v<version>/, verifies the SHA-256 recorded in install/native-artifacts.json and writes a .whisper-rn.sha256 marker so reinstalls are no-ops. The checksums are null in git; install/write-native-artifacts-manifest.js fills them in CI right before npm publish. A git checkout therefore never downloads anything and builds from source (RNWHISPER_BUILD_FROM_SOURCE=1 for pods, rnwhisperBuildFromSource=true for Gradle, both set in the example app). RNWHISPER_SKIP_POSTINSTALL=1 skips the download.
Release order matters: npm run release (release-it) tags and creates the GitHub release; the tag push runs the workflow, which builds Android, builds iOS, uploads both archives to that release, then publishes to npm. workflow_dispatch re-uploads assets to an existing release (optionally re-publishing).
Bootstrap (scripts/bootstrap.sh)
Developer environment only; it never touches vendor/:
example/dependencies- Example model/audio assets into
example/assets/(dummy models fromvendor/whisper.cpp/models/whenCI=true)
iOS Framework Build Process
- CMake generates Xcode project from
ios/CMakeLists.txt - Builds for multiple targets: iOS device, iOS simulator, tvOS device, tvOS simulator
- Creates universal
rnwhisper.xcframeworkwith all architectures - Includes Metal kernel sources (compiled at runtime) and the public C headers
- Script:
scripts/build-ios.sh
TypeScript Build
- Uses
react-native-builder-bob - Outputs: CommonJS, ES Module, TypeScript definitions
- Output dir:
lib/
Android Build
- Gradle + CMake
- NDK builds JNI library
- Output:
librnwhisper.so
Key Dependencies
Runtime
react-native: Core frameworkwhisper.cpp: C++ ASR engine (vendored atvendor/whisper.cpp/, seevendor/README.md)safe-buffer: Buffer polyfill
Realtime Transcription
@fugood/react-native-audio-pcm-stream: ForAudioPcmStreamAdapter- Filesystem module (e.g.,
react-native-fs): For WAV file writing
Development
react-native-builder-bob: TypeScript buildtypedoc+typedoc-plugin-markdown: API docs@commitlint,lefthook: Git hooksrelease-it: Publishing
Troubleshooting Common Issues
iOS Build Issues
- Clean derived data:
rm -rf ~/Library/Developer/Xcode/DerivedData - Clean build:
npm run clean && cd example/ios && pod install - Large models: Enable Extended Virtual Addressing entitlement
Android Build Issues
- Unknown host CPU on Apple Silicon: Use NDK 24.0.8215888+
- See
docs/TROUBLESHOOTING.mdfor more details
Performance Issues
- Use Release mode for testing
- Choose appropriate model size for device
- Adjust
maxThreadsif needed - Consider quantized models
Related Documentation
- API Docs:
docs/API/ - Tips & Tricks:
docs/TIPS.md - Troubleshooting:
docs/TROUBLESHOOTING.md - Contributing:
CONTRIBUTING.md - whisper.cpp upstream: https://github.com/ggerganov/whisper.cpp
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

