Showing the bundled guide — checking GitHub for the latest version…

Indic Reader

🌐 Live app: https://indic-reader.pages.dev/

A single-file web app for reading, narrating, and translating Indic-script scriptures (Sanskrit, Hindi, Gujarati). Works in any modern browser. Everything runs locally on your device by default — no server, no tracking, no cookies. Cloud-based features (OCR, translation, dictionary) are optional and opt-in with explicit consent.


Table of contents

  1. Quick start
  2. Loading documents
  3. Reading & narration
  4. Full-screen immersive reading
  5. Layouts (A / B / C)
  6. Language filter
  7. Outline / table of contents
  8. Word meanings & translation
  9. Split Sandhi (word separation)
  10. Correcting language & text
  11. Vedabase deep links
  12. OCR for scanned documents
  13. Re-OCR a region
  14. Cloud OCR setup
  15. Google Drive integration
  16. Settings & preferences
  17. Keyboard shortcuts
  18. Privacy & data handling
  19. Troubleshooting
  20. Known limitations

Quick start

  1. Open index.html in any modern browser (Chrome, Firefox, Safari, Edge — desktop or mobile)
  2. Drag a file onto the drop zone, or tap 📁 Choose Files
  3. For PDFs, select page range, tap ▶ Process Pages
  4. Use the ▶ Play button to narrate, or scroll/tap to read

For scanned PDFs (image-only), turn on Force OCR before processing.


Loading documents

Five ways to load content:

Method When to use
📁 Choose Files Pick files from your device — most common path
📂 Browse Open a folder on desktop (File System Access API); falls back to multi-file picker on mobile
🟢 Drive Pick from Google Drive (requires one-time OAuth setup — see Drive integration)
✍️ Paste Text Paste Sanskrit/Hindi/Gujarati text directly
Drag & drop Drop files anywhere in the page
Drive share link Paste a public Drive URL — file must be set to "Anyone with the link can view"

Supported file types: PDF, DOCX, TXT, RTF, HTML, XML, images (JPG, PNG, etc.)

Pasting formatted text

When you paste from a rich source (a web page, a document editor), the app reads the clipboard's HTML so paragraphs, line breaks, and bullet lists are preserved rather than flattened — bullets are normalized and each paragraph keeps its own line. A small indicator tells you whether rich formatting was detected. If a source only provides plain text (some apps strip formatting on copy), a ¶ Restore paragraphs button reconstructs structure by breaking at sentence ends and before section numbers, and recognizes a wide range of bullet markers. Pasted text starts a clean document, so it never disturbs a PDF you already have open (its reading position and OCR cache are preserved). Structure carries through to both Text View and Document View.


Reading & narration

Two views, switchable via the tabs at the top of the reader:

Player controls (bottom bar)

Button Action
Previous line / verse
▶ / ⏸ Play / pause narration
Stop narration
Next line / verse
Page chip Tap to jump to a specific page

Narration chips (floating player)

Reading-time options sit on the floating player as compact chips that highlight when active (tap to toggle), kept on a single line:

⚙️ Settings panel

Set-and-forget options live in a single ⚙️ Settings panel (button beside Library), grouped into OCR, Playback, Reading layout, and Document, so the reading surface stays uncluttered:

Full-screen immersive reading

A ⛶ Full screen tab puts the reader into a distraction-free mode showing only the current view (Text or Document) with a single floating control bar — minimise ⤢, ⏮, play/pause, ⏭, a Text/Document toggle, and a language filter. The app header, upload area, settings, and all editing tools are hidden. Document View fills the entire screen in this mode (the normal half-height cap is removed). The control bar stays visible while you read, and the app enters full-screen automatically once a page is processed, so you go straight into reading. Exit any time with . On Android Chrome this also engages the browser's true full-screen; on iOS it relies on the immersive layout (and is fully chrome-free when the app is added to the Home Screen).

Per-verse translation narration

Each translated verse block carries its own 🔊 button next to the translation text — tap it to hear that verse's translation read aloud in the right voice for the target language, independent of the main narration.

Background & car playback

While narrating, the screen is held awake so playback doesn't stop when the display would otherwise sleep. With a phone connected to a car by Bluetooth, narration plays through the car speakers and the basic steering-wheel/head-unit play/pause may control it. Turning on 🔋 Screen-off play keeps a silent media session alive so lock-screen transport controls appear and playback continues when backgrounded — but because that media session competes with the speech channel, it can make narration less stable on Android Auto; leave it off for the most reliable in-car audio. A web app cannot appear as a native app on the Android Auto / CarPlay projected screen — that requires a native build — so use Bluetooth audio mode in the car.

Line numbers

Text View shows a muted line number in the left gutter of each line, to make it easy to reference a line for correction. The numbers are drawn so they're never included when you select or copy text.

Speed chips (per language)

Narration speed is set per language (Gujarati, Hindi, Sanskrit) with three compact speed chips showing the current value (e.g. ગુ 1.0×). Tap a chip to expand a fine-tune slider for that language; tap again to collapse. Sanskrit chanting is traditionally slower; Hindi/Gujarati prose can be faster. The default is 1.0× for all three (existing saved preferences are kept). The Sanskrit chip drives both plain reading speed and chant-mode pacing across its full range.

Screen wake lock

While narrating, the screen stays on automatically. When you stop narration or close the tab, it releases. Requires HTTPS; some browsers (older Safari) don't support it and screen behavior falls back to OS default.

Chhand (verse metre) & scansion

Each Sanskrit verse shows a metre pill, e.g. 📐 Śārdūlavikrīḍita · 38 akṣ. Metre is identified two ways:

A verse has two related, separate controls:

The tones are a recitation aid (not canonical Vedic svara — laukika verses carry no textual accent); the guru/laghu marks are the rigorous scansion. Both views share the same scansion engine, so the marks always agree.

Syllables display with their full conjunct spelling: a consonant cluster that opens a word (e.g. the स् of स्वर्ण, the क् of क्षेत्रेषु) is shown attached to the syllable it begins (स्वर्, क्षेत्), while a cluster in the middle of a word closes its syllable (वर्). This is a display refinement only — the heavy/light weights and metre detection are unchanged.

Honest limit: scansion is only as accurate as the text. OCR errors (a wrong vowel length, a dropped conjunct) shift the marks, so verify against a clean source for serious study. The metre also shapes chant-mode recitation pacing.


Layouts (A / B / C)

Top of the reader has a layout selector. Choose what suits your reading style:

Choice is saved in your browser; persists across sessions.

Switching to B or C the first time will prompt for consent (translation sends text to Google Translate's public endpoint).


Language filter

Buttons above the text: All / ગુ Guj / સં Sans / હિ Hin


Outline / table of contents

For DOCX files, the 📑 Outline button appears in the reader header. Tap it to see the document's structure as a navigable list.

The outline shows different things depending on the document:

Strategy When used What you see
Headings DOCX uses Word's Heading 1/2/3 styles Hierarchical TOC indented by heading level
Author page breaks DOCX has explicit page breaks (Ctrl+Enter in Word) "Page 1, Page 2, …" matching author intent
Auto-chunked Document has neither headings nor page breaks "Chunk 1, Chunk 2, …" — each ≈500 words

Tap any entry to scroll the reader to that section. Brief highlight pulse shows where you landed.


Word meanings & translation

Show Meanings (Sanskrit only)

Below each Sanskrit verse, tap Show Meanings to look up each word:

  1. Queries the Cologne University Monier-Williams dictionary (the authoritative Sanskrit lexicon)
  2. Returns English definitions, then auto-translates them to Gujarati
  3. Cached per-session — repeated lookups are instant

Source badge per word:

Translate verse

Below each line, tap 🌐 Translate verse to get full-verse translations:

This sends the verse text to Google Translate's public endpoint. Disabled by default; first use prompts for consent.

Translate page (Gujarati ↔ Hindi)

In Text View, three buttons in the controls row translate the current page, always leaving Sanskrit verses untouched:

The translation appears as a block under each translated line (the original stays, so narration and study are unaffected). Each block has its own 🔊 button to read that verse's translation aloud on demand. The translate controls appear only in Text View. Tap the same translate button again to hide. Sanskrit lines are never sent. Results are cached on-device. Uses Google Translate's public endpoint under the same consent as Translate verse. To have translations spoken automatically as part of continuous narration, enable 🔊 Read translation in narration in Settings.

Bookmark notes

Tap the ☆ on any line to bookmark it. In the Library, each bookmark shows the full line text (not just a snippet), and you can attach a note to it (tap "+ Add note"). Notes and the full text are stored on-device.


Split Sandhi (word separation)

Sanskrit verses fuse words together through sandhi (e.g. तपोवनम् = तपस् + वनम्). The ✂️ Split Sandhi button (on every Sanskrit verse, next to Show Meanings) separates them.

How it works — accuracy first, with honest fallback:

  1. Real morphology — it first sends the verse text to the University of Hyderabad Heritage segmenter, an academic Sanskrit morphological analyzer, routed through your own Cloudflare Worker (the same one used for Cloud OCR). The badge shows "University of Hyderabad Heritage segmenter ✓" when this succeeds.
  2. On-device fallback — if the service is unreachable, it falls back to a built-in rule-based assistant that handles the reliable cases (avagraha , visarga sandhi). The badge then says "Guided assistant".

The overlay lets you edit the result directly:

Honest limitation: Sanskrit segmentation is inherently ambiguous — even the academic engine returns the ranked best analysis, not a guaranteed-correct one. And per-word translations come from Google Translate, which is weak on isolated/inflected Sanskrit words — treat the inline translation as a quick gloss and the tap-for-dictionary (Monier-Williams) as the more reliable source. The split/merge editing exists precisely so you have the final say. Treat all of it as a study aid.

Setup: Split Sandhi reuses your Cloud OCR Worker URL. If you haven't set that up (see Cloud OCR setup), it uses the on-device assistant only. Per-word meanings require online lookups to be enabled (same consent as Show Meanings).


Correcting language & text

Automatic script detection is good but not perfect, especially on OCR'd pages. Two manual overrides give you the final say, and both persist (saved in your browser):

Language overridelong-press any word (or right-click on desktop) to open a picker:

Use this when a Sanskrit verse is being read in the Hindi voice, or a word shows the wrong dictionary.

Text correction — when OCR garbles a verse, use Re-OCR a region (in Document View) to fix the actual text. Corrections are keyed to the original text and survive re-processing.


For recognized scriptures (Śrīmad-Bhāgavatam, Bhagavad-gītā), each verse gets a 📖 link to the official Vedabase entry:

Manual reference entry (when auto-detection fails): if a Sanskrit verse has no working link, it shows a 🔗 Set reference button. Tap it to enter the details by hand:

Your manual references are saved (keyed to the verse text) and persist across reloads and re-processing. Use ↺ Clear my reference in the dialog to remove one.

Vedabase content is BBT-copyrighted; we deep-link only, never copy text.


OCR for scanned documents

OCR (Optical Character Recognition) extracts text from images and scanned PDFs.

When to use OCR

File type OCR needed?
Digital PDF with embedded text No — text extracted directly (perfect accuracy)
Scanned PDF (image-only) Yes — turn on Force OCR
JPEG / PNG of a page Yes — runs automatically
DOCX / RTF / TXT / HTML No — text already structured

Force OCR toggle

Located near "Clear All" on the main screen. Turn ON when:

Local OCR (default)

Uses Tesseract.js running entirely in your browser. Free, private, works offline once language data is cached. Slower (~5–15 seconds per page) and less accurate than cloud OCR on poor scans.

Language data (~5MB for Gujarati + Devanagari) is downloaded from tessdata.projectnaptha.com on first OCR, then cached.

OCR quality indicator

After processing, a small badge near each source shows which engine processed it:

A confidence banner appears for low-quality results (< 60% confidence) — verify against the original page.

You can tap the engine badge any time to see a per-page diagnostic: which engine OCR'd each page, your current settings, and why cloud failed (if it did).


Re-OCR a region

When OCR mis-reads part of a page — most commonly an inline Sanskrit verse embedded in Gujarati/Hindi prose — you can re-OCR just that area instead of the whole page.

In Document View, each page has a 🔍 Re-OCR a region button. It is pinned to the top of the page (sticky), so it stays visible no matter how tall the page image is — you never have to scroll to the bottom to find it.

  1. Tap it, then drag a box over the text that was mis-read
  2. A dialog shows the cropped image and runs OCR automatically
  3. Choose the language for that region — Sanskrit only (default, best for verses), Gujarati only, Hindi only, or Auto
  4. Choose the engine — ☁️ Google Vision (if configured) or 📱 Local
  5. Edit the result by hand in the text box if needed
  6. Apply it in one of three ways (below)

Three ways to apply the result:

All edits and removals are keyed to the original text and persist across re-processing.

Why this helps: OCR engines assign one dominant script per visual line, so short inline Sanskrit gets misread as the surrounding script. Cropping just the verse and forcing Sanskrit-only removes that conflict and usually reads the verse correctly. If the scan is poor, you can also just type the correction directly — re-OCR is optional.

This works with local OCR too — no cloud account needed for the on-device option.

Fix a single line from Text View

You don't have to go to Document View to fix one line. In Text View each line has a small ✏️ button (next to the ☆); it glows amber when the line contains low-confidence words. Tapping it opens Fix line:

(Re-OCR needs the line's position on the page image, so it's available for OCR'd PDF/image pages; for pasted or native-text PDFs the dialog offers manual edit only.)

Low-confidence flagging

With ⚠️ Check OCR on (Prefs strip), words the OCR engine reported low confidence for get a wavy amber underline (a tooltip shows the confidence). Both Google Vision and local Tesseract provide per-word confidence; it's stored in the OCR cache so flags persist on reopen.


Cloud OCR setup

Cloud OCR significantly improves accuracy on poor scans, photos, and books with marginalia. Optional and opt-in — disabled by default.

Why a proxy?

Google Cloud Vision blocks direct browser calls (CORS). The app uses a free Cloudflare Worker as a proxy between your browser and Google. Your API key lives on Cloudflare as an encrypted secret — never exposed in browser source code.

One-time setup (≈ 10 minutes)

Two parts:

Part 1: Get a Google Cloud Vision API key

  1. Go to console.cloud.google.com
  2. Create a new project (e.g. indic-reader)
  3. Billing → Link a billing account (free tier covers 1,000 calls/month at $0, but a card is required)
  4. Billing → Budgets & alerts → Create budget: set amount to
    with default thresholds — this emails you BEFORE any charge
  5. APIs & Services → Library: search "Cloud Vision API", click ENABLE
  6. APIs & Services → Credentials → + Create credentials → API key
  7. Copy the key. Click "Edit API key":
    • API restrictions: Restrict key → check ONLY "Cloud Vision API"
    • Application restrictions: None (Worker calls don't preserve referrer)
    • SAVE

Part 2: Deploy a Cloudflare Worker

  1. Go to dash.cloudflare.com → Workers & Pages
  2. Create → Hello World → name it (e.g. indic-ocr-proxy) → Deploy
  3. Open Worker → Edit code → delete the template → paste the entire cloudflare-worker-google-ocr.js file
  4. Save and Deploy
  5. Worker → Settings → Variables and Secrets, add two:
    • GOOGLE_API_KEY = (your key from Part 1) — mark as Encrypt (Secret)
    • ALLOWED_ORIGIN = your app's URL (e.g. https://indic-reader.pages.dev) — use * only for testing
  6. Save

Part 3: Configure the app

  1. In the app, tap ⚙ Cloud OCR (gear icon near Cloud OCR toggle)
  2. Provider: ⭐ Cloudflare Worker → Google Vision
  3. Paste your Worker URL (e.g. https://indic-ocr-proxy.YOUR.workers.dev)
  4. Tap 🔍 Test connection — should show ✓ Connection works. Detected: "TEST"
  5. Save credentials
  6. Flip ☁️ Cloud OCR toggle ON

OCR.space alternative (Gujarati only, no setup)

For Gujarati-only documents and quick testing:

  1. Get a free key at ocr.space/ocrapi/freekey
  2. ⚙ Cloud OCR → choose OCR.space → paste key → save

Free tier: 25,000 pages/month. No Sanskrit support in free tier.

Cost reality

Provider Free tier After free tier
Google Cloud Vision 1,000 calls/mo
.50 per 1,000 calls
OCR.space 25,000 calls/mo Need paid plan
Cloudflare Workers 100,000 calls/day $0.50 per million

For personal reading (a few dozen pages a day), you'll never exceed any free tier. Set the

Google budget alert and you can't be surprised.

Saving credentials to your device's password manager

In the Cloud OCR settings dialog, 🔒 Save to device stores your API key (OCR.space) or Worker URL (Google proxy) in your operating system's secure password store via the browser's Credential Management API — Google Password Manager on Android, iCloud Keychain on iOS where supported, or the Windows credential store. It's protected by your device lock, and the app never sees it again except when you tap ↺ Restore from device. Availability depends on your browser/OS; where programmatic storage isn't supported the key simply stays in on-device storage as before.


Google Drive integration

Optional and OAuth-gated. Lets you pick files directly from your Drive instead of downloading then uploading.

Setup

  1. In Google Cloud Console (same project as Vision), enable:
    • Google Drive API
    • Google Picker API
  2. Create credentials:
    • OAuth 2.0 Client ID (Web application)
      • Authorized JavaScript origins: your app URL (e.g. https://indic-reader.pages.dev)
    • API key (separate from the Vision key)
      • API restrictions: Drive API + Picker API
      • Application restrictions: HTTP referrers → https://YOUR-APP-URL/*
  3. OAuth consent screen → set up as External + Testing → add your own email as Test User
  4. In the app: tap 🟢 Drive → enter OAuth Client ID and API key → save

Usage

  • Tap 🟢 Drive🔐 Sign in & pick from Drive
  • Google's Picker UI opens; select files
  • Files are downloaded into the app and processed

Drive integration is fiddly to set up. If you have trouble, the local file picker works perfectly without any setup.


Settings & preferences

Saved automatically in browser localStorage:

Setting Default Description
Theme Auto Light / dark / follow system
Layout A Single column
Voice per language First available Use Voice Settings to choose
Speed per language 1.0× Three sliders
Bookmarks (★ stars) None Per-line favorites
Show Meanings consent Off Required for Sanskrit dictionary
Translate consent Off Required for verse translation
Cloud OCR consent Off Required for cloud OCR
Drive credentials None OAuth Client ID + API key
Cloud OCR credentials None Worker URL or OCR.space key

Reset privacy choices

Bottom of the page → "Reset privacy choices" — revokes all consents and re-shows the privacy banner. Doesn't delete credentials.

Clear All

Top of the file-input section → wipes loaded documents and in-memory state. Doesn't touch settings or credentials.


Keyboard shortcuts

Desktop only:

Key Action
Space Play / pause narration
Previous line
Next line
Decrease speed
Increase speed
T Toggle Text / Document view
F Cycle language filter (All → Guj → Sans → Hin → All)
S Toggle Auto-scroll
Esc Stop narration / close dialogs

Privacy & data handling

Files stay on your device by default. The app runs entirely in your browser — there's no Indic Reader server.

What's sent externally and when

Feature Sends data to When
Cloud OCR Cloudflare Worker → Google Vision (or OCR.space) Each page processed (only when toggle is ON)
Re-OCR a region Cloudflare Worker → Google Vision (or OCR.space) When you re-OCR a selected region with a cloud engine (local option sends nothing)
Fix a single line / Continuous next-page OCR Cloudflare Worker → Google Vision (or OCR.space) When you re-OCR one line, or when Continuous mode prepares the next page, with a cloud engine (local option sends nothing)
Split Sandhi Cloudflare Worker → UoH Heritage segmenter (verse text only, no files); Google Translate for per-word meanings When you tap "Split Sandhi" (segmenter falls back to on-device if unreachable; meanings need online lookups enabled)
Show Meanings Cologne University MW API + Google Translate When you tap "Show Meanings"
Translate verse Google Translate (public endpoint) When you tap "Translate verse" or use layout B/C
Translate page (Gu↔Hi, →EN) Google Translate (public endpoint) When you tap 🌐 ગુ→हि / हि→ગુ / →EN (Gujarati/Hindi text only; Sanskrit verses not sent)
Vedabase link vedabase.io (link only, no content) When you tap the 📖 link (opens in new tab)
Drive Picker Google (drive.google.com) When you tap Drive sign-in
First-launch CDN cdn.jsdelivr.net Loading PDF/OCR/DOCX libraries (cached afterward)
Tesseract language data tessdata.projectnaptha.com First local OCR run (cached afterward)

What's NOT sent

  • No analytics or telemetry
  • No tracking pixels
  • No cookies
  • No data sent to Anthropic or any other party
  • Your text is never sent anywhere unless you explicitly trigger an external feature

Stored only on your device (never uploaded)

Your preferences and manual corrections live in this browser's localStorage and are never sent anywhere: language/text overrides, line text corrections (inline edit / re-OCR), manual verse references (the canto/chapter/verse you enter for vedabase.io links), saved sandhi splits, translation cache, bookmark notes, voice/theme preferences, the Prefs toggles, and Drive credentials. The OCR cache, the last-opened document (for the Continue button), and your reading position are stored in this browser's IndexedDB, on-device only. Optionally, the 🔒 Save to device button places your OCR credential in your OS password manager (Google Password Manager / iCloud Keychain / Windows), not in the app. A manual verse reference only ever produces a vedabase.io link, sent solely when you tap that link.

Your data rights (GDPR/DPDP)

  • Access/Portability: use "Copy Text" to export all extracted content
  • Erasure: "Clear All" wipes in-memory state; browser "Clear site data" wipes preferences
  • Withdraw consent: "Reset privacy choices" link at the bottom

Troubleshooting

"No Indic text found"

Usually means the document has no embedded text. Solutions:

  1. Turn ON Force OCR, re-process
  2. If the file is a Google Docs PDF, font mapping may be broken — Force OCR fixes it
  3. For images: make sure the script is clearly readable (clean scan, no glare)

Cloud OCR shows "Local (cloud failed)"

First, tap the engine badge — it now shows a per-page diagnostic: your current settings (Cloud ON/OFF, fallback ON/OFF), which engine OCR'd each page, and the run log with the specific failure reason. This tells you exactly what happened.

If a page genuinely fell back to local, common causes:

  • Worker URL wrong or Worker not deployed
  • Google API key invalid or restrictions blocking it
  • ALLOWED_ORIGIN on Worker doesn't match your app URL
  • Free quota exceeded
  • Network down

Note: the badge reflects the last processing run. Turning Cloud OCR on does not re-OCR already-loaded pages — you must re-process the page (or use Re-OCR a region for a targeted fix).

Sanskrit being read as Gujarati (or vice versa)

Two fixes:

  1. Quick fix (classification): long-press the word → set its language, or "Set line → Sanskrit" to fix the narration voice. See Correcting language & text.
  2. Root fix (garbled text): if the text itself is wrong from OCR, use Re-OCR a region in Document View to re-read just that verse with Sanskrit-only hints.

The underlying cause is a known OCR limitation: inline script-switching within a single line confuses every OCR engine. Standalone verses work; short embedded quotes may misread.

"Drive Picker error"

Usually means API key restrictions are too strict. Quick fixes:

  1. Edit the Drive Picker API key in Google Cloud Console
  2. Application restrictions → temporarily set to "None"
  3. Wait 5 minutes for propagation
  4. Reload the app

If still broken, just skip Drive — use the regular file picker.

Narration sounds wrong / weird voice

System TTS voices vary by device. Tap Voice Settings in the reader and pick a better voice:

  • Android: install "Google Text-to-Speech" + the language packs
  • Windows: Settings → Time & Language → Speech → Add voices
  • macOS/iOS: System Settings → Accessibility → Spoken Content → System Voice

For Sanskrit specifically, Hindi voices are usually the best fallback since true Sanskrit TTS is rare.

Page won't scroll on mobile

Pull down firmly to refresh. If the player bar is hiding the bottom of content, try landscape orientation. The bottom bar's height adapts but can occasionally need a refresh.

App still shows the old version number after an update

The footer shows the running APP_VERSION (e.g. Indic Reader v66). If you deployed a new build but still see the old number — even in incognito or another browser — the problem is almost always that the new files are not actually live yet, not a browser cache:

  1. Confirm the deploy happened. Open the raw page in a fresh incognito window (no service worker there) and check the footer version, or View source and search for APP_VERSION. A fresh browser fetches straight from the server, so whatever it shows is what the server is serving. If that's still old, the deploy didn't go live.
  2. Check the right branch / build. On Cloudflare Pages or GitHub Pages, make sure the commit landed on the production branch and the build/deploy actually ran and succeeded (not a preview deployment URL).
  3. Bump both version strings. APP_VERSION in index.html and CACHE_NAME in sw.js should be bumped together so the service-worker cache is invalidated.
  4. Edge cache. The included _headers sets Cache-Control: no-cache on /, index.html, sw.js, manifest.json, robots.txt and sitemap.xml, so the HTML and worker always revalidate. If you host elsewhere, ensure your host isn't serving HTML with a long cache TTL; purge the CDN cache after deploying.
  5. Stuck service worker (already-installed users only): the worker is network-first for the page and calls skipWaiting(), so a normal reload while online should update it. If a device is truly stuck, browser Settings → Clear site data for the domain forces a clean fetch.

Known limitations

Things this app does not and cannot do well:

  • Vedic pitch accents (svara marks) — Web Speech API can't render them. Pre-recorded chanting audio would be needed.
  • Real-time collaboration — no shared state across devices/users.
  • Note-taking — bookmarks (★) now support a text note and store the full line, but there are no inline highlights or freehand annotations.
  • Inline script-switching OCR — short embedded Sanskrit quotes in Gujarati prose often misread (limitation of all OCR engines, not specific to this app). Mitigated by Re-OCR a region or Fix a single line with Sanskrit-only hints, and manual text/language correction.
  • Continuous next-page OCR gap — for an uncached page, narration can outrun the background OCR on large scans/slow connections, causing a brief pause at the page boundary (the few-line lead usually hides it; cached pages are instant).
  • Device password manager support🔒 Save to device relies on the browser's Credential Management API; it works on Chromium (Android/Windows/desktop) and where iOS supports it, but isn't available in every browser.
  • Vedabase content scraping — BBT-copyrighted; we deep-link to the official site only, never copy text.
  • Translation quality for technical Sanskrit — Google Translate handles colloquial Sanskrit poorly. Use the MW dictionary for word meanings, treat full-verse auto-translation as approximate.
  • Sandhi splitting is not guaranteed correct — the Heritage segmenter gives the ranked best analysis, but Sanskrit segmentation is inherently ambiguous. The on-device fallback is heuristic only. Always verify with the built-in split/merge editing. See Split Sandhi.
  • Offline-first PWA install — the file works offline once loaded, but proper PWA install with home-screen icon requires the GitHub Pages bundle.
  • Persistent translation cache — currently per-session. Reloading the page loses cached translations. (Language/text corrections, bookmark notes, the OCR cache, and saved sandhi splits do persist.)
  • Screen-off narration on Android — Web Speech narration is suspended by the OS when the screen is manually locked; no web app can override this. The screen is held awake during narration as the practical workaround. True screen-off background audio needs a native app.
  • Android Auto / CarPlay projected screen — a web app cannot register as a native in-car app, so Indic Reader can't appear as a tile on the car's Auto/CarPlay screen. Use Bluetooth audio mode in the car (see Background & car playback).
  • iOS storage — Safari clears site data in Private Browsing, with "Block All Cookies" on, or after 7 days of non-use. Add to Home Screen for durable storage; a startup banner warns if storage is blocked.
  • iOS Gujarati voice — iOS has no native Gujarati voice; the app transliterates to Devanagari and uses the Hindi voice so Gujarati is still spoken (Hindi-accented). Android with the Google Gujarati voice is recommended for proper Gujarati narration.

OCR reading order

For cloud-OCR'd pages, the app rebuilds the visual reading order at the line level — keeping each recognized line's words and punctuation intact and reordering only whole lines top-to-bottom — so multi-column headers and skewed scans don't scramble words. The OCR pipeline is versioned so improvements automatically invalidate stale cached results without any manual cache clearing.


File structure — what each file is for

The project ships in a few bundles. Here's what every file does and whether you need it.

Core app (the only truly required file)

File Purpose Required?
index.html The entire application — all HTML, CSS, and JavaScript in one self-contained file (~350 KB). Open it directly in a browser and everything works: reading, narration, OCR, meanings, sandhi splitting. Everything else is optional enhancement. ✅ Yes

Offline & install support (PWA)

File Purpose Required?
sw.js Service worker. Caches the app so it loads offline after the first visit, and powers "install to home screen". It is network-first for the page (so a new deploy is fetched immediately when online) and cache-first for static assets. The version string inside (e.g. indic-reader-v66) is bumped on every update to invalidate the old cache. Optional (enables offline)
manifest.json PWA manifest — app name, description, icons, theme color, display mode, categories. Lets phones offer "Add to Home Screen" with a proper icon, and feeds app/store listings. Optional
icon-192.png App icon (192×192) for home screen / launcher. Optional
icon-512.png App icon (512×512) for splash screen / high-DPI, and the link-preview image. Optional

Discoverability (SEO)

File Purpose Required?
robots.txt Allows all search-engine crawlers and points them to the sitemap. Recommended when hosting
sitemap.xml Lists the site's URL(s) for search engines. Submit it in Google Search Console / Bing Webmaster Tools to speed up indexing. Recommended when hosting

The <head> of index.html also carries SEO metadata: a descriptive <title> and <meta name="description">/keywords, a <link rel="canonical">, Open Graph and Twitter card tags (for link previews), and JSON-LD structured data (WebApplication) describing the app, its features and languages. If you deploy on a domain other than indic-reader.pages.dev, update the canonical / og:url / twitter image URLs, the robots.txt sitemap line, and the <loc> in sitemap.xml to match.

Hosting & security (Cloudflare Pages / GitHub Pages)

File Purpose Required?
_headers Security headers for Cloudflare Pages — Content-Security-Policy (restricts which domains the app may contact), plus standard hardening headers. This is what allows the Worker, dictionary, and translate endpoints while blocking everything else. Recommended when hosting
_headers-no-csp A fallback version with the CSP relaxed, in case the strict policy ever blocks something on your setup. Rename to _headers only if you hit a CSP problem. Optional
.github/workflows/deploy.yml GitHub Actions workflow that auto-deploys the repo to GitHub Pages on every push. Only relevant if you host on GitHub. Optional (GitHub only)
README.md This documentation file. Reference

Cloud OCR & Sandhi (the Cloudflare Worker)

File Purpose Required?
cloudflare-worker-google-ocr.js The Worker proxy — paste this into a Cloudflare Worker (it is not part of the website; it runs separately on Cloudflare's edge). It does two jobs: (1) proxies page/region images to Google Cloud Vision for OCR, and (2) proxies verse text to the UoH Heritage segmenter for Split Sandhi. Both are needed only because browsers can't call those services directly (CORS). Needs your Google API key set as a Worker secret. Only for Cloud OCR & API sandhi
cloudflare-worker-azure-ocr.js An alternative Worker using Azure Computer Vision instead of Google, if you prefer Azure. Use one or the other, not both. Optional alternative

Convenience bundles & local testing

File Purpose Required?
indic-reader-github.zip Zipped copy of the GitHub Pages bundle (app + PWA + headers + workflow), ready to upload to a repo. Convenience
indic-reader-standalone.zip Zipped copy of the standalone/PWA bundle for offline or self-hosting. Convenience
serve.py A tiny local web server (Python, in the standalone/PWA bundle) — run python serve.py to test the app at localhost with correct headers, since some features need http:// rather than file://. Local testing only
android-wrapper/, ios-wrapper/, windows-wrapper/ Thin native wrappers (in the standalone/PWA bundle) that load the app in a WebView, if you want to package it as an installable app for each platform. Optional

Minimal setups

  • Just try it: index.html alone.
  • Offline-capable website: index.html + sw.js + manifest.json + both icons + _headers (and robots.txt + sitemap.xml for search engines).
  • Full features (Cloud OCR + API sandhi): the above, plus deploy cloudflare-worker-google-ocr.js to a Cloudflare Worker and paste its URL into the app's Cloud OCR settings.

Credits & sources

  • Monier-Williams dictionary: Cologne South Asia Studies / cologne-digital-sanskrit-lexicon
  • Sandhi segmentation: University of Hyderabad Sanskrit Heritage / Saṃsādhanī tools (sanskrit.uohyd.ac.in)
  • OCR: Tesseract.js (local), Google Cloud Vision (cloud)
  • DOCX parsing: mammoth.js
  • PDF rendering: pdf.js (Mozilla)
  • Sanskrit transliteration: IAST scheme
  • Vedabase: deep links to vedabase.io (BBT Inc.)

App code is provided for educational and personal study use. Scripture texts loaded into the app retain their original copyrights — verify your right to OCR or process any document before doing so. Vedabase content is BBT-copyrighted; this app links to but does not redistribute their text.

No commercial redistribution of this app or its output.