@tilawi/react-native-quran-asr
v0.1.0
Published
On-device Quran recitation recognition for React Native / Expo: onnxruntime runner, recognition pipeline and a lifecycle-safe microphone hook.
Maintainers
Readme
@tilawi/react-native-quran-asr
On-device Quran recitation recognition for React Native / Expo. Record a recitation, find the verse, or get a transcript for word-by-word memorization feedback, entirely on the phone: no server, and the audio never leaves the device.
This is the React Native layer of the Tilawi app's voice search and memorization checks:
@tilawi/quran-asr: the recognition core (decoding, verse matching, trust gate, memorization judge). Accuracy numbers are there.@tilawi/expo-pcm-recorder: 16 kHz microphone capture.- The model and data:
muhdur/tilawi-fastconformer-quranon Hugging Face (88 MB ONNX).
What's inside
| Export | |
|---|---|
| createQuranAsr({ loadAssets }) | Loads the model and data once (you decide from where), then transcribeClip(pcm) for voice search or transcribeTextOnly(pcm) for memorization. Clips without speech throw asr_no_speech. transcribeTextOnly handles clips over 60 s in overlapping 30 s windows to bound memory; transcribeClip runs in one pass, so keep voice-search clips short (inference memory grows about 13 MB per second of audio). |
| useAsrRecorder({ ensurePermission }) | A recorder hook: start(), stop() returns the whole clip as a Float32Array, cancel(), plus isRecording, durationMs and a live metering level. Safe against unmounts and double taps mid-start. |
| createOnnxRunner(modelPath) | The onnxruntime-react-native runner used by default. |
Install
npx expo install @tilawi/react-native-quran-asr @tilawi/expo-pcm-recorder onnxruntime-react-native expo-audio expo-asset expo-file-systemThis needs a development build (native code). Two setup notes, both learned the hard way:
Android + Expo SDK 54: onnxruntime-react-native isn't linked. It ships a legacy
unimodule.json, so Expo autolinking treats it as an Expo module and skips React Native linking;NativeModules.Onnxruntimeis thennullon Android (microsoft/onnxruntime#29004, closed as stale but still present onmain). Fix it with areact-native.config.jsin your app:module.exports = { dependencies: { 'onnxruntime-react-native': { platforms: { android: { sourceDir: 'android' } } }, }, };Android builds use an unpinned ONNX Runtime. onnxruntime-react-native's Gradle file resolves
onnxruntime-android:latest.integration, so a build can silently pick up a newer native runtime than the JS expects (microsoft/onnxruntime#32744). Tilawi pins it with a patch-package patch replacinglatest.integrationwith1.24.3.
Add the model files to Metro's asset extensions (metro.config.js) if you bundle them with the app:
config.resolver.assetExts.push('onnx', 'bin'); // ship the JSON files renamed to .bin so they stay out of the JS bundleUsage
import { createQuranAsr, useAsrRecorder } from '@tilawi/react-native-quran-asr';
import { requestRecordingPermissionsAsync } from 'expo-audio';
const asr = createQuranAsr({
// Return { modelPath, vocab, quranCtcTokens, quran }. The model must be a
// local file path; the three JSON files come from the Hugging Face repo.
loadAssets: async () => loadMyAssets(),
});
function VoiceSearch() {
const recorder = useAsrRecorder({
ensurePermission: async () => {
const { granted } = await requestRecordingPermissionsAsync();
if (!granted) throw new Error('asr_permission_denied');
},
});
const onStart = async () => {
asr.warmUp(); // load the model while the user recites
await recorder.start();
};
const onStop = async () => {
const pcm = await recorder.stop();
const result = await asr.transcribeClip(pcm);
if (result.surah > 0) navigateTo(result.surah, result.ayah);
// surah/ayah are 0 when no verse was matched confidently
};
// ...
}For memorization feedback, pass await asr.transcribeTextOnly(pcm) and the expected verse words to judgeAttempt from @tilawi/quran-asr.
Tilawi's own asset loader (bundled with expo-asset, model copied to the document directory on first use) is a good template: it resolves each bundled file with Asset.fromModule, reads the JSON files, and copies the model to Paths.document so onnxruntime gets a stable path.
License
MIT. The model is CC-BY-4.0 (fine-tuned from NVIDIA's FastConformer) and the Quran text is from the Tanzil Project; see the Hugging Face model card.
