TL;DR: A birth chart is deterministic. Same inputs, same output, forever. So I stopped asking "how do I build this API?" and started asking "what actually needs to leave the device?" The answer: much less than I expected.
Astrology apps have a shape, and the shape is: thin client, fat backend. The phone collects a birth date and sends it somewhere. Something comes back. The phone draws it.
I built JanamJyot the other way round, and the interesting part wasn't the astrology. It was discovering how much of what I assumed needed a server didn't.
๐งญ Start with what has to be true
A birth chart is deterministic. Date, time and place in; planetary positions, lagna, nakshatra, divisional charts and the dasha timeline out. Same inputs, same outputs, forever. There is no freshness requirement, no personalisation layer, no A/B test.
That is a strange thing to put behind a network call.
A weather app needs a server because weather changes. A chart doesn't change. Once you've computed someone's kundli, it is the same kundli in five years. The only reason to recompute it is that you threw it away.
So the question stopped being "how do I build this API" and became "what actually needs to leave the device."
The answer turned out to be: much less than I thought.

JanamJyot's live kundli form. Enter birth details and the chart is calculated on the spot, no account needed.
๐ฑ Capacitor, and the bits that earn their place
JanamJyot is React + TypeScript wrapped for Android with Capacitor. I'd used Capacitor before as a "ship the web app as an APK" shortcut. This is the first time I used it as the actual point.
Four plugins do the work, and each one replaced something I'd otherwise have built a backend for:
| Plugin | What I didn't have to build |
|---|---|
| Local notifications | Push infrastructure, device-token table, timezone-aware cron job |
| Speech recognition | Audio upload and storage |
| Biometric auth | An account system to secure |
| Filesystem + share sheet | Render service, signed URLs, bucket lifecycle policy |
๐ Local notifications
The daily reading isn't pushed from a server. The dasha timeline is already computed and stored; the app schedules notifications against it locally. No push infrastructure, no device-token table, no cron job wondering what timezone you're in. The phone already knows what day it is.
import { LocalNotifications } from '@capacitor/local-notifications';
// The timeline already exists. Scheduling is just reading from it.
await LocalNotifications.schedule({
notifications: upcoming.map((day, i) => ({
id: i,
title: day.headline,
body: day.summary,
schedule: { at: day.at }, // local time, device's own clock
})),
});
This one genuinely surprised me. "Daily notifications" sounds like a backend feature. It's a backend feature only when the content isn't knowable in advance. Here it is.
๐๏ธ Speech recognition
Questions get asked out loud ("shaadi kab hogi?") because typing a question in Hindi on a phone keyboard is miserable. On-device recognition means the audio never leaves, which also means I'm not storing anyone's voice anywhere. That's a privacy property I got for free by being lazy about infrastructure.
๐ Biometric auth
A kundli is personal in a way people underestimate until someone picks up their phone. App lock is a few lines with @aparajita/capacitor-biometric-auth, and it's local. There's no account to secure, so there's nothing to breach.
๐ Filesystem
PDF reports are generated on the device and handed to the share sheet. No render service, no signed URLs, no bucket with a lifecycle policy.
๐ฅ๏ธ What's left for the server
Not nothing. I'm not going to pretend it's fully offline.
There's an Express API, bundled and deployed as a single Vercel serverless function, which handles saved profiles and the LLM calls for written interpretations. That exists because those two things genuinely need to be somewhere else: persistence across devices, and an API key that cannot ship in an APK.
But the split is worth stating precisely, because it's the whole design:
The chart is computed. The model only phrases what was computed.
The language model never gets raw birth details to reason about astrologically. It receives already-calculated chart data and writes about that. A model that is allowed to decide what your dasha is will eventually tell you something your own chart contradicts, and in this category, people make decisions on the answer.
That constraint is also why multilingual output stopped being scary. Hindi, Hinglish and English differ in how the result is said, not in what the result is. Translation is a presentation problem when the facts were settled before any language was chosen.

JanamJyot supports Hindi, Hinglish and English, and the flow is simple: birth details, kundli, insights, AI guidance.
๐ชถ The APK stayed small
The side effect I didn't plan: shipping computation instead of fetching results keeps the app tiny and makes it work on bad connections, which in India is not an edge case. Someone on 3G in a village opens the app and gets their chart at the same speed as someone on fibre, because the network isn't in the path.
I keep running into this pattern now and I think it generalises past astrology:
If the output is deterministic from inputs the device already has, the server is a habit, not a requirement.
Plenty of things qualify: EMI calculators, unit conversion, tax estimators, anything calendrical. We put them behind APIs because that's the shape we've learned, not because the computation needs to be somewhere else.
โ Quick takeaways
- Deterministic output? Compute it on the device and keep it there.
- Notifications can be scheduled locally when the content is knowable in advance.
- Keep the server for what genuinely can't live in an APK: cross-device profiles and secret API keys.
- Let the model phrase, never decide. Calculate first, then let the LLM explain what was calculated.
๐ Try it
JanamJyot is free and live at janamjyot.lzworth.in.
If you've shipped Capacitor in production, I'd like to know which plugin surprised you by replacing a service. Mine was local notifications, by a distance. Tell me in the comments ๐
About me: I'm Vansh Kashyap, a full-stack and AI automation developer in New Delhi and a co-founder of LZ Worth. I've shipped 17 live web apps and 7 Android apps, all of them open and checkable at vanshkashyap.lzworth.in.
๐ป Code: github.com/Obitouchiha002 ยท ๐ผ LinkedIn: in/techbyvansh
Top comments (0)