No bullshit astrology calculations. Celestine is a TypeScript library for birth charts, transits, progressions, and everything else you need for serious astrological software. Every single calculation is validated against NASA data, JPL Horizons, and Swiss Ephemeris — not "inspired by" or "based on", but byte-for-byte verified against the actual sources astronomers use.
Built for practitioners who actually care about precision. 2400+ unit tests. Zero runtime dependencies. If the math is wrong, your tests will catch it. If NASA says Mars is at 127.4532°, Celestine gives you 127.4532°. Not 127.45° or "close enough" — the real number, every time.
npm install celestine
import { calculateChart, ephemeris, time, zodiac } from "celestine";
// Calculate a complete birth chart
const chart = calculateChart({
year: 2000,
month: 1,
day: 1,
hour: 12,
minute: 0,
second: 0,
timezone: 0,
latitude: 51.5074, // London
longitude: -0.1278,
});
console.log(`Rising: ${chart.angles.ascendant.formatted}`);
console.log(`Sun: ${chart.planets[0].formatted}`);
console.log(`Aspects: ${chart.aspects.all.length}`);
// Or use individual modules for more control
const jd = time.toJulianDate({ year: 2000, month: 1, day: 1, hour: 12 });
const sun = ephemeris.getSunPosition(jd);
const position = zodiac.eclipticToZodiac(sun.longitude);
console.log(position.formatted); // "10°27' Capricorn"
// Check planetary dignity
const marsAries = zodiac.getPlanetaryDignity(
zodiac.Planet.Mars,
zodiac.Sign.Aries
);
console.log(marsAries.state); // "Domicile" (Mars rules Aries)
Calculate precise positions of celestial bodies at any moment, validated against JPL Horizons and Swiss Ephemeris.
import { ephemeris, time } from "celestine";
// Get Julian Date for a specific moment
const jd = time.toJulianDate({ year: 2000, month: 1, day: 1, hour: 12 });
// Get individual body positions
const sun = ephemeris.getSunPosition(jd);
console.log(sun.longitude); // 280.46° (Capricorn)
console.log(sun.distance); // 0.983 AU
console.log(sun.isRetrograde); // false
const moon = ephemeris.getMoonPosition(jd);
console.log(moon.longitude); // 223.32° (Scorpio)
console.log(moon.latitude); // 5.17°
// Get all positions at once using the unified API
const positions = ephemeris.getAllPositions(jd);
console.log(positions.get(ephemeris.CelestialBody.Mars));
// Check retrograde status
const mercury = ephemeris.getMercuryPosition(jd);
if (mercury.isRetrograde) {
console.log("Mercury retrograde!");
}
// Get zodiac sign for a body
const sign = ephemeris.getSign(ephemeris.CelestialBody.Venus, jd);
console.log(sign); // "Sagittarius"
Available Bodies:
Features:
The Time module provides comprehensive astronomical time calculations, all verified against NASA reference data.
import { time } from "celestine";
// Julian Date conversions
const jd = time.toJulianDate({
year: 2025,
month: 12,
day: 18,
hour: 15,
minute: 30,
second: 0,
});
const date = time.fromJulianDate(jd); // Convert back to calendar date
// Julian Centuries from J2000.0 epoch
const T = time.toJulianCenturies(jd); // For ephemeris calculations
// Sidereal Time (for house calculations)
const gmst = time.greenwichMeanSiderealTime(jd); // Greenwich Mean Sidereal Time
const lst = time.localSiderealTime(gmst, -5.0); // Local Sidereal Time (longitude: -5°)
// Delta T corrections (TT ↔ UT)
const deltaT = time.deltaT(2025, 12); // ΔT in seconds
const ttJD = time.utToTT(jd, 2025, 12); // Convert UT to Terrestrial Time
// Time validation
const isValid = time.isValidDate(2024, 2, 29); // true (leap year)
const validation = time.validateCalendarDateTime({
year: 2025,
month: 12,
day: 18,
hour: 15,
minute: 30,
second: 0,
}); // { valid: true, errors: [] }
// Constants
console.log(time.J2000_EPOCH); // 2451545.0
console.log(time.DAYS_PER_CENTURY); // 36525
Available:
All calculations are based on authoritative sources (Meeus, NASA, IERS) and tested against 309 unit tests including 17 NASA official reference values.
Calculate astrological house cusps and angles using multiple house systems, all verified against Swiss Ephemeris.
import { houses } from "celestine";
// Calculate houses for a birth chart
const birthChart = houses.calculateHouses(
{ latitude: 51.5074, longitude: -0.1278 }, // London
245.5, // Local Sidereal Time in degrees
23.4368, // Obliquity of ecliptic
"placidus" // House system
);
console.log(birthChart.angles);
// { ascendant: 120.5, midheaven: 285.3, descendant: 300.5, imumCoeli: 105.3 }
console.log(birthChart.cusps);
// { cusps: [120.5, 145.2, 172.8, ...] } // 12 house cusps
// Supported house systems
const systems = [
"placidus",
"koch",
"equal",
"whole-sign",
"porphyry",
"regiomontanus",
"campanus",
];
Available:
Calculate tropical zodiac positions and essential dignities based on traditional astrological doctrine.
import { zodiac } from "celestine";
// Convert ecliptic longitude to zodiac position
const venus = zodiac.eclipticToZodiac(217.411111);
console.log(venus);
// {
// sign: Sign.Scorpio,
// signName: 'Scorpio',
// degree: 7,
// minute: 24,
// second: 40,
// formatted: "7°24'40\" Scorpio"
// }
// Get sign properties
const aries = zodiac.getSignInfo(zodiac.Sign.Aries);
console.log(aries);
// {
// element: Element.Fire,
// modality: Modality.Cardinal,
// polarity: Polarity.Positive,
// ruler: Planet.Mars,
// symbol: '♈'
// }
// Check planetary dignity
const sunAries = zodiac.getPlanetaryDignity(
zodiac.Planet.Sun,
zodiac.Sign.Aries
);
console.log(sunAries);
// {
// state: DignityState.Exaltation,
// strength: 4, // +4 for exaltation
// exaltationDegree: 19,
// description: 'Sun exalted in Aries'
// }
// Check multiple dignities
zodiac.isRuler(zodiac.Planet.Mars, zodiac.Sign.Aries); // true (Mars rules Aries)
zodiac.isExalted(zodiac.Planet.Sun, zodiac.Sign.Aries); // true (Sun exalted in Aries)
zodiac.isDetriment(zodiac.Planet.Mars, zodiac.Sign.Libra); // true (opposite Aries)
// Format with options
const formatted = zodiac.formatZodiacPosition(venus, {
useSymbol: true, // Use ♏ instead of "Scorpio"
includeSeconds: false, // Omit seconds
});
// "7°24' ♏"
Available:
Calculate aspects between celestial bodies with configurable orbs, patterns detection, and applying/separating indicators.
import { aspects, ephemeris, time } from "celestine";
// Get planetary positions
const jd = time.toJulianDate({ year: 2000, month: 1, day: 1, hour: 12 });
const sun = ephemeris.getSunPosition(jd);
const moon = ephemeris.getMoonPosition(jd);
const mars = ephemeris.getMarsPosition(jd);
// Create bodies array for aspect detection
const bodies = [
{ name: "Sun", longitude: sun.longitude, longitudeSpeed: sun.longitudeSpeed },
{
name: "Moon",
longitude: moon.longitude,
longitudeSpeed: moon.longitudeSpeed,
},
{
name: "Mars",
longitude: mars.longitude,
longitudeSpeed: mars.longitudeSpeed,
},
];
// Find all aspects between bodies
const result = aspects.calculateAspects(bodies);
for (const aspect of result.aspects) {
console.log(aspects.formatAspect(aspect));
// "Sun ⚹ Moon (2°57', 51%, separating)"
}
// Detect aspect patterns (T-Square, Grand Trine, Yod, etc.)
const patterns = aspects.findPatterns(result.aspects);
for (const pattern of patterns) {
console.log(`${pattern.type}: ${pattern.bodies.join(", ")}`);
}
// Calculate angular separation
const separation = aspects.angularSeparation(sun.longitude, moon.longitude);
console.log(separation); // 57.05°
// Check if within orb of an aspect
const match = aspects.findMatchingAspect(separation);
if (match) {
console.log(`${match.aspect.name} with ${match.deviation}° orb`);
}
Available Aspects (14 types):
Aspect Patterns:
Features:
Calculate complete astrological birth charts by combining all modules into a cohesive API. All calculations verified against Swiss Ephemeris.
import { calculateChart, CelestialBody } from "celestine";
// Calculate a complete birth chart
const chart = calculateChart({
year: 1879,
month: 3,
day: 14,
hour: 11,
minute: 30,
second: 0,
timezone: 0.667, // LMT offset (or use standard timezone)
latitude: 48.4,
longitude: 10.0,
});
// Access planetary positions
for (const planet of chart.planets) {
console.log(`${planet.name}: ${planet.formatted} in House ${planet.house}`);
}
// "Sun: 23°30' Pisces in House 9"
// "Moon: 14°31' Sagittarius in House 6"
// Access chart angles
console.log(`Rising: ${chart.angles.ascendant.formatted}`);
// "Rising: 11°38' Cancer"
console.log(`Midheaven: ${chart.angles.midheaven.formatted}`);
// "Midheaven: 12°50' Pisces"
// Access house cusps
for (const [num, cusp] of Object.entries(chart.houses.cusps)) {
console.log(`House ${num}: ${cusp.formatted}`);
}
// Access aspects
for (const aspect of chart.aspects.all) {
console.log(`${aspect.body1Name} ${aspect.symbol} ${aspect.body2Name}`);
}
// Access chart summary
console.log(chart.summary.elements);
// { fire: 3, earth: 1, air: 2, water: 4 }
console.log(chart.summary.modalities);
// { cardinal: 2, fixed: 3, mutable: 5 }
console.log(chart.summary.retrograde);
// ['Uranus']
console.log(chart.summary.patterns);
// [{ type: 'TSquare', bodies: ['Sun', 'Moon', 'Saturn'] }]
Chart Options:
const chart = calculateChart(birthData, {
houseSystem: "placidus", // 'koch', 'equal', 'whole-sign', etc.
includeAsteroids: true, // Ceres, Pallas, Juno, Vesta
includeChiron: true, // Chiron
includeLilith: true, // Black Moon Lilith
includeNodes: true, // Lunar Nodes
includeLots: true, // Part of Fortune, Part of Spirit
aspectTypes: "major", // 'all', 'major', or custom array
zodiacType: "tropical", // Currently only tropical supported
});
Also Available:
import {
calculateChart,
calculatePlanets,
calculateHouseCusps,
validateBirth,
formatChart,
getAvailableHouseSystems,
} from "celestine";
// Calculate only planetary positions
const planets = calculatePlanets(birthData);
// Calculate only house cusps and angles
const houses = calculateHouseCusps(birthData, { houseSystem: "koch" });
// Validate birth data before calculation
const validation = validateBirth(birthData);
if (!validation.valid) {
console.log(validation.errors);
}
// Format chart for display
console.log(formatChart(chart, "text"));
// Get available house systems
const systems = getAvailableHouseSystems(); // ['placidus', 'koch', ...]
Features:
Calculate when current planetary positions form aspects to natal chart positions — the foundation of predictive astrology.
import { transits, time, type NatalPoint } from "celestine";
// Define natal points from a birth chart
const natalPoints: NatalPoint[] = [
{ name: "Sun", longitude: 280.37, type: "planet" },
{ name: "Moon", longitude: 223.32, type: "luminary" },
{ name: "ASC", longitude: 101.65, type: "angle" },
];
// Calculate transits at a specific moment
const jd = time.toJulianDate({ year: 2025, month: 12, day: 19, hour: 12 });
const result = transits.calculateTransits(natalPoints, jd);
for (const transit of result.transits) {
console.log(transits.formatTransit(transit));
// "Saturn □ Sun (1°23', 85%, applying)"
}
// Search transits over a date range
const searchResult = transits.searchTransits({
natalPoints,
startJD: jd,
endJD: jd + 365, // One year
transitingBodies: [
transits.CelestialBody.Saturn,
transits.CelestialBody.Jupiter,
],
});
// Get timing details for each transit
for (const timing of searchResult.transits) {
console.log(
`${timing.transit.transitingBody} ${timing.transit.symbol} ${timing.transit.natalPoint}`
);
console.log(
` Enters orb: ${timing.enterOrbDate?.month}/${timing.enterOrbDate?.day}`
);
console.log(
` Exact: ${timing.exactDates[0]?.month}/${timing.exactDates[0]?.day}`
);
console.log(
` Exits orb: ${timing.exitOrbDate?.month}/${timing.exitOrbDate?.day}`
);
}
// Detect house ingresses
const houseCusps = [0, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300, 330];
const ingresses = transits.calculateAllIngresses(
transits.CelestialBody.Mars,
houseCusps,
jd,
jd + 365
);
// Find retrograde periods
const mercuryRx = transits.findRetrogradePeriods(
transits.CelestialBody.Mercury,
jd,
jd + 365
);
for (const period of mercuryRx) {
console.log(transits.formatRetrogradePeriod(period));
// "Mercury Rx: Dec 13 - Jan 2 (21 days), 8°23' Capricorn → 22°10' Sagittarius"
}
Transit Types:
Features:
Calculate how the natal chart evolves over time using secondary progressions, solar arc directions, and other progression techniques.
import {
progressions,
calculateProgression,
formatProgressedChart,
} from "celestine";
// Define birth data
const birth = {
year: 1990,
month: 6,
day: 15,
hour: 14,
minute: 30,
second: 0,
timezone: -5,
latitude: 40.7128, // New York
longitude: -74.006,
};
// Calculate secondary progressions for a target date
const target = { year: 2024, month: 1, day: 1 };
const result = calculateProgression(birth, target);
console.log(`Age: ${result.ageAtTarget.toFixed(1)} years`);
console.log(`Solar Arc: ${result.solarArc.toFixed(2)}°`);
// View progressed positions
for (const body of result.bodies) {
console.log(`${body.name}: ${body.progressedFormatted}`);
if (body.hasChangedSign) {
console.log(
` ★ Changed from ${body.natalSignName} to ${body.progressedSignName}`
);
}
}
// Check progressed angles
console.log(`Progressed ASC: ${result.angles.ascendant.progressedFormatted}`);
console.log(`Progressed MC: ${result.angles.midheaven.progressedFormatted}`);
// View aspects between progressed and natal positions
for (const aspect of result.aspects.slice(0, 5)) {
console.log(
`${aspect.progressedBody} ${aspect.aspectType} ${aspect.natalBody}`
);
}
// Get detailed progressed Moon report
const moonReport = progressions.getProgressedMoonReport(birth, target);
console.log(`Moon Phase: ${moonReport.phase.phaseName}`);
console.log(`Moon Sign Transits:`);
for (const transit of moonReport.signTransits.slice(0, 5)) {
console.log(
` ${transit.signName}: age ${transit.entryAge.toFixed(
1
)} - ${transit.exitAge.toFixed(1)}`
);
}
// Use solar arc directions
const solarArc = progressions.calculateSolarArc(
result.birthJD,
result.targetJD
);
console.log(`Solar Arc: ${progressions.formatSolarArc(solarArc)}`);
// Format complete result
console.log(formatProgressedChart(result));
Progression Types:
Features:
Full API documentation is available at https://anonyfox.github.io/celestine
npm install # Install dependencies
npm test # Run tests
npm run build # Build package
npm run docs # Generate documentation
npm run lint # Check linting
npm run format # Format code