Skip to content
Documentation · all sections

Muhurta Engine

panchang-ts · v5.0.0 · MIT

Score any date against 13 stock occasions or your own rule, understand the scoring model and Vara × Tithi yogas, and ship pre-computed muhurta tables.

Scoring a date

import { scoreMuhurta, computeAuspiciousDatesInRange, vivahRule } from 'panchang-ts';

const r = scoreMuhurta(new Date('2026-05-12'), DELHI, vivahRule, { timezone: 330 });
// → { date, score: 0..100, passes: boolean,
//     reasons: string[],           // diagnostic English
//     factors: MuhurtaFactor[] }   // { code, axis, index?, delta } — stable

r.factors.filter(f => f.delta < 0);              // what cost the day points
r.factors.some(f => f.axis === 'exclusion');     // hard-excluded?

const dates = computeAuspiciousDatesInRange(
  vivahRule,
  new Date('2026-05-01'),
  new Date('2026-05-31'),
  DELHI,
  { timezone: 330 },
);   // MuhurtaDay[] sorted by score desc; full panchang attached

// Single year, the shape most callers reach for first:
computeAuspiciousDatesForYear(2027, vivahRule, DELHI, { timezone: 330 });

Scoring model: starts at 50; +10 per matching auspicious axis (tithi / nakshatra / vara / yoga), −15 per inauspicious axis, hard exclusions zero the score. Special yogas (Amrit Siddhi, Sarvartha Siddhi, Ravi/Guru Pushya) add +5; Jwalamukhi subtracts 10. Clamped 0..100; passes: true when score ≥ 50.

scoreMuhurta computes only the sections it actually scores against, so it is cheaper than a full getDailyPanchang. computeAuspiciousDatesInRange does not narrow — each returned day carries its complete panchang for callers to drill into.

Rules — 13 stock occasions, or your own

Stock rules: vivahRule, grihaPraveshRule, namakaranaRule, vidyarambhRule, vahanKharidiRule, annaprashanRule, mundanRule, upanayanamRule, karnavedhaRule, aksharabhyasamRule, seemanthamRule, shopOpeningRule, travelStartRule — or all of them as STOCK_MUHURTA_RULES. A rule is pure data:

const myRule: MuhurtaRule = {
  occasion: 'launch_party',
  auspiciousVaras: [3, 4, 5],
  auspiciousNakshatras: [11, 12, 21],
  bhadra: 'penalize',        // 'ignore' | 'penalize' | 'exclude'
  excludeEkadashi: true,
  excludeAdhikaMasa: true,
};

Tithi and vara are scored jointly

The classical Vara × Tithi yogas — Siddha, Amrita, Dagdha, Visha, Hutasana, Krakacha, Samvartaka — are applied to every rule, so a Rikta tithi landing on a Saturday is partly redeemed by Siddha yoga rather than flatly penalised. Set varaTithiYogas: false for the older per-anga-only scoring, or call computeVaraTithiYogas(vara, tithi) directly. Where an auspicious and an inauspicious yoga both fire — a documented ambiguity in the sources — both are surfaced as separate factors and allowed to net out.

Handling Bhadra

bhadra defaults to 'ignore'; the stock rules use 'penalize'. A whole-day 'exclude' is rarely what you want: Vishti karana sits at fixed positions in the tithi cycle, so vetoing the day removes seven tithis outright — among them Shukla Ekadashi, which the same sources list as preferred for vivah. Read panchang.inauspicious.bhadra for the window and schedule around it. excludeBhadra: true still works as an alias for bhadra: 'exclude'.

Pre-computed table — build your own and cache it

Scoring a year of days runs the engine ~365 times. If your app asks the same question repeatedly, compute the answer once and ship the JSON — the same pattern the festival, eclipse and moon-phase tables use.

import { buildMuhurtaTable, vivahRule } from 'panchang-ts';

const table = buildMuhurtaTable({
  rule: vivahRule,
  location: { latitude: 25.3176, longitude: 82.9739 },
  timezoneOffsetMinutes: 330,
  startYear: 2026,
  endYear: 2031,
  referenceLocation: 'Varanasi',
});
// persist JSON.stringify(table) — 6 years of vivah dates is ~74 KB

Read it back through the engine-free panchang-ts/muhurta entry (~1.7 KB, no astronomy code in your bundle):

import {
  readMuhurtaForYear,
  readMuhurtaForDate,
  readMuhurtaYearRange,
  readMuhurtaOccasion,
  readBestMuhurtaDays,
} from 'panchang-ts/muhurta';

const table = JSON.parse(await (await fetch('/muhurta-vivah.json')).text());

readMuhurtaOccasion(table);          // 'vivah'
readMuhurtaYearRange(table);         // { start: 2026, end: 2031 }
readMuhurtaForYear(table, 2026);     // MuhurtaTableDay[] — passing days, by date
readMuhurtaForDate(table, '2026-11-11');
readBestMuhurtaDays(table, 5);       // top 5 across the table, highest first

Only days that pass the rule are stored by default; pass includeFailures: true to keep every day with its score. Scores are location- and rule-dependent, so a table built for Varanasi and vivah says nothing about another place or occasion. The repo's npm run muhurta:gen script is a worked example:

npm run muhurta:gen -- muhurta-vivah.json vivah
FunctionReturns
computePanchaka(instant, loc)whether the Moon sits in the last five nakshatras at an instant
classifyPanchaka / isPanchakaDosha / findPanchakaOnsetthe named Panchaka type from its onset vara — see the daily-result note
computeVaraTithiYogas(vara, tithi)the Vara × Tithi yogas firing for that pair