swisseph-wasm

SwissEph WebAssembly Library

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

🌟 Features


β˜• Enjoying this project? Support development on Ko-fi to help keep it free and actively maintained!


πŸš€ Live Demo

Try it now: Interactive SwissEph Demo

Experience all features including:

πŸ“¦ Installation

npm

npm install swisseph-wasm

yarn

yarn add swisseph-wasm

pnpm

pnpm add swisseph-wasm

CDN (Browser)

<script type="module">
  import SwissEph from 'https://cdn.jsdelivr.net/gh/prolaxu/swisseph-wasm@main/src/swisseph.js';
  // Your code here
</script>

πŸ“¦ What’s Included

πŸš€ Quick Start

πŸ‘€ Want to see it in action first? Try the Interactive Demo

Basic Usage

import SwissEph from 'swisseph-wasm';

// Create and initialize
const swe = new SwissEph();
await swe.initSwissEph();

// Calculate planetary position
const jd = swe.julday(2023, 6, 15, 12.0); // June 15, 2023, 12:00 UTC
const sunPos = swe.calc_ut(jd, swe.SE_SUN, swe.SEFLG_SWIEPH);

console.log(`Sun longitude: ${sunPos[0]}Β°`);

// Clean up
swe.close();

Birth Chart Example

import SwissEph from 'swisseph-wasm';

// You can also import the example calculator
// import { BirthChartCalculator } from 'swisseph-wasm/examples/birth-chart.js';

const calculator = new BirthChartCalculator();

const chart = await calculator.calculateBirthChart({
  year: 1990, month: 5, day: 15,
  hour: 14, minute: 30, timezone: -5,
  latitude: 40.7128, longitude: -74.0060
});

console.log('Planetary Positions:');
Object.entries(chart.planets).forEach(([name, planet]) => {
  console.log(`${planet.symbol} ${name}: ${planet.zodiacSign.formatted}`);
});

calculator.destroy();

🌐 Cross-Platform Usage

SwissEph WebAssembly works seamlessly across different environments. Here are platform-specific setup instructions:

Node.js

import SwissEph from 'swisseph-wasm';

async function nodeExample() {
  const swe = new SwissEph();
  await swe.initSwissEph();

  const jd = swe.julday(2023, 6, 15, 12.0);
  const sunPos = swe.calc_ut(jd, swe.SE_SUN, swe.SEFLG_SWIEPH);

  console.log(`Sun longitude: ${sunPos[0].toFixed(2)}Β°`);
  swe.close();
}

nodeExample().catch(console.error);

Vite (Vue.js, React, etc.)

Add this configuration to your vite.config.js:

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue' // or react, etc.

export default defineConfig({
  plugins: [vue()],
  server: {
    fs: {
      allow: ['..']
    }
  },
  assetsInclude: ['**/*.wasm'],
  optimizeDeps: {
    exclude: ['swisseph-wasm']
  }
})

Vue.js Component

<script setup>
import SwissEph from 'swisseph-wasm';
import { onMounted, onUnmounted, ref } from 'vue';

const swe = ref(null);
const isInitialized = ref(false);
const result = ref('');

onMounted(async () => {
  try {
    swe.value = new SwissEph();
    await swe.value.initSwissEph();
    isInitialized.value = true;
  } catch (error) {
    console.error('Failed to initialize SwissEph:', error);
  }
});

onUnmounted(() => {
  if (swe.value) {
    swe.value.close();
  }
});

const calculate = () => {
  if (!isInitialized.value) return;

  const jd = swe.value.julday(2023, 6, 15, 12.0);
  const sunPos = swe.value.calc_ut(jd, swe.value.SE_SUN, swe.value.SEFLG_SWIEPH);
  result.value = `Sun: ${sunPos[0].toFixed(2)}Β°`;
};
</script>

<template>
  <div>
    <button @click="calculate" :disabled="!isInitialized">
      Calculate Sun Position
    </button>
    <p></p>
  </div>
</template>

React Component

import React, { useState, useEffect } from 'react';
import SwissEph from 'swisseph-wasm';

function AstrologyCalculator() {
  const [swe, setSwe] = useState(null);
  const [isInitialized, setIsInitialized] = useState(false);
  const [result, setResult] = useState('');

  useEffect(() => {
    const initSwissEph = async () => {
      try {
        const swissEph = new SwissEph();
        await swissEph.initSwissEph();
        setSwe(swissEph);
        setIsInitialized(true);
      } catch (error) {
        console.error('Failed to initialize SwissEph:', error);
      }
    };

    initSwissEph();

    return () => {
      if (swe) {
        swe.close();
      }
    };
  }, []);

  const calculate = () => {
    if (!isInitialized || !swe) return;

    const jd = swe.julday(2023, 6, 15, 12.0);
    const sunPos = swe.calc_ut(jd, swe.SE_SUN, swe.SEFLG_SWIEPH);
    setResult(`Sun: ${sunPos[0].toFixed(2)}Β°`);
  };

  return (
    <div>
      <button onClick={calculate} disabled={!isInitialized}>
        Calculate Sun Position
      </button>
      <p>{result}</p>
    </div>
  );
}

export default AstrologyCalculator;

Vanilla HTML + JavaScript

<!DOCTYPE html>
<html>
<head>
    <title>SwissEph Example</title>
</head>
<body>
    <button id="calculate">Calculate Sun Position</button>
    <div id="result"></div>

    <script type="module">
        import SwissEph from 'https://cdn.jsdelivr.net/gh/prolaxu/swisseph-wasm@main/src/swisseph.js';

        let swe = null;
        let isInitialized = false;

        // Initialize SwissEph
        (async () => {
            try {
                swe = new SwissEph();
                await swe.initSwissEph();
                isInitialized = true;
                console.log('SwissEph initialized successfully');
            } catch (error) {
                console.error('Failed to initialize SwissEph:', error);
            }
        })();

        // Calculate button handler
        document.getElementById('calculate').addEventListener('click', () => {
            if (!isInitialized || !swe) {
                document.getElementById('result').textContent = 'SwissEph not initialized';
                return;
            }

            const jd = swe.julday(2023, 6, 15, 12.0);
            const sunPos = swe.calc_ut(jd, swe.SE_SUN, swe.SEFLG_SWIEPH);
            document.getElementById('result').textContent = `Sun: ${sunPos[0].toFixed(2)}Β°`;
        });

        // Cleanup on page unload
        window.addEventListener('beforeunload', () => {
            if (swe) {
                swe.close();
            }
        });
    </script>
</body>
</html>

Webpack Configuration

If using Webpack, add this to your webpack.config.js:

module.exports = {
  // ... other config
  experiments: {
    asyncWebAssembly: true,
  },
  module: {
    rules: [
      {
        test: /\.wasm$/,
        type: 'webassembly/async',
      },
    ],
  },
};

πŸ“š Documentation

Document Description
DOCUMENTATION.md Complete API reference and usage guide
QUICK_REFERENCE.md Quick developer reference
examples/README.md Example usage patterns
tests/README.md Test suite documentation

🎯 Core Capabilities

Planetary Calculations

Coordinate Systems

Time Functions

Advanced Features

πŸ§ͺ Testing

The library includes a comprehensive test suite with 106 tests:

npm install
npm test                 # Run all tests
npm run test:coverage   # Run with coverage report
npm run test:watch      # Watch mode

Test Coverage:

πŸ“ Project Structure

swiss-wasm/
β”œβ”€β”€ src/
β”‚   └── swisseph.js              # Main library file
β”œβ”€β”€ wasm/
β”‚   β”œβ”€β”€ swisseph.js              # WebAssembly module
β”‚   └── swisseph.wasm            # WebAssembly binary
β”œβ”€β”€ tests/
β”‚   β”œβ”€β”€ swisseph.test.js         # Core functionality tests
β”‚   β”œβ”€β”€ swisseph-advanced.test.js # Advanced features tests
β”‚   β”œβ”€β”€ swisseph-integration.test.js # Integration tests
β”‚   β”œβ”€β”€ swisseph-constants.test.js # Constants validation
β”‚   β”œβ”€β”€ __mocks__/               # Mock implementations
β”‚   └── README.md                # Test documentation
β”œβ”€β”€ examples/
β”‚   β”œβ”€β”€ basic-usage.js           # Basic usage examples
β”‚   β”œβ”€β”€ birth-chart.js           # Birth chart calculator
β”‚   └── README.md                # Examples documentation
β”œβ”€β”€ DOCUMENTATION.md             # Complete API reference
β”œβ”€β”€ QUICK_REFERENCE.md           # Quick developer guide
└── README.md                    # This file

🌐 Browser Support

Modern Browsers:

Requirements:

πŸ”§ Troubleshooting

Common Issues and Solutions

WASM Loading Errors in Vite/Vue.js

Error: WebAssembly.instantiate(): expected magic word 00 61 73 6d, found 3c 21 64 6f

Solution: Add Vite configuration to your vite.config.js:

export default defineConfig({
  plugins: [vue()],
  server: {
    fs: { allow: ['..'] }
  },
  assetsInclude: ['**/*.wasm'],
  optimizeDeps: {
    exclude: ['swisseph-wasm']
  }
})

Import Errors in Node.js

Error: Cannot read properties of undefined (reading 'ccall')

Solution: Always call await swe.initSwissEph() before using any methods:

const swe = new SwissEph();
await swe.initSwissEph(); // Required!
const jd = swe.julday(2023, 6, 15, 12.0);

Browser Process Errors

Error: process is not defined in browser

Solution: This is fixed in the latest version. Update to the latest version:

npm update swisseph-wasm

Memory Issues

Error: Out of memory or performance issues

Solution: Always call swe.close() when done:

try {
  const swe = new SwissEph();
  await swe.initSwissEph();
  // ... your calculations
} finally {
  swe.close(); // Always clean up!
}

CDN Import Issues

Error: Module not found when using CDN

Solution: Use the correct CDN URL:

// βœ… Correct
import SwissEph from 'https://cdn.jsdelivr.net/gh/prolaxu/swisseph-wasm@main/src/swisseph.js';

// ❌ Incorrect
import SwissEph from 'https://cdn.jsdelivr.net/npm/swisseph-wasm';

TypeScript Errors

Error: Type definitions not found

Solution: Types are included in the package:

import SwissEph from 'swisseph-wasm';
// Types are automatically available

Performance Tips

  1. Reuse instances: Create one SwissEph instance and reuse it for multiple calculations
  2. Batch calculations: Calculate multiple planets in one session rather than creating new instances
  3. Memory management: Always call close() when done
  4. Async initialization: Use await swe.initSwissEph() only once per instance

πŸ’‘ Examples

🌟 Interactive Examples: Try all these examples and more in the Live Demo

Current Planetary Positions

async function getCurrentPositions() {
  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];
  const positions = {};

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

  swe.close();
  return positions;
}

Sidereal vs Tropical

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

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

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

  // Sidereal (Lahiri)
  swe.set_sid_mode(swe.SE_SIDM_LAHIRI, 0, 0);
  const sidereal = swe.calc_ut(jd, swe.SE_SUN, swe.SEFLG_SWIEPH | swe.SEFLG_SIDEREAL);

  swe.close();

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

πŸ”§ Development

Running Examples

# Basic usage examples
node examples/basic-usage.js

# Birth chart calculation
node examples/birth-chart.js

Performance Tips

  1. Reuse instances for multiple calculations
  2. Batch operations in single sessions
  3. Always clean up with swe.close()
  4. Validate inputs before calculations
  5. Use appropriate flags for your needs

Error Handling

async function safeCalculation() {
  let swe = null;
  try {
    swe = new SwissEph();
    await swe.initSwissEph();
    // calculations here
    return result;
  } catch (error) {
    console.error('Calculation failed:', error);
    return null;
  } finally {
    if (swe) swe.close();
  }
}

πŸ“„ 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.

Swiss Ephemeris License:

🀝 Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new functionality
  4. Ensure all tests pass
  5. Update documentation
  6. Submit a pull request

πŸ“ž Support

πŸ† Credits

πŸ“‹ Project License

This project is licensed under GPL-3.0-or-later - see the LICENSE file for details.

βš–οΈ Swiss Ephemeris Licensing

IMPORTANT: This library incorporates the Swiss Ephemeris, which uses a dual licensing model:

πŸ†“ For Open Source Projects:

πŸ’Ό For Commercial/Proprietary Projects:

πŸ“ž Commercial Licensing Contact

For commercial use of Swiss Ephemeris:

Astrodienst AG πŸ“§ Email: swisseph@astro.ch 🌐 Website: https://www.astro.com/swisseph/ πŸ“ Address: Dammstrasse 23, CH-8702 Zollikon, Switzerland

πŸ” License Compliance Guide

Use Case License Required Action Needed
πŸ†“ Open Source Project GPL v3 βœ… Use freely, keep project open source
πŸ’Ό Commercial Product Commercial License ⚠️ Contact Astrodienst AG
πŸŽ“ Educational/Research GPL v3 βœ… Use freely for non-commercial purposes
🏒 Internal Business Tools Commercial License ⚠️ Contact Astrodienst AG

πŸ“š Additional Resources

⚠️ Disclaimer

The author of swisseph-wasm is not affiliated with Astrodienst AG and cannot provide commercial licenses for Swiss Ephemeris. This WebAssembly wrapper is provided β€œas is” under GPL v3. Users are responsible for ensuring compliance with Swiss Ephemeris licensing terms for their specific use case.

🀝 Contributing

We welcome contributions! Please feel free to submit a Pull Request.


Ready to calculate the cosmos? Start with the Quick Reference or dive into the Complete Documentation! 🌟