A description of the app as it is shipped. To learn the astronomy and calendar
maths from scratch, read the tutorial in guide/ — that is the
primary teaching source. For a terse reference of the formulas and their
provenance see METHODOLOGY.md (panchanga) and
METHODOLOGY_JYOTISH.md (kundli).
A static, installable PWA — Svelte 5 (runes) + Vite + TypeScript. It computes a daily panchanga, a lunar month calendar, a festival list, a birth chart (Kundli) with guna-milan matching, a local Sky view, and an interactive Learn guide — entirely on-device, for any bundled city or GPS point, in English/हिन्दी with Latin or Devanagari numerals.
Two runtime dependencies only: astronomy-engine (ephemeris) and dexie
(IndexedDB). No backend, no telemetry, no remote fonts. AGPL-3.0.
- Pure compute, isolated I/O. The calculation layers are pure functions of
(instant, location, options). Result caching and persistence live in the storage layer, never inside the math. (The compute layer does transparently memoize a few pure, hot sub-computations — sunrise/moonrise and new-moon instants — which is referentially invisible: identical inputs give identical outputs, distinct from the storage result cache.) - One swappable astronomy backend. Everything reads positions through
lib/astro; swappingephemeris.ts+ayanamsa.tswould change the engine without touching anything above. - Sidereal by default, sunrise-anchored civil days, ayanamsa applied once at the boundary — see Conventions.
- Data-driven i18n, not branch-driven: a language is a table of strings, not a fork in the code.
- Accuracy honesty: validate against Drik or label "preview"; never widen a tolerance to hide a disagreement (see Accuracy).
lib/astro/ julian · ephemeris (Sun/Moon/planets/nodes) · ayanamsa ·
sunrise · altaz · angle ← swappable backend
↓ ↓
lib/panchanga/ lib/jyotish/
tithi nakshatra yoga karana grahas · lagna · chart (+ houses) ·
vara masa samvat ritu ayana dasha (Vimshottari) · divisional (navamsa) ·
muhurta moon-phase tiebreakers matching (ashtakoota) · sky-math · names
festivals/
↓ ↓
routes/ + components/ Day Month Festivals Kundli Sky Learn Settings
lib/state/ (runes) preferences · clock · jyotish-draft · sw-update
lib/storage/ (Dexie) db · cache · birth-profiles · saved-locations · preferences
lib/i18n/ + lib/format/ en/hi + names + transliteration · numerals · time
lib/location/ cities (curated metros + diaspora) · GPS
julian.ts (instants, civil-midnight fixpoint that survives DST/extreme offsets),
ephemeris.ts (the adapter over astronomy-engine: Sun, Moon, the five visible
planets, true node, obliquity, sidereal time — EQJ vectors rotated to
ecliptic-of-date), ayanamsa.ts (IAU-2006 precession; Lahiri default +
Raman, anchored to Swiss-Ephemeris realizations — KP/Yukteshwar/True-Chitra
were dropped as near-duplicates, see METHODOLOGY_JYOTISH §3.3), sunrise.ts,
altaz.ts (horizontal coords, rise/set arcs and magnitudes for the Sky dome +
the tonight observation table), angle.ts.
compute.ts exposes the pure computePanchanga(); one file per anga, plus masa
(with adhik-maas), samvat, ritu, ayana, muhurta, moon-phase. festivals/ holds
the rule set (pan-india.ts + rules.ts); tiebreakers.ts (one level up, in
panchanga/) is the vyapini-window / tie-break engine — the single place
complexity concentrates and the highest-risk file. bisect.ts is the
angular-crossing root-finder that locates anga end times.
Pure arithmetic on top of the same astro backend: grahas.ts (sidereal graha
longitudes), lagna.ts (ascendant from sidereal time), chart.ts (whole-sign
houses), dasha.ts (Vimshottari from Moon nakshatra), divisional.ts (navamsa),
matching.ts (ashtakoota guna-milan from two charts), names.ts, glyphs.ts,
rashi-art.ts. sky-math.ts (+ the lib/sky/ data & geometry module) back the
Sky and Learn views.
Hash-routed pages in routes/ (no router dependency) render components in
components/ (DayCard, MonthGrid, KundliChart, MoonPhase, BodyIcon,
LocationPicker, …). Kundli/Match/Sky/Learn are lazy-loaded. The Sky and Learn
views share components/sky/ (the all-sky dome, ecliptic wheel, time clock,
observation table, concept primer) and lib/sky/ (their bilingual data tables +
SVG geometry). festivals.worker.ts computes the year's festival list off the
main thread (postMessage requires $state.snapshot() of the location — $state
proxies don't structured-clone).
lib/state/*.svelte.ts are runes stores — preferences (language, numerals,
ayanamsa, node type, month system, week start, location, theme), clock,
jyotish-draft, sw-update. lib/storage is Dexie (versioned schema, currently
v3) with cachedPanchangas, cachedFestivals, birthProfiles, and
saved-locations/preferences mirrors.
lib/i18n is two string tables (en.ts/hi.ts) plus names.ts/names-hi.ts
for the Sanskrit name sets, driven by a per-language META table. A
transliteration preference swaps grahas/rashis between Sanskrit (Mesha,
Maṅgala) and Western (Aries, Mars); tithi/nakshatra/yoga/festival names stay
Sanskrit in both. lib/format handles numerals (Devanagari/Latin) and time.
| Hash | Page |
|---|---|
#/ or #/today |
Today (rendered by Day.svelte with today's date) |
#/day/YYYY-MM-DD |
Day detail (same component) |
#/month/YYYY-MM |
Month calendar |
#/festivals/YYYY |
Festival list |
#/kundli |
Birth chart (+ the Milan matching section) |
#/sky |
Sky view (local-sky dome + tonight's rise/set table) |
#/learn |
Learn guide (how the panchanga works, interactive) |
#/settings |
Settings |
preferences (runes) → a route reads the instant + location + options → calls a
pure compute*() → the storage layer memoizes the result → the component renders.
Changing a preference re-runs the affected deriveds; nothing writes preferences
during render. The Sky and Learn views share a time widget (SkyClock) that runs
a self-throttled rAF (idle 1 Hz at rest, ~30 fps while playing) and tears it down
on unmount.
- Sidereal longitudes everywhere; the ayanamsa is subtracted once at the boundary, never baked into the ephemeris.
- Civil days are sunrise-anchored (vara, and the day a tithi "belongs to"); festival walks re-anchor per civil day rather than adding 86.4 M ms.
- Defaults (
storage/db.ts): purnimanta month system, true node (Rahu/Ketu), transliteration on, Lahiri ayanamsa. - End times come from
bisect.tsangular bisection, not linear interpolation.
pnpm dev(Vite, port 5173) ·pnpm build(static bundle +vite-plugin-pwaservice worker) ·pnpm test(Vitest) ·pnpm run lint— the one gate:tsc --noEmit+svelte-check(types + a11y) + ESLint (typescript-eslint + svelte).- Output is fully static and offline-capable; deploy anywhere that serves files.
- Node pinned via
.nvmrcandpackage.json(engines/volta).
Formulas are sourced and fixture-tested (sunrise/sunset, anga end-times, a multi-year New Delhi festival audit, a multi-city smoke, polar/DST regressions). Known gaps are named, not hidden (e.g. strict moonrise/moonset coverage). The rule: anything not proven against a reference is labelled a preview, and tolerances are never loosened to manufacture agreement.
Historical note: the original 800-line pre-implementation build spec is in git history (before this rewrite) if the early design rationale is ever needed.