I built a Quran audio project where the same JavaScript generates both a static website and an Android app, with no runtime dependencies in the generator. Here is how it is structured and what I would do again.
The result is called Suwar: one page per surah, in Arabic and English.
The constraint
I wanted every one of the 114 surahs to have its own indexable page, in two languages, plus a mobile app that reuses the same interface. That is 228 pages. I did not want a framework build step to maintain for something this simple.
The structure
- Data: a fetch script pulls the text once and validates it (114 surahs, 6,236 verses) before anything is generated. If the counts are off, the build stops.
-
Generator: plain Node.js, no dependencies. It writes one HTML file per surah per language, plus a sitemap,
hreflangalternates, JSON-LD, and anllms.txtfile. - App: Capacitor wraps the same built UI as an Android project. The audio player uses MediaSession (repeat, speed, sleep timer, resume).
- Tests: run with Node and a headless Chromium, so the generator can be checked without an Android SDK.
What worked
- Validating data before generating. A wrong verse count fails the build instead of shipping bad pages.
- Keeping the generator dependency-free. It runs anywhere Node runs, which mattered because I develop on a machine where package installs are not always possible.
- One UI for web and app. Fixes land once.
What I would watch for
- Attribution and licensing: the text comes from Tanzil under CC BY 3.0, so the source must be credited and the text must not be modified.
- Audio rights are separate from text rights. Each reciter is configured with a
licensedflag so that any of them can be removed at once. - Ads: a small banner on regular screens and an optional rewarded ad. None during playback.
If you build content sites with many near-identical pages, the validate-then-generate pattern is the part worth copying.
Top comments (0)