vedic-muhurta
v0.1.0
Published
Electional-astrology (muhūrta) engine on top of vedic-panchanga — scan a date range for auspicious time windows for an activity, filtered by the person's janma rāśi & nakṣatra (Chandrabala, Tarabala). Ships a vehicle-purchase rule set.
Maintainers
Readme
vedic-muhurta
Pick auspicious times (muhūrta) for an undertaking, on top of
vedic-panchanga. Give it an
activity (a rule set), the
person it is for (janma rāśi + nakṣatra), a location and a date range — it
scans the range and returns the time windows where every rule holds, already
clear of Rāhu Kāla / Yamaganda / Gulika Kāla and of Viṣṭi (Bhadrā) karaṇa, each
scored 0–100 with the reasons spelled out.
Ships one packaged activity, vehiclePurchase (vāhana kharīdī). Everything
about it is plain data — copy and tweak it, or write your own activity the same
way.
- Person-aware — filters by Chandrabala (Moon's rāśi from the janma rāśi) and Tarabala (running nakṣatra from the janma nakṣatra)
- Panchāṅga-aware — vāra, nakṣatra, tithi (by pakṣa ordinal), yoga, karaṇa
- Windows, not just days — contiguous good stretches with start/end, duration, score, grade and per-factor reasons
- Safe by default — Rāhu Kāla / Yamaganda / Gulika / Viṣṭi are always excluded; Abhijit Muhūrta earns a bonus
- Extensible — an
Activityis a JSON-ish object; bring your own for gṛha-praveśa, travel, etc. - Zero config astronomy — all positions come from
vedic-panchanga(pure TypeScript, no native deps)
npm install vedic-muhurta
# vedic-panchanga is pulled in automaticallyQuick start
import { findMuhurta, vehiclePurchase } from "vedic-muhurta";
const res = findMuhurta({
activity: vehiclePurchase,
person: { janmaRashi: "Simha", janmaNakshatra: "Magha" }, // names or 1-based indices
from: "2026-09-05", // civil dates in `timezone`
to: "2026-09-20",
latitude: 28.6139,
longitude: 77.209,
timezone: "Asia/Kolkata",
});
for (const w of res.windows) {
console.log(
`${w.start.toLocaleString()} – ${w.end.toLocaleString()} ` +
`(${w.durationMinutes} min) ${w.score}/100 ${w.grade}`,
);
console.log(" ✓ " + w.reasons.join("; "));
if (w.cautions.length) console.log(" ! " + w.cautions.join("; "));
}Already have a time in mind? Judge it directly:
import { evaluateMuhurta, vehiclePurchase } from "vedic-muhurta";
const v = evaluateMuhurta({
activity: vehiclePurchase,
person: { janmaRashi: "Simha", janmaNakshatra: "Magha" },
at: new Date("2026-09-11T12:45:00+05:30"),
latitude: 28.6139,
longitude: 77.209,
timezone: "Asia/Kolkata",
});
console.log(v.ok, v.score, v.grade);
console.log(v.blockers); // non-empty ⇢ why it was rejectedWhat you get
type FindMuhurtaResult = {
activity: string; // "vehiclePurchase"
person: { janmaRashi: string; janmaNakshatra: string }; // canonical names
range: { from: string; to: string; timezone: string };
windows: MuhurtaWindow[]; // earliest first, non-overlapping
skippedDays: { date: string; reason: string }[]; // e.g. polar day
};
type MuhurtaWindow = {
start: Date;
end: Date;
durationMinutes: number;
score: number; // 0–100, mean across the window
grade: "excellent" | "good" | "fair";
reasons: string[]; // factors that held all through
cautions: string[]; // minor negatives / factors that drifted
panchangaAt: PanchangaSnapshot; // tithi, vara, nakshatra, yoga, karana,
// moonSign, chandrabala, tarabala at `start`
};evaluateMuhurta returns one InstantVerdict:
type InstantVerdict = {
ok: boolean; // false if any hard rule fails
score: number;
grade: "excellent" | "good" | "fair" | "rejected";
reasons: string[];
cautions: string[];
blockers: string[]; // hard-rule failures; non-empty exactly when !ok
panchangaAt: PanchangaSnapshot;
};Options (findMuhurta)
| Option | Type | Default |
| -------------------- | -------------------------------------- | ------------------ |
| activity | Activity — required | — |
| person | { janmaRashi, janmaNakshatra } — required | — |
| from / to | "yyyy-mm-dd" in timezone — required | — |
| latitude | number (−90…90) — required | — |
| longitude | number (−180…180) — required | — |
| timezone | IANA id | "Asia/Kolkata" |
| ayanamsa | "lahiri" \| "raman" \| "kp" \| "fagan_bradley" | "lahiri" |
| stepMinutes | sampling granularity | 15 |
| minScore | drop windows below this | 55 |
| minDurationMinutes | drop windows shorter than this | = stepMinutes |
| dayStart / dayEnd| "HH:MM" clamp on the daily scan | sunrise → sunset |
person.janmaRashi accepts "Simha", "Leo", "सिंह" or 5;
person.janmaNakshatra accepts "Magha", "मघा" or 10 (spacing and case are
ignored).
Activities
An Activity is just data. Each cycle gets a Judgement — { good?, caution?, avoid? }
of 1-based indices. good adds weight, caution subtracts a little (still
eligible), avoid disqualifies the window outright. Anything unlisted is neutral.
import type { Activity } from "vedic-muhurta";
const grihaPravesh: Activity = {
name: "grihaPravesh",
label: { iast: "Griha Pravesha", english: "house-warming" },
vara: { good: [2, 4, 5, 6], avoid: [3, 7] }, // 1 = Sunday … 7 = Saturday
nakshatra: { good: [4, 5, 7, 13, 15, 17, 22, 26, 27] },
tithi: { good: [2, 3, 5, 7, 10, 11, 13], avoid: [4, 9, 14], avoidAmavasya: true },
yoga: { avoid: [17, 27] }, // Vyatipata, Vaidhriti
karana: { avoid: [7] }, // Vishti / Bhadra
chandrabala: { good: [1, 3, 6, 7, 10, 11], caution: [2, 5, 9], avoid: [4, 8, 12] },
tarabala: { good: [2, 4, 6, 8, 9], caution: [1], avoid: [3, 5, 7] },
// weights: { nakshatraGood: 20 }, // optional per-activity overrides
};Tithi is judged by ordinal within the pakṣa (1–15), so one list covers both
Śukla and Kṛṣṇa; avoidAmavasya / avoidPurnima target those two days
specifically. See src/activities/vehicle-purchase.ts
for the shipped rule set with notes on each list.
Lower-level exports
import {
resolveRashi, resolveNakshatra, rashiName, nakshatraName,
chandrabala, tarabala, TARA_NAMES,
evaluatePanchanga, snapshot, grade, DEFAULT_WEIGHTS,
} from "vedic-muhurta";evaluatePanchanga(panchanga, activity, janmaRashi1, janmaNakshatra1) applies
the rules to a Panchanga you already computed (compute it with the
time option so each aṅga is the one running at your instant).
How a window is scored
Starting from DEFAULT_WEIGHTS.base (50), each factor adds or subtracts:
- vāra favourable
+16, mixed−8, avoided → rejected - nakṣatra favourable
+14, not on the list−6, avoided → rejected - tithi favourable
+10, mixed−4, Riktā / Amāvasyā (per activity) → rejected - yoga mixed
−10, Vyatīpāta / Vaidhṛti (per activity) → rejected - karaṇa not Viṣṭi
+2, Viṣṭi → rejected - Chandrabala strong
+14, weak−3, 4th/8th/12th → rejected - Tarabala favourable
+12, Janma−6, Vipat/Pratyari/Vadha → rejected - inside Abhijit Muhūrta
+6; inside Rāhu Kāla / Yamaganda / Gulika → rejected
Score is clamped to 0–100. grade is excellent ≥ 82, good ≥ 68, else fair.
Override any weight per activity via activity.weights.
Accuracy & limitations
- Panchāṅga level only — no lagna, no navāṁśa, no planetary aspects. This finds a clean muhūrta by the almanac factors; it is not a full electional chart.
- Discrete sampling — windows are found by stepping
stepMinutesthrough the day, so an edge is accurate to one step, not to the exact aṅga boundary. Use a smallerstepMinutesfor tighter edges (at a linear cost in time). - Rule lists are a consensus — the
vehiclePurchaselists follow commonly published vāhana-muhūrta guidance. Traditions differ; adjust theActivity. - Dates —
from/toare civil dates intimezone, resolved via noon UTC; fine for every common zone, off by a day only for offsets beyond ±12 h. - Astronomy accuracy is whatever
vedic-panchangaprovides (almanac-grade, aṅga boundaries within ~a minute).
