swisseph-wasm

SwissEph WebAssembly Library Documentation

A JavaScript wrapper for the Swiss Ephemeris WebAssembly module, providing high-precision astronomical calculations for astrology, astronomy, and related applications.

Table of Contents

Installation

Option 1: Direct Download

Download the library files and include them in your project:

src/swisseph.js       # Main library file
wasm/swisseph.js      # WebAssembly module
wasm/swisseph.wasm    # WebAssembly binary

Option 2: ES Module Import

import SwissEph from './src/swisseph.js';

Option 3: HTML Script Tag

<script type="module">
  import SwissEph from './src/swisseph.js';
  // Your code here
</script>

Quick Start

Basic Setup

import SwissEph from './src/swisseph.js';

// Create instance
const swe = new SwissEph();

// Initialize the WebAssembly module
await swe.initSwissEph();

// Calculate Julian Day
const jd = swe.julday(2023, 6, 15, 12.0); // June 15, 2023, 12:00 UTC

// Calculate Sun position
const sunPosition = swe.calc_ut(jd, swe.SE_SUN, swe.SEFLG_SWIEPH);
console.log(`Sun longitude: ${sunPosition[0]}°`);

// Clean up when done
swe.close();

Birth Chart Example

async function calculateBirthChart(year, month, day, hour, minute, timezone) {
  const swe = new SwissEph();
  await swe.initSwissEph();
  
  // Convert local time to UTC
  const utcHour = hour + minute / 60 - timezone;
  const jd = swe.julday(year, month, day, utcHour);
  
  // Define planets to calculate
  const planets = [
    { id: swe.SE_SUN, name: 'Sun' },
    { id: swe.SE_MOON, name: 'Moon' },
    { id: swe.SE_MERCURY, name: 'Mercury' },
    { id: swe.SE_VENUS, name: 'Venus' },
    { id: swe.SE_MARS, name: 'Mars' },
    { id: swe.SE_JUPITER, name: 'Jupiter' },
    { id: swe.SE_SATURN, name: 'Saturn' },
    { id: swe.SE_URANUS, name: 'Uranus' },
    { id: swe.SE_NEPTUNE, name: 'Neptune' },
    { id: swe.SE_PLUTO, name: 'Pluto' }
  ];
  
  const chart = {};
  
  for (const planet of planets) {
    const result = swe.calc_ut(jd, planet.id, swe.SEFLG_SWIEPH);
    chart[planet.name] = {
      longitude: result[0],
      latitude: result[1],
      distance: result[2],
      speed: result[3]
    };
  }
  
  swe.close();
  return chart;
}

// Usage
const chart = await calculateBirthChart(1990, 5, 15, 14, 30, -5); // May 15, 1990, 2:30 PM, UTC-5
console.log(chart);

API Reference

Core Methods

initSwissEph()

Initializes the WebAssembly module. Must be called before using any other methods.

await swe.initSwissEph();

julday(year, month, day, hour)

Calculates Julian Day Number for a given date and time.

const jd = swe.julday(2023, 6, 15, 12.5); // June 15, 2023, 12:30 UTC

Parameters:

Returns: Julian Day Number (number)

calc_ut(jd, planet, flags)

Calculates planetary positions for Universal Time.

const result = swe.calc_ut(jd, swe.SE_SUN, swe.SEFLG_SWIEPH);

Parameters:

Returns: Float64Array with [longitude, latitude, distance, speed]

calc(jd, planet, flags)

Calculates planetary positions (alternative method with more detailed output).

const result = swe.calc(jd, swe.SE_MOON, swe.SEFLG_SWIEPH);

Returns: Object with detailed position data:

{
  longitude: number,
  latitude: number,
  distance: number,
  longitudeSpeed: number,
  latitudeSpeed: number,
  distanceSpeed: number
}

Time Functions

deltat(jd)

Calculates Delta T (difference between Terrestrial Time and Universal Time).

const deltaT = swe.deltat(jd);

sidtime(jd)

Calculates sidereal time for a given Julian Day.

const siderealTime = swe.sidtime(jd);

utc_to_jd(year, month, day, hour, minute, second, gregflag)

Converts UTC time to Julian Day.

const result = swe.utc_to_jd(2023, 6, 15, 12, 30, 0, swe.SE_GREG_CAL);
// Returns: { julianDayET: number, julianDayUT: number }

Date Conversion Functions

revjul(jd, gregflag)

Converts Julian Day back to calendar date.

const date = swe.revjul(jd, swe.SE_GREG_CAL);
// Returns: { year: number, month: number, day: number, hour: number }

date_conversion(year, month, day, hour, gregflag)

Converts calendar date to Julian Day.

const jd = swe.date_conversion(2023, 6, 15, 12.5, swe.SE_GREG_CAL);

Utility Functions

degnorm(degrees)

Normalizes degrees to 0-360 range.

const normalized = swe.degnorm(370); // Returns 10

split_deg(degrees, roundflag)

Splits decimal degrees into degrees, minutes, seconds.

const split = swe.split_deg(123.456789, swe.SE_SPLIT_DEG_ROUND_SEC);
// Returns: { degree: 123, min: 27, second: 24, fraction: 0, sign: 4 }

day_of_week(jd)

Returns day of week for a Julian Day (0=Monday, 6=Sunday).

const dayOfWeek = swe.day_of_week(jd);

Sidereal Functions

set_sid_mode(sidmode, t0, ayan_t0)

Sets sidereal calculation mode.

swe.set_sid_mode(swe.SE_SIDM_LAHIRI, 0, 0);

get_ayanamsa(jd)

Gets ayanamsa value for sidereal calculations.

const ayanamsa = swe.get_ayanamsa(jd);

Information Functions

version()

Returns Swiss Ephemeris version string.

const version = swe.version();

get_planet_name(planet)

Returns planet name for a planet constant.

const name = swe.get_planet_name(swe.SE_SUN); // Returns "Sun"

Cleanup

close()

Closes the Swiss Ephemeris and frees memory. Call when done.

swe.close();

Examples

Example 1: Current Planetary Positions

async function getCurrentPlanetaryPositions() {
  const swe = new SwissEph();
  await swe.initSwissEph();
  
  const now = new Date();
  const jd = swe.julday(
    now.getUTCFullYear(),
    now.getUTCMonth() + 1,
    now.getUTCDate(),
    now.getUTCHours() + now.getUTCMinutes() / 60
  );
  
  const planets = [
    swe.SE_SUN, swe.SE_MOON, swe.SE_MERCURY, swe.SE_VENUS,
    swe.SE_MARS, swe.SE_JUPITER, swe.SE_SATURN
  ];
  
  const positions = {};
  
  for (const planet of planets) {
    const result = swe.calc_ut(jd, planet, swe.SEFLG_SWIEPH);
    const name = swe.get_planet_name(planet);
    positions[name] = result[0]; // longitude in degrees
  }
  
  swe.close();
  return positions;
}

Example 2: Sidereal vs Tropical Positions

async function compareSiderealTropical(year, month, day, hour) {
  const swe = new SwissEph();
  await swe.initSwissEph();

  const jd = swe.julday(year, month, day, hour);

  // Set Lahiri ayanamsa for sidereal calculations
  swe.set_sid_mode(swe.SE_SIDM_LAHIRI, 0, 0);

  // Tropical position
  const tropical = swe.calc_ut(jd, swe.SE_SUN, swe.SEFLG_SWIEPH);

  // Sidereal position
  const sidereal = swe.calc_ut(jd, swe.SE_SUN, swe.SEFLG_SWIEPH | swe.SEFLG_SIDEREAL);

  // Get ayanamsa value
  const ayanamsa = swe.get_ayanamsa(jd);

  swe.close();

  return {
    tropical: tropical[0],
    sidereal: sidereal[0],
    ayanamsa: ayanamsa,
    difference: tropical[0] - sidereal[0]
  };
}

Example 3: Time Zone Conversion

async function calculateWithTimeZone(year, month, day, hour, minute, second, timezone) {
  const swe = new SwissEph();
  await swe.initSwissEph();

  // Convert to UTC using built-in function
  const utcResult = swe.utc_to_jd(year, month, day, hour, minute, second, swe.SE_GREG_CAL);

  // Adjust for timezone
  const localJD = utcResult.julianDayUT - timezone / 24;

  // Calculate planetary positions
  const sunPos = swe.calc_ut(localJD, swe.SE_SUN, swe.SEFLG_SWIEPH);

  swe.close();

  return {
    julianDay: localJD,
    sunLongitude: sunPos[0],
    deltaT: swe.deltat(localJD)
  };
}

Example 4: Moon Phases

async function getMoonPhase(year, month, day) {
  const swe = new SwissEph();
  await swe.initSwissEph();

  const jd = swe.julday(year, month, day, 12.0);

  // Get Sun and Moon positions
  const sunPos = swe.calc_ut(jd, swe.SE_SUN, swe.SEFLG_SWIEPH);
  const moonPos = swe.calc_ut(jd, swe.SE_MOON, swe.SEFLG_SWIEPH);

  // Calculate phase angle
  let phaseAngle = moonPos[0] - sunPos[0];
  if (phaseAngle < 0) phaseAngle += 360;

  // Determine phase
  let phase;
  if (phaseAngle < 45 || phaseAngle > 315) phase = "New Moon";
  else if (phaseAngle < 135) phase = "Waxing";
  else if (phaseAngle < 225) phase = "Full Moon";
  else phase = "Waning";

  swe.close();

  return {
    phaseAngle: phaseAngle,
    phase: phase,
    sunLongitude: sunPos[0],
    moonLongitude: moonPos[0]
  };
}

Constants

Planet Constants

// Major planets
swe.SE_SUN = 0;
swe.SE_MOON = 1;
swe.SE_MERCURY = 2;
swe.SE_VENUS = 3;
swe.SE_MARS = 4;
swe.SE_JUPITER = 5;
swe.SE_SATURN = 6;
swe.SE_URANUS = 7;
swe.SE_NEPTUNE = 8;
swe.SE_PLUTO = 9;
swe.SE_EARTH = 14;

// Lunar nodes and apogee
swe.SE_MEAN_NODE = 10;
swe.SE_TRUE_NODE = 11;
swe.SE_MEAN_APOG = 12;
swe.SE_OSCU_APOG = 13;

// Major asteroids
swe.SE_CHIRON = 15;
swe.SE_PHOLUS = 16;
swe.SE_CERES = 17;
swe.SE_PALLAS = 18;
swe.SE_JUNO = 19;
swe.SE_VESTA = 20;

// Uranian planets
swe.SE_CUPIDO = 40;
swe.SE_HADES = 41;
swe.SE_ZEUS = 42;
swe.SE_KRONOS = 43;
swe.SE_APOLLON = 44;
swe.SE_ADMETOS = 45;
swe.SE_VULKANUS = 46;
swe.SE_POSEIDON = 47;

Calculation Flags

// Ephemeris types
swe.SEFLG_JPLEPH = 1;      // JPL ephemeris
swe.SEFLG_SWIEPH = 2;      // Swiss ephemeris (default)
swe.SEFLG_MOSEPH = 4;      // Moshier ephemeris

// Coordinate systems
swe.SEFLG_HELCTR = 8;      // Heliocentric
swe.SEFLG_BARYCTR = 16384; // Barycentric
swe.SEFLG_TOPOCTR = 32768; // Topocentric
swe.SEFLG_EQUATORIAL = 2048; // Equatorial coordinates
swe.SEFLG_XYZ = 4096;      // Cartesian coordinates
swe.SEFLG_RADIANS = 8192;  // Radians instead of degrees

// Special flags
swe.SEFLG_SPEED = 256;     // Calculate speed
swe.SEFLG_TRUEPOS = 16;    // True positions (no light-time correction)
swe.SEFLG_J2000 = 32;      // J2000 coordinates
swe.SEFLG_NONUT = 64;      // No nutation
swe.SEFLG_NOGDEFL = 512;   // No gravitational deflection
swe.SEFLG_NOABERR = 1024;  // No aberration
swe.SEFLG_SIDEREAL = 65536; // Sidereal positions

// Composite flags
swe.SEFLG_ASTROMETRIC = 1536; // No aberration + no gravitational deflection

Sidereal Modes

swe.SE_SIDM_FAGAN_BRADLEY = 0;
swe.SE_SIDM_LAHIRI = 1;
swe.SE_SIDM_DELUCE = 2;
swe.SE_SIDM_RAMAN = 3;
swe.SE_SIDM_USHASHASHI = 4;
swe.SE_SIDM_KRISHNAMURTI = 5;
swe.SE_SIDM_DJWHAL_KHUL = 6;
swe.SE_SIDM_YUKTESHWAR = 7;
swe.SE_SIDM_JN_BHASIN = 8;
swe.SE_SIDM_BABYL_KUGLER1 = 9;
swe.SE_SIDM_BABYL_KUGLER2 = 10;
swe.SE_SIDM_BABYL_KUGLER3 = 11;
swe.SE_SIDM_BABYL_HUBER = 12;
swe.SE_SIDM_BABYL_ETPSC = 13;
swe.SE_SIDM_ALDEBARAN_15TAU = 14;
swe.SE_SIDM_HIPPARCHOS = 15;
swe.SE_SIDM_SASSANIAN = 16;
swe.SE_SIDM_GALCENT_0SAG = 17;
swe.SE_SIDM_J2000 = 18;
swe.SE_SIDM_J1900 = 19;
swe.SE_SIDM_B1950 = 20;
swe.SE_SIDM_USER = 255;

Calendar Types

swe.SE_JUL_CAL = 0;   // Julian calendar
swe.SE_GREG_CAL = 1;  // Gregorian calendar

Degree Splitting Flags

swe.SE_SPLIT_DEG_ROUND_SEC = 1;
swe.SE_SPLIT_DEG_ROUND_MIN = 2;
swe.SE_SPLIT_DEG_ROUND_DEG = 4;
swe.SE_SPLIT_DEG_ZODIACAL = 8;
swe.SE_SPLIT_DEG_KEEP_SIGN = 16;
swe.SE_SPLIT_DEG_KEEP_DEG = 32;

Advanced Usage

Working with Fixed Stars

async function getFixedStarPosition(starName, year, month, day) {
  const swe = new SwissEph();
  await swe.initSwissEph();

  const jd = swe.julday(year, month, day, 0);

  // Calculate fixed star position
  const starPos = swe.fixstar(starName, jd, swe.SEFLG_SWIEPH);

  if (starPos) {
    const result = {
      name: starName,
      longitude: starPos[0],
      latitude: starPos[1],
      distance: starPos[2],
      magnitude: swe.fixstar_mag(starName)
    };

    swe.close();
    return result;
  }

  swe.close();
  return null;
}

// Usage
const sirius = await getFixedStarPosition("Sirius", 2023, 6, 15);

House Calculations

async function calculateHouses(year, month, day, hour, latitude, longitude, houseSystem = 'P') {
  const swe = new SwissEph();
  await swe.initSwissEph();

  const jd = swe.julday(year, month, day, hour);

  // Calculate houses
  const houses = swe.houses(jd, latitude, longitude, houseSystem);

  // Calculate house positions for planets
  const planets = [swe.SE_SUN, swe.SE_MOON, swe.SE_MERCURY, swe.SE_VENUS, swe.SE_MARS];
  const planetHouses = {};

  for (const planet of planets) {
    const planetPos = swe.calc_ut(jd, planet, swe.SEFLG_SWIEPH);
    const housePos = swe.house_pos(
      swe.sidtime(jd) * 15, // ARMC
      latitude,
      23.44, // obliquity
      houseSystem,
      planetPos[0],
      planetPos[1]
    );

    planetHouses[swe.get_planet_name(planet)] = {
      longitude: planetPos[0],
      house: Math.floor(housePos)
    };
  }

  swe.close();

  return {
    houses: houses,
    planetHouses: planetHouses
  };
}

Eclipse Calculations

async function findNextSolarEclipse(startYear, startMonth, startDay) {
  const swe = new SwissEph();
  await swe.initSwissEph();

  const startJD = swe.julday(startYear, startMonth, startDay, 12);

  // Find next solar eclipse
  const eclipse = swe.sol_eclipse_when_glob(
    startJD,
    swe.SEFLG_SWIEPH,
    swe.SE_ECL_TOTAL | swe.SE_ECL_ANNULAR | swe.SE_ECL_PARTIAL,
    0 // forward search
  );

  if (eclipse) {
    const eclipseDate = swe.revjul(eclipse[1], swe.SE_GREG_CAL);

    swe.close();

    return {
      julianDay: eclipse[1],
      date: eclipseDate,
      type: eclipse[0] & swe.SE_ECL_TOTAL ? 'Total' :
            eclipse[0] & swe.SE_ECL_ANNULAR ? 'Annular' : 'Partial',
      magnitude: eclipse[4]
    };
  }

  swe.close();
  return null;
}

Topocentric Calculations

async function calculateTopocentric(year, month, day, hour, latitude, longitude, altitude) {
  const swe = new SwissEph();
  await swe.initSwissEph();

  // Set topocentric location
  swe.set_topo(longitude, latitude, altitude);

  const jd = swe.julday(year, month, day, hour);

  // Calculate topocentric positions
  const planets = [swe.SE_SUN, swe.SE_MOON, swe.SE_VENUS, swe.SE_MARS];
  const positions = {};

  for (const planet of planets) {
    // Geocentric position
    const geocentric = swe.calc_ut(jd, planet, swe.SEFLG_SWIEPH);

    // Topocentric position
    const topocentric = swe.calc_ut(jd, planet, swe.SEFLG_SWIEPH | swe.SEFLG_TOPOCTR);

    positions[swe.get_planet_name(planet)] = {
      geocentric: geocentric[0],
      topocentric: topocentric[0],
      difference: geocentric[0] - topocentric[0]
    };
  }

  swe.close();
  return positions;
}

Error Handling

Best Practices

async function safeCalculation(year, month, day, hour) {
  let swe = null;

  try {
    swe = new SwissEph();
    await swe.initSwissEph();

    // Validate input
    if (year < -5000 || year > 5000) {
      throw new Error('Year out of valid range (-5000 to 5000)');
    }

    if (month < 1 || month > 12) {
      throw new Error('Month must be between 1 and 12');
    }

    if (day < 1 || day > 31) {
      throw new Error('Day must be between 1 and 31');
    }

    if (hour < 0 || hour >= 24) {
      throw new Error('Hour must be between 0 and 23.999');
    }

    const jd = swe.julday(year, month, day, hour);

    // Check for valid Julian Day
    if (isNaN(jd) || jd === swe.TJD_INVALID) {
      throw new Error('Invalid Julian Day calculated');
    }

    const result = swe.calc_ut(jd, swe.SE_SUN, swe.SEFLG_SWIEPH);

    if (!result || result.length < 4) {
      throw new Error('Failed to calculate planetary position');
    }

    return {
      success: true,
      julianDay: jd,
      longitude: result[0],
      latitude: result[1],
      distance: result[2],
      speed: result[3]
    };

  } catch (error) {
    return {
      success: false,
      error: error.message
    };
  } finally {
    // Always clean up
    if (swe) {
      swe.close();
    }
  }
}

Performance Tips

1. Reuse SwissEph Instance

class AstrologyCalculator {
  constructor() {
    this.swe = null;
    this.initialized = false;
  }

  async init() {
    if (!this.initialized) {
      this.swe = new SwissEph();
      await this.swe.initSwissEph();
      this.initialized = true;
    }
  }

  async calculateChart(year, month, day, hour) {
    await this.init();

    const jd = this.swe.julday(year, month, day, hour);
    const planets = [this.swe.SE_SUN, this.swe.SE_MOON, this.swe.SE_MERCURY];
    const chart = {};

    for (const planet of planets) {
      const result = this.swe.calc_ut(jd, planet, this.swe.SEFLG_SWIEPH);
      chart[this.swe.get_planet_name(planet)] = result[0];
    }

    return chart;
  }

  destroy() {
    if (this.swe) {
      this.swe.close();
      this.swe = null;
      this.initialized = false;
    }
  }
}

// Usage
const calculator = new AstrologyCalculator();
const chart1 = await calculator.calculateChart(1990, 5, 15, 14.5);
const chart2 = await calculator.calculateChart(1985, 12, 25, 8.0);
calculator.destroy(); // Clean up when done

2. Batch Calculations

async function batchCalculatePlanets(jd, planetList, flags) {
  const swe = new SwissEph();
  await swe.initSwissEph();

  const results = {};

  // Calculate all planets in one session
  for (const planet of planetList) {
    const result = swe.calc_ut(jd, planet, flags);
    results[swe.get_planet_name(planet)] = result;
  }

  swe.close();
  return results;
}

Browser Support

Modern Browsers

Requirements

Polyfills

For older browsers, you may need:

Testing

The library includes comprehensive tests. To run them:

npm install
npm test

Test Coverage

Troubleshooting

Common Issues

  1. WebAssembly not loading
    // Ensure proper path to WASM files
    // Check browser console for loading errors
    
  2. Invalid Julian Day
    // Validate date inputs before calculation
    if (isNaN(jd) || jd === swe.TJD_INVALID) {
      throw new Error('Invalid date');
    }
    
  3. Memory leaks
    // Always call close() when done
    try {
      // calculations
    } finally {
      swe.close();
    }
    
  4. Incorrect time zones
    // Convert to UTC before calculation
    const utcHour = localHour - timezoneOffset;
    

License

This library is a wrapper around the Swiss Ephemeris, which is licensed under the GNU General Public License (GPL) for non-commercial use. For commercial use, a license from Astrodienst is required.

Support

For issues and questions:

Version History