Getting started
The TajweedFormer SDK 2.0.0 runs TajweedFormer V4 on the device and checks the tajweed of a recited ayah: per sound, per word, madd counts, with every number behind a verdict available. Four products, one shape: Android, Apple (iOS and macOS), Web and Native (Windows and Linux). Each is linked to this portal by its key.
1. Access and a key
Sign in with your Hugging Face account and request access here, on the access form: who you are, what you will build, and the models' no-profit licence (NPL-1.2). A person at Quran Lab reads every request; once it is approved, the portal opens for you. Apps download the model from Quran Lab's own servers with their key; there is nothing to download by hand.
The fastest way from there: the Overview gives you one prompt for your coding assistant. It installs the SDK, wires a new key, chooses the tajweed settings from your app's audience, explains recording sharing, drafts your directory listing, checks the setup and tells you in a few lines what it did. To do it by hand, create a key under Keys and follow your product below.
Android
Kotlin, an AAR for minSdk 26. Key type apps; the app is identified by the Gradle applicationId. Source: github.com/Quran-Lab/tajweedformer-android.
// app/build.gradle.kts
dependencies {
implementation("ai.quranlab:tajweedformer:2.0.0")
}
The key, kept out of your repository:
# local.properties (git-ignored)
QURANLAB_KEY=qlk_v1_...
Read it in Gradle into a BuildConfig field or a manifest placeholder, and pass it to the SDK's configure call at launch. The exact calls (configure, load the model, a session for one ayah, the result) are in the package's README.
Apple: iOS and macOS
Swift, the Neural Engine with the exact CPU engine as fallback. Key type apps (iOS), desktop (macOS); the app is identified by the bundle id. Source: github.com/Quran-Lab/tajweedformer-apple.
// Swift Package Manager
https://github.com/Quran-Lab/tajweedformer-apple product TajweedFormer
import TajweedFormer
The key, kept out of your repository:
// Secrets.xcconfig (git-ignored), the target's base configuration
QURANLAB_KEY = qlk_v1_...
// Info.plist: QuranLabKey = $(QURANLAB_KEY)
let key = Bundle.main.object(forInfoDictionaryKey: "QuranLabKey") as? String
An iOS app uses an apps key and its macOS build a desktop key: two keys. Add the App Attest capability for recording sharing. The exact calls (configure, load the model, a session for one ayah, the result) are in the package's README.
Web
WebAssembly for browsers and Node 20 or later. Key type web; the app is identified by the page's exact origin. Source: github.com/Quran-Lab/tajweedformer-web.
npm install @quran-lab/tajweedformer
The key, kept out of your repository:
# .env.local (git-ignored)
VITE_QURANLAB_KEY=qlk_v1_...
A web key is public by design: it works only from the exact https origins you give it. The page needs a secure context. The exact calls (configure, load the model, a session for one ayah, the result) are in the package's README.
Native: Windows and Linux
The C API, for desktop apps and servers on x86-64. Key type desktop; the app is identified by package family name or AppUserModelID; reverse-DNS app id. Source: github.com/Quran-Lab/tajweedformer-c.
# Windows or Linux x86-64: the C library and its CMake project
git clone --recurse-submodules https://github.com/Quran-Lab/tajweedformer-c
# then add_subdirectory(tajweedformer-c) in your CMakeLists.txt
The key, kept out of your repository:
# the environment, or your app's config file
QURANLAB_KEY=qlk_v1_...
Never compile the key into shared source. Windows apps are identified by their package family name or AppUserModelID, Linux apps by their reverse-DNS app id. The exact calls (configure, load the model, a session for one ayah, the result) are in the package's README.
3. What the key does
With the key, the SDK by itself:
- registers the app on its first launch: it appears under Apps, nothing to fill in;
- fetches its signed settings (Ed25519, checked against a key built into the SDK) at launch, when the app comes to the foreground and every minute while it is in use, so what you change here reaches running apps within about a minute;
- downloads only the model files its device needs, checks every file against the signed manifest, keeps the exact CPU engine as the fallback, and switches revisions only at the start of a session;
- sends anonymous daily counters (every field) and grouped issue reports (Issues).
Without a key the SDK still works offline, but sends nothing and gets no settings.
4. Tajweed settings
Your coding assistant sets them for each app through the setup API, after asking you, and changes them whenever you ask it. If you chose to show the Model tab when it asked, you can also change them yourself under Apps, the app, Model, Tajweed checks. Your app's code wins over the portal, which wins over the model's calibrated defaults. Devices receive the portal's settings in the signed config:
"tajweed": {
"v": 1,
"preset": "balanced",
"word": {"preset": "general"},
"overrides": {"cons_sub": {"preset": "sensitive"}, "vowel_sub": {"threshold": 1.25}},
"checks": {"letters": true, "qalqalah": false},
"context": "c16_l64"
}
| Setting | What it does |
|---|
| preset | How readily a sound is flagged: strict (fewest false flags), balanced (the default), sensitive (catch more), very_sensitive (catch the most, for a teacher who reviews the flags) |
| word | When a whole word is flagged: strict, general, beginner, or your own cut within the model's range (a higher cut flags fewer words). Available when the model's calibration ships a word level |
| overrides | Per mistake type (vowel_sub, vowel_del, vowel_ins, short_to_long, long_to_short, cons_sub, cons_del, insertion): another preset, or a threshold from -20 to 20 (a log-likelihood-ratio cut: higher flags less) |
| checks | letters, vowels, length, madd, gemination, qalqalah, lazim. Validated checks are on by default, experimental ones off until turned on; checks the model marks off are never reported in a release build, whatever this page says |
| context | How often results come: c16_l64 every 320 ms (the default), c8_l64 every 160 ms (CPU engine only), c24_l72 and c24_l256 every 480 ms. Read the list from the loaded model |
An invalid field is dropped, never the whole block. A coding assistant can set the same block through the setup API, with a one-line reason the Model tab shows when it is on.
5. Showing results to learners
Every SDK gives three layers: the raw model outputs per chunk (on request), the decoded sounds with the runner-up and a confidence, and the tajweed verdicts per sound and per word, each with the evidence behind it. A good screen highlights the words of the ayah by their verdict and shows, on a tap, what was expected and what was heard, phrased as something that may need another look, never as a ruling or a grade.
The licence asks one line of every app, where results appear: "Automatic tajweed feedback can be wrong. It does not replace a qualified teacher." Never charge for the model, access to it or a feature it powers; your own teaching and work stay yours.
Downloads per device
An SDK downloads only the files its device uses. Every file of the sdk/v4.0 folder is tagged in the signed manifest with its platforms, chip, context and role, and a file outside the device's profile is never downloaded or opened. TajweedFormer V4 at the default context c16_l64, sizes over the network (on disk after unpacking in brackets):
| Device | Downloads | What |
|---|
| Android, any phone | 56.5 MB (73.5 MB), before the first session | The CPU set: tokens, calibration, the expected sequence of every ayah, the int8 weights that every context shares, the front end, the c16_l64 graph and its frame tensors |
| Android with an NPU | Exynos 2500 +61.5 MB, Exynos 2600 +61.7 MB; Tensor G3 +120.8, G4 +120.8, G5 +121.0 MB, later | Only that chip's NPU program for the default context, fetched lazily on Wi-Fi after the CPU path passed its self-test |
| Apple, iOS and macOS | 56.5 MB (73.5 MB), before the first session | The same CPU set as Android; the exact CPU engine runs every context. The Neural Engine builds are not served yet |
| Web (WASM), browsers and Node | 56.5 MB (73.5 MB) | The same CPU set as Android |
| Native, Windows and Linux | 58.9 MB (77.2 MB); 61.2 MB with the ONNX Runtime graphs | The CPU set for all four contexts; the ONNX Runtime graphs (+0.59 MB per context) only for the server tier |
| Another context, later | c8_l64 +0.65 MB, c24_l72 +0.85 MB, c24_l256 +0.86 MB | Its graph and frame tensors; the weights are shared |
Big files are stored gzip. Each file is downloaded with HTTP range requests into a partial file, so an interrupted download resumes where it stopped. Its size and SHA-256 are checked against the signed manifest, then it is unpacked and the unpacked file is checked again; only the unpacked file is kept. Any mismatch deletes both and the file is fetched again. A context can be used once its own files and the shared ones are all present and verified.
The CPU engine is always there. An NPU program downloads only after the CPU path works on the device; if it fails to load or to pass its self-test, the SDK stays on the CPU, which gives the same result bit for bit, and the fallback reason is counted in the daily analytics.
Limits
Madd lazim and shadda (gemination) are off in V4. The qalqalah check, for a missing bounce, is experimental: at the default 320 ms context it flags about 2.9% of correct qalqalah sites. Accuracy is weakest on children and on phone audio: there Balanced flags 3 to 7% of sites per check family (an upper bound, from recordings not certified correct), against 0.3 to 0.5% on studio recitation. At Balanced, on 16 reciters the model never saw, 0.3% of correct vowels and 0.5% of correct letters are flagged, while 46% of non-native learners' letter mistakes and 32% of real students' vowel mistakes are caught. Fewest false flags halves the false flags; Catch the most catches 60 to 71% at about 2% false flags.
6. Recording sharing
Learners can give the audio of their recitations to Quran Lab, to help the open recitation models listen better: a sadaqah jariyah for those who choose it. There is nothing to switch on in your app. Quran Lab decides, per app and platform, when learners are asked, and the SDK asks them with its own sheet. Nothing is recorded until a learner says yes, and you never see the recordings.
- Who decides. Quran Lab, from its side, in the signed config each install reads: whether an app's learners are asked at all, on which platforms. Since SDK 2.0.1 the old
contributions option is ignored; leave it out of new code. - The ask. The SDK's default sheet, in the learner's language (English, Arabic, Indonesian, Urdu, Turkish, Malay, French, Bengali), after a session with feedback, never at the first launch. "Share my recitations" and "Not now" have the same weight; "Not now" is asked again at most twice, at least 7 sessions apart. In an app for children (the SDK's audience set to children) the sheet asks a parent or guardian. You may choose the moment (each SDK's
askNow), and change how it looks (below). Your app's page in the portal shows the sheet in every language. - What is shared. Only after a yes, and while sharing stays on: the audio of each recitation session while the SDK listens (compressed as Ogg Opus), the ayat recited, the model version, the device family, the OS major version and the day, under a random contribution code. No name, account, contacts, location or device identifier.
- Where it goes. Straight from the device to Quran Lab's storage in the EU, uploaded in the background and resumed after a restart. Quran Lab is a waqf: the models it trains are free and never sold. You, your key and your app cannot open, download or delete the recordings. Your app's page shows counts only: how often learners were asked, how many shared or declined, and the minutes shared, each from 10.
- Stopping and deleting. Turning the switch off stops uploads at once. "Delete what I shared", in the SDK's settings row, removes everything that contribution code shared from Quran Lab's servers. Only the learner can do it: the request is signed by a key that never leaves their device. A learner who lost the device uses the one-time recovery code the SDK showed them at portal.quranlab.ai/delete.
- Where it works. In every SDK. Apple apps prove their install with App Attest and Android apps with Google Play Integrity. Web pages and Windows or Linux apps cannot prove theirs, so they share at a lower trust level: fewer sessions a day per app and per learner, and their sessions are marked so training can weigh them apart.
Make the sheet look like your app
Two ways, both keeping the learner's choice the same: theme the default sheet (colours, fonts, corners), or show a screen of your own built from the SDK's copy strings, the same lines in every language the SDK ships, and pass the learner's answer back to the SDK. Keep both buttons the same weight and nothing pre-ticked.
If you want different wording, tell your coding assistant: it records your text with your app's declaration (sharing_notice, through the setup API) and Quran Lab reviews it. Without a review, keep the SDK's lines as they are.
Apple
// SwiftUI, once near the root: the SDK presents its sheet here, themed
ContentView()
.quranLabSharing(theme: SharingTheme(accent: .teal, cornerRadius: 20))
// UIKit: SharingConsentViewController(theme:), AppKit: SharingConsentViewControllerMac(theme:)
// A screen of your own: not in SDK 2.0.1, which has no presenter hook yet.
// Contributions.copy.string("sheet.title") already gives the deck's lines.
Android
// Compose, once near the root of the UI
SharingSheetHost(style = SharingStyle(accent = Teal, shape = RoundedCornerShape(20.dp)))
// Views: SharingDialogs.show(activity, sharing, request, SharingTheme(accent = teal))
// Your own screen: take the requests, show the deck's lines, answer
QuranLabAndroid.sheets = { request -> showMySheet(request) }
val sharing = QuranLabAndroid.cloud?.sharing
sharing?.copy?.get("sheet.title")
sharing?.accept() // or sharing?.decline()
Web
/* <quranlab-consent>, the SDK's sheet, reads CSS custom properties */
quranlab-consent {
--ql-bg: #fff; --ql-fg: #1d1d1f; --ql-muted: #6e6e73;
--ql-accent: #0f766e; --ql-accent-fg: #fff;
--ql-radius: 20px; --ql-font: inherit;
}
import { enableSharing, copyFor } from '@quran-lab/tajweedformer/consent';
const deck = copyFor('ar'); // deck.t('sheet.title'), deck.t('sheet.share'), ...
enableSharing(ql, { ask: async (a) => showMySheet(a, deck) }); // resolves to { yes, guardianConfirmed }
Native (C)
/* The C API has no drawn sheet: your app shows it */
TfClientOptions o = tf_client_default_options();
o.on_consent = on_consent; /* TfConsentRequest: reask, guardian, copy_json (the deck) */
/* ... after the learner answers: */
tf_client_sharing_answer(client, 1); /* 1 share, 0 not now */
Exact names for your SDK version are in its README. On Apple, SDK 2.0.1 presents its own sheet (themed with SharingTheme) and has no hook yet for a screen of your own.
Analytics and privacy
The SDK sends daily sums and histograms, never audio, text or identifiers, and this portal hides any group under 10 installs. Privacy lists every field and what to declare in the App Store and Google Play forms.
Apps on SDK 1.x
Apps built with SDK 1.x keep working and keep receiving their current model. SDK 1.x never receives TajweedFormer V4: to use it, update to SDK 2.0.0. Your key stays the same.