Mushaf

A Quran reader for the web that shows the printed Mushaf itself: 604 page images, every ayah on them tappable, and pages that turn like a book.

A printed Mushaf is read by the page, so the reader shows the page by default rather than reflowed text, and does the work around it. It knows where every ayah sits on each image, turns pages without losing your place, and checks what is already in the browser before asking anyone else.

Open Mushaf

Plays the six stages below in order, about 55 seconds. Scroll or press Esc to stop.

Built with
Angular 20 and Ionic 8, server-rendered
Hosting
AWS Amplify
Pages
604 images, 1260 px wide
Text
Uthmani script, from alquran.cloud
Ayah geometry
13,766 line boxes, bundled with the app
Ads
None, and no analytics code
curl -s https://mushaf.geohashim.com/tabs/mushaf/page/1 | grep -oE '<title>[^|]*|og:image" content="[^"]*' <title>الفاتحة - صفحة 1 og:image" content="https://android.quran.com/data/width_1260/page001.png

Real output from the live site, October 2026. The tap session runs the reader's own hit-test code offline on the bundled geometry; the tap positions are illustrative.

1Find the page

Every way into the reader ends at the same address, /tabs/mushaf/page/n, with n from 1 to 604. The index, the search results, bookmarks and shared links all resolve to a page number first.

  • The juz list is a table of start pages that ships with the app, so opening a juz needs no request.
  • Ayah search is the one way in that needs a request: it asks alquran.cloud, then looks up the exact page of each of the first 30 matches. The one you pick opens with that ayah highlighted.
  • Jump to page accepts 1 to 604. Anything else closes the dialog and leaves you where you were.
  • The server names each page before it reaches the browser, from the same bundled geometry, and sets the link preview image to that page.

604 pages, 30 juz, one route

30 of 30 juz start pages match the bundled geometry

  1. Juz 30 in the index/tabs/mushaf/page/582Table lookup, no request
  2. Jump to page 300 Illustrative input/tabs/mushaf/page/300Inside 1 to 604, opens
  3. Jump to page 605 Illustrative inputno changeOutside 1 to 604, the dialog just closes
  4. A shared link/tabs/mushaf/page/2Titled on the server: البقرة - صفحة 2
Juz start pages are the reader's own table, each checked against the first ayah of that juz in the bundled geometry file. The page 2 title is the live server's output. The jump inputs are illustrative; the 1 to 604 check is the reader's.

2Know where every ayah sits

The page is a picture, so the reader carries a map of it: one box per ayah per line, in the image's own 1260 px coordinates. The map loads once, when you first open a page in image view, and is used every time you tap.

  • A tap is scaled into page coordinates, then tested against the boxes. Inside a box wins. Otherwise the nearest box wins, but only within 50 px of the 1260 px page, about 13 px on screen when the page is drawn 330 px wide.
  • An ayah that runs over several lines has a box on each, and all of them light up together.
  • Highlights are placed as percentages of the image, so they stay on the ayah at any zoom from 50 to 200 percent and on any screen. Recitation and search results use the same boxes.

Ayah keys like 1:7 stand in for the text here; the real page image carries the words.

Taps against the bundled geometry Illustrative input

Tap or click the page to run the same test.

Pages
604
Line boxes
13,766
Ayahs
6,236
This page
10 boxes
Tolerance
50 px
Boxes are the reader's bundled ayahinfo.json. The hit test and the highlight maths are the reader's own, copied verbatim and run in this page, with page images taken as 1260 by 2038 px, the size measured on four sampled live pages. Tap positions are illustrative; the first four match an offline run of the same code.

3Turn the page like a book

On a wide screen the reader shows two pages at once, paired as in print: the odd page on the right, the even page on the left. Open an even page and its odd partner becomes the main page.

  • In two-page view of the page images, a turn is a 3D curl in six segments. The free edge leads while you drag, the spine takes just over half the final rotation so the whole sheet ends past vertical, and the turn commits past 60 degrees or springs back.
  • A button turn runs the full half circle in 500 ms with ease-in-out; a release finishes the rest of the way in proportion, never under 200 ms.
  • The address updates with replaceState, so turning pages adds no history entries and the URL always points at the page on screen.
  • In single-page view, a swipe needs more than 50 px of travel and 1.5 times more sideways than vertical movement, and the current page stays on screen until the next page's text has arrived.

Side view of a turn from pages 3 and 4 Illustrative input

Angle0°
On screen3 | 4
Address/tabs/mushaf/page/3

Drag across the book to turn the page.

The curl is drawn with the reader's own segment formula and easing, side on. The drags are illustrative; the 60 degree commit, the 500 ms and 200 ms timings and the page pairing are the reader's.

4Check this browser first

Before a text request leaves the browser, the reader's service worker looks in its own cache. A reply it already holds is served from there, with no network at all. Only a miss goes out to alquran.cloud.

  • The rule is performance: a cached reply up to 30 days old wins. Older ones are dropped and fetched again.
  • It keeps the 100 most recently used replies. When a new one arrives with the cache full, the least recently used goes. Search, translation and tafseer replies share those 100 places.
  • The worker is installed during the first visit, so requests made before it takes over go straight to the network.
  • Page images are not in this cache. They come from quran.com's CDN, which marks them cacheable for about 296 days, so the browser's own cache can answer repeats.

The text cache through a reading session Illustrative input

/v1/page/1/quran-uthmani
Requests
0
From this browser
0
From alquran.cloud
0
Evicted
0

Single-page view: read pages 1 to 110 in order, go back to page 105, then back to page 4.

The session is illustrative. Its handling follows the live site's service worker exactly: performance, 100 entries, 30 days, least recently used out first. On the live site, opening page 1 and pressing next left exactly two entries, /v1/page/1/quran-uthmani and /v1/page/2/quran-uthmani.

Not in this browserAbove this line is what ships with the app or is already cached. Below it, the reader asks third parties: alquran.cloud for the text, quran.com's CDN for the page image.

5Fetch the text, one page at a time

One request per page, for the Uthmani text of exactly the ayahs on it. In two-page view, a second request fetches the facing page.

  • The reply carries ten fields per ayah. The reader keeps five: its number, its number in the surah, the juz, the surah, and the text, which feeds copy, share and the text views.
  • Its page boundaries agree with the bundled geometry: the same ayahs, page for page, on all six pages sampled.
  • If the request fails in image view, the reader still shows the page image and raises no error. In the text views it says so, with a retry button.

The reply, and what the reader keeps

GET api.alquran.cloud/v1/page/2/quran-uthmani

    Same ayahs on both sides

    Request fails, image viewThe page image still shows, and no error.
    Request fails, text viewsFailed to load page and a Retry button
    Field names are from the live API's reply for page 1; the kept five are the reader's own mapping. Ayah counts per page were compared on the live API and the bundled geometry in October 2026. The failure messages are the reader's code; the text itself is elided here.

    6Draw the printed page

    The page image is the default view: page001.png to page604.png, 1260 px wide, from quran.com's CDN, which serves them from its Hafs Madani set. The ayah boxes from stage 2 sit on top as highlights.

    • Opened on a screen 1024 px or wider, the reader starts in two-page view unless you have chosen a view yourself. The check runs when the reader opens, not on every resize.
    • At 768 px wide and below, the zoom and two-page buttons are hidden and the page fits the screen.
    • The stacked page edges beside a spread thin out near the covers: one edge line per page still to come on that side, up to three.

    Page 1 opened on three screens, with ayah 1:7 highlighted Illustrative input

    Line boxes from the bundled geometry stand in for the page images. The layout rules and the 1:7 highlight percentages are the reader's code and its real output; the screen widths are illustrative.

    Measured on the live site

    The repo documents no benchmarks. These are byte counts from the live site, its two third-party sources and the bundled data, taken in October 2026.

    What a page costs

    The image is the cost. The text reply is one to two percent of it.

    Page 50 image126 kB
    Page 1 image40 kB
    Page 50 text1.4 kB
    Page 1 text0.7 kB

    Images 40,445 to 125,578 bytes; text replies 692 to 1,393 bytes compressed, over four sampled pages.

    What ships once

    The geometry for all 604 pages is one JSON file, fetched once and kept.

    As stored346 kB
    As sent121 kB

    346,322 bytes, sent as 121,179 with brotli. The service worker also prefetches the 95 files of the app shell.

    What the server sends

    The page list is prerendered. Each page is rendered on the server when asked, so a shared link arrives titled, with its own preview image.

    Index: prerenderedPage: server-rendered

    A rendered page is about 40.5 kB of HTML. If server rendering takes over 10 seconds, the server sends the client-rendered shell instead.

    Deliberately missing

    Zoom and two-page view on phones
    Their buttons are hidden at 768 px wide and below.
    Swipes in some places
    Ignored within 20 px of the screen edge, which belongs to the iOS back gesture, and while the page is zoomed in.
    An unbounded text cache
    The last 100 replies, up to 30 days old, and no more.
    Long search result lists
    The first 30 matches, each needing its own page lookup.

    Try it

    • Open the index of surahs and juz at mushaf.geohashim.com/tabs/mushaf.
    • Go straight to any page: /tabs/mushaf/page/1 to /tabs/mushaf/page/604.
    • On a screen 1024 px or wider, pages open as a two-page spread. Drag across it to turn the page.
    • Tap an ayah on the page to play, copy, share, bookmark, translate it or read its tafseer.

    A first visit starts with a short welcome screen.

    Open Mushaf