Work offline
A Qur’an app is expected to work on a plane. This page is the sizes of what each block ships, so you can decide what goes in the bundle, what is fetched once and cached, and what never belongs on a phone at all.
The rule of thumb
One edition of the text plus its font is about 3.4 MB and answers every question about the words. A rendered page is 38 KB in the engine, 196–613 KB as SVG. Ship the text; ship pages by demand; never ship the 50 MB word index.
What one file weighs
Measured from the copies this site serves:
| Block | File | What it is | Size |
|---|---|---|---|
| Quran Text | fonts/hafs.woff2 | the Ḥafṣ font, converted to woff2 for the web (the TTF the dataset ships is 301,396 bytes) | 105.1 KB |
| Quran Text | fonts/warsh.woff2 | the Warsh font — a second riwayah means a second font | 86.7 KB |
| Quran SVG | svg/001.svg | page 1, glyph outlines plus the ayah hit-layer, uncompressed | 191.7 KB |
| Quran SVG Elements | pages/001.svg | page 1 split into words and marks, uncompressed | 219.6 KB |
| Quran Engine | 001.qvp | page 1 as the engine's binary | 37.4 KB |
| Quran Engine | 001.words.json | page 1's text sidecar, five forms per word | 4.2 KB |
| Quran Engine | qvp_ffi.wasm | the engine itself, built from source for this site | 304.2 KB |
| Quran Assets | assets-surah-headers-mushaf-qalon-color.svg | one scan-traced surah header, full colour | 26.2 KB |
| Quran Assets | assets-ayah-markers-font-001-regular-color.svg | one font-derived ayah marker, full colour | 10.2 KB |
The rest, from the source repositories at the commits pinned in public/demo/SOURCES.json, on 11 September 2026:
| Block | Artefact | Bytes | Notes |
|---|---|---|---|
| Quran Text | data/mushaf/hafs.json | 3,110,420 | one edition; the npm package bundles the same data minified at 2,529,558 |
data/mushaf/warsh.json | 1,912,438 | the seven range from 1.9 to 3.1 MB | |
data/mushaf/hafs.nested.json.gz | 1,756,658 | the same edition as surah → ayah → words, gzipped | |
data/fonts/UthmanicHafs-v-3.0.ttf | 301,396 | one font per edition; 247–301 KB each as TTF | |
data/ayah-map.json | 2,285,199 | converts references between the seven editions without loading them | |
data/word-index.json | 50,335,755 | servers and build steps — the repository says so | |
| Quran SVG | hafs/kfqc/svg/042.svg | 613,372 | a full page; page 001 is 196,304, page 300 is 591,875 |
hafs/kfqc/svg-br/042.svg.br | 106,450 | the same page Brotli-compressed — ship these and serve Content-Encoding: br | |
all of hafs/kfqc/svg-br/ | 74,487,813 | 722 files: 604 pages plus surah-specific variants; uncompressed 432,448,834 | |
| Quran SVG Elements | quran-svg-hafs-kfgqpc.tar.gz | 107,920,115 | the only release artefact: the whole corpus, to obtain one page |
| Quran Engine | quran-engine-pages-hafs-kfgqpc.tar.gz | 44,093,442 | 604 .qvp pages, sidecars and atlas; 92.5 MB unpacked, 38.9 MB under Brotli per the README |
atlas.qva | ≈10 KB | which page an ayah is on, surah metadata, juz and hizb — the README’s figure | |
| Quran Tajweed | tajweed-annotations-uthmani-hafs-v0.4.0.json | 1,806,369 | every span for the whole muṣḥaf, as positions |
tajweed-rules-v0.4.0.json | 96,939 | the rule corpus | |
| Qiraat Ayah Map | data/counting-systems.json | 2,878 | the six systems; the boundary primitives are 51,714 |
Decide what goes where
| Ship inside the app | Fetch once, cache by digest | Never on a device |
|---|---|---|
| The default edition and its font (Ḥafṣ: 3.1 MB + 301 KB) | Other editions the user turns on, each with its own font | word-index.json — 50 MB; use it at build time to precompute what you need |
ayah-map.json if bookmarks must resolve across editions offline | Engine pages, by page or by juz, as the reader moves | The 108 MB Elements tarball — split it on a server and serve pages |
| The engine itself — 280 KB of wasm, or the native library | Tajwīd annotations, once, tied to the edition digest they were computed for | Uncompressed SVG pages — ship the .br copies |
Warsh, Qālūn and al-Sūsī use code points that almost no general font draws. Cache warsh.json without
UthmanicWarsh-v-3.0.ttf and the download succeeds, the digest matches, and the words render as boxes. Each muṣḥaf
file names its font in its own font block; fetch the two together.
Choose the unit of download
Quran Text has no per-surah file: a surah, a page and a juz are slices of one array, so the smallest download for one ayah is the whole edition, and at 1.9–3.1 MB that is the right unit. For rendered pages the unit is one page (
NNN.qvpin the engine,NNN.svg.brfor Quran SVG) and a juz is a sensible batch.Verify every download against its digest
Each block publishes the digest differently, and the check is one hash either way:
Block Where the digest is Quran Text data/manifest.json—files[].sha256for every emitted fileQuran Engine quran-engine-pages-hafs-kfgqpc.tar.gz.sha256beside the release assetQuran SVG Elements CHECKSUMS.txtinside the bundle —sha256sum -c CHECKSUMS.txtQuran Tajweed edition.sha256inside the annotations file names the text the spans belong to// After fetching data/mushaf/warsh.json into `bytes`, against data/manifest.json const expected = manifest.files.find((f) => f.path === "data/mushaf/warsh.json").sha256; const hash = await crypto.subtle.digest("SHA-256", bytes); const actual = [...new Uint8Array(hash)].map((b) => b.toString(16).padStart(2, "0")).join(""); if (actual !== expected) throw new Error("warsh.json: digest mismatch — do not cache"); // expected: 2134d90016ecd26543ca6eaef21427227ec7641d667b0fc14fbb8e4c3f6dbd3aCache under the digest, not under the URL — a cache key names everything the value depends on (Engineering, rule 8.1). A file that hashes the same is the same file; a file that hashes differently is a new edition of the text, and everything derived from the old one (search caches, tajwīd offsets, word-level bookmarks) is suspect.
Keep the provenance with the cache
Store
provenance.text.sha256, the provenance of each cached muṣḥaf, beside your own data. It lets a bookmark written against one build be checked against another; see Store bookmarks and progress.Check for updates, and know what the check does today
The JavaScript library has an update check built in. It never throws and never rejects: it resolves to
nullwhenever it cannot get an answer.const status = await m.checkForUpdate(); // default url: Mushaf.VERSION_URL, 5 s timeout // { edition, upToDate, localSource, latestSource, dataset, downloadUrl } — or nullIt resolves to null in production right nowMushaf.VERSION_URLishttps://quran.ws/version, and the result’sdownloadUrlis built onhttps://quran.ws/files/. On 11 September 2026 both paths return an HTML page, not aformat: "quran-version"document, socheckForUpdate()returnsnullfor every edition. Treatnullas “unknown”, not as “up to date”, and do not build an update flow on this until the endpoint exists. Recorded indocs/dx/quran-text.md.Until then, the working update check is the one in step 2: re-fetch
data/manifest.json, compare the digest of the file you hold, and download when it differs.
What is not offline
Quran PNG is an HTTP service and has no offline form. Inside an app that already
holds engine pages, page.cropSvg() gives the same artwork with no network.
Page 001 as a 38 KB engine binary, drawn in your browser — the size in the table above, rendered.