swisseph
Dart FFI bindings to the Swiss Ephemeris C library.
Compute planetary positions, house cusps, ayanamsa values, rise/set times, eclipses, crossings, heliacal events, and coordinate transforms — with no code generation, no Flutter dependency, and full isolate safety.
Features
- ~88 methods covering the near-complete Swiss Ephemeris API
- All 47 standard ayanamsas — Lahiri, Fagan-Bradley, Raman, Krishnamurti, and 43 more
- 14 house systems — Placidus, Koch, Whole Sign, Campanus, Equal, Gauquelin sectors, and more
- Solar and lunar eclipses — global and local search, attributes, geographic location of greatest eclipse
- Planetary occultations — lunar occultation search and location
- Sun/Moon/heliocentric crossings — find when bodies cross a longitude
- Heliacal events — rising, setting, visibility limits
- Coordinate transforms — horizon, ecliptic, equatorial, refraction
- Nodes and apsides — mean, osculating, barycentric
- Orbital elements — osculating elements, distance extremes
- Phase and magnitude — elongation, phase angle, apparent magnitude
- Three ephemeris engines — Moshier (no files needed), Swiss Ephemeris (.se1 files), JPL
- Multiple coordinate systems — tropical, sidereal, heliocentric, barycentric, equatorial, topocentric
- Isolate-safe — each instance wraps its own
DynamicLibrary; copy the.soper isolate for independent C global state - Instance-based API — no singletons, no hidden state
- Native asset build hook —
dart pub getcompiles the vendored C source automatically viaCBuilder
Platform support
| Platform | Status |
|---|---|
| Linux | Supported (gcc/clang) |
| macOS | Supported (clang) |
| Windows | Supported (MSVC) |
| Android | Supported (NDK) |
| Web | Supported (WASM via Emscripten) |
Native platforms require Dart SDK 3.11+ and a C compiler.
Web requires the pre-built assets/swisseph.{js,wasm} served from your app.
Install
dart pub add swisseph
Or add it to your pubspec.yaml directly:
dependencies:
swisseph: ^0.5.0
Alternatively, install from the Git repository:
dependencies:
swisseph:
git:
url: https://gitlab.com/ninthhouse/swisseph.dart
ref: master
Then run dart pub get, which triggers the native build hook to compile
the C source.
Quick start
Cross-platform (recommended)
import 'package:swisseph/swisseph.dart';
void main() async {
final swe = await SwissEph.load();
final jd = swe.julday(2000, 1, 1, 12.0);
final sun = swe.calcUt(jd, seSun, seFlgMosEph | seFlgSpeed);
print('Sun: ${sun.longitude}°');
swe.close();
}
SwissEph.load() works on both native and web. On native it finds the
build hook output automatically; on web it loads the WASM module.
Native only
SwissEph.find() scans .dart_tool/ for the build-hook output, which is
fine during local development from the package root. In production
deployments — compiled binaries, apps launched from a different working
directory, or non-default native-asset layouts — pass an explicit path
to SwissEph.load(path) or the SwissEph(path) constructor. The Dart
native-assets pipeline is the supported production loading path.
import 'package:swisseph/swisseph.dart';
void main() {
final swe = SwissEph.find();
// Julian Day for 2000-01-01 12:00 UT
final jd = swe.julday(2000, 1, 1, 12.0);
// Sun position (Moshier — no data files needed)
final sun = swe.calcUt(jd, seSun, seFlgMosEph | seFlgSpeed);
print('Sun: ${sun.longitude}°');
// Sidereal position (Lahiri)
swe.setSidMode(seSidmLahiri);
final sidSun = swe.calcUt(
jd, seSun, seFlgMosEph | seFlgSpeed | seFlgSidereal,
);
print('Sidereal Sun: ${sidSun.longitude}°');
// House cusps (Campanus, Washington DC)
final houses = swe.houses(jd, 38.8977, -77.0365, hsysCampanus);
print('Ascendant: ${houses.ascendant}°');
print('MC: ${houses.mc}°');
// Sunrise
final rise = swe.riseTrans(
jd, seSun,
rsmi: seCalcRise, geolon: -77.0365, geolat: 38.8977,
);
print('Sunrise JD: ${rise.transitTime}');
// Next solar eclipse visible from Washington DC
final eclipse = swe.solEclipseWhenLoc(
jd, seFlgSwiEph,
geolon: -77.0365, geolat: 38.8977,
);
print('Next solar eclipse: JD ${eclipse.maxEclipse}');
// Next Sun crossing of 0° Aries
final cross = swe.solCrossUt(0.0, jd, seFlgSwiEph);
print('Next vernal equinox: JD $cross');
swe.close();
}
See example/example.dart for a fuller example
with formatted output.
Web setup
Serve the pre-built WASM assets from your web app:
- Copy
assets/swisseph.jsandassets/swisseph.wasmto your web app's asset directory - Load the
swisseph.jsscript in your HTML - Use
SwissEph.load()in your Dart code, orSwissEph.load('assets/swisseph')if you serve the module under a non-default URL (the path is passed through to wasm_ffi, which auto-resolves the.js/.wasmextension).
The WASM module exports all 94 Swiss Ephemeris functions (~539 KB wasm + ~81 KB JS glue). By default, web uses Moshier ephemeris (no files needed). For Swiss Ephemeris precision, load .se1 files into the in-memory virtual filesystem:
final swe = await SwissEph.load();
// Fetch .se1 files (your app's fetch logic — HTTP, Flutter asset bundle, etc.)
final planetBytes = await fetchFile('sepl_18.se1');
final moonBytes = await fetchFile('semo_18.se1');
// Push into MEMFS
swe.loadEpheFile('sepl_18.se1', planetBytes);
swe.loadEpheFile('semo_18.se1', moonBytes);
swe.setEphePath('/ephe');
// Now calcUt with seFlgSwiEph uses Swiss Ephemeris precision
final sun = swe.calcUt(jd, seSun, seFlgSwiEph | seFlgSpeed);
Only load the files you need — current-era planet + moon files are ~5 MB uncompressed (~3 MB gzipped).
API reference
| Category | Methods |
|---|---|
| Date/time | julday, revjul, utcToJd, jdToUtc, jdetToUtc, utcTimeZone, dayOfWeek, deltat, deltatEx, timeEqu, sidTime, sidTime0, lmtToLat, latToLmt |
| Config | setEphePath, setSidMode, setTopo, setJplFile, setInterpolateNut, setLapseRate, setDeltaTUserdef, setTidAcc, getTidAcc, getLibraryPath, getCurrentFileData, loadEpheFile, close, version |
| Positions | calcUt, calc |
| Houses | houses, housesEx, housesEx2, housesArmc, housesArmcEx2, housePos, gauquelinSector |
| Ayanamsa | getAyanamsaUt, getAyanamsa, getAyanamsaExUt, getAyanamsaEx, getAyanamsaName |
| Fixed stars | fixstar2Ut, fixstar2, fixstar2Mag |
| Eclipses | solEclipseWhenLoc, solEclipseWhenGlob, solEclipseHow, solEclipseWhere, lunEclipseWhen, lunEclipseWhenLoc, lunEclipseHow, lunOccultWhenLoc, lunOccultWhenGlob, lunOccultWhere |
| Crossings | solCrossUt, solCross, moonCrossUt, moonCross, moonCrossNodeUt, moonCrossNode, helioCrossUt, helioCross |
| Rise/set | riseTrans, riseTransTrueHor |
| Nodes | nodApsUt, nodAps |
| Orbits | getOrbitalElements, orbitMaxMinTrueDistance |
| Phenomena | phenoUt, pheno |
| Heliacal | heliacalUt, heliacalPhenoUt, visLimitMag |
| Coordinates | azAlt, azAltRev, cotrans, refrac, refracExtended |
| Names | getPlanetName, houseName |
| Utilities | degnorm, radNorm, degMidp, radMidp, difDegn, difDeg2n, splitDeg |
All native memory uses Arena-scoped using() allocation. Errors throw
SweException.
Ephemeris modes
| Mode | Flag | Files needed | Accuracy |
|---|---|---|---|
| Moshier | seFlgMosEph |
None | ~1 arcsecond |
| Swiss Ephemeris | seFlgSwiEph |
.se1 data files |
Sub-arcsecond |
| JPL | seFlgJplEph |
JPL ephemeris files | Highest |
Moshier works out of the box with no data files but is limited to ~1 arcsecond accuracy.
Bundled ephemeris files
This package includes Swiss Ephemeris data files in ephe/ for
sub-arcsecond precision with no extra downloads:
| Files | Contents | Coverage |
|---|---|---|
sepl_*.se1 / seplm*.se1 |
Main planets (Sun–Pluto, lunar nodes) | 5400 BC – 5400 AD |
semo_*.se1 / semom*.se1 |
Moon (high precision) | 5400 BC – 5400 AD |
seas_*.se1 / seasm*.se1 |
Main asteroids (Ceres, Pallas, Juno, Vesta, Chiron, Pholus) | 5400 BC – 5400 AD |
sefstars.txt |
~2,900 named fixed stars (expanded SIMBAD catalog) | Current epoch |
ast0/se00010s.se1 |
Asteroid 10 (Hygiea) | Short range |
Each .se1 file covers a 600-year range. The _00 through _48 files
cover 0 AD – 5400 AD; the m06 through m54 files cover
5400 BC – 0 AD. Together they span roughly 10,800 years — more than
enough for any practical astrological or historical use.
To use the bundled files, point setEphePath to the ephe/ directory
inside the installed package. Use Isolate.resolvePackageUri to find it
at runtime:
import 'dart:isolate';
final pkgUri = Uri.parse('package:swisseph/swisseph.dart');
final resolved = await Isolate.resolvePackageUri(pkgUri);
final ephePath = resolved!.resolve('../ephe').toFilePath();
swe.setEphePath(ephePath);
final sun = swe.calcUt(jd, seSun, seFlgSwiEph | seFlgSpeed);
This resolves to the ephe/ directory in the pub cache (e.g.
~/.pub-cache/hosted/pub.dev/swisseph-0.2.0/ephe/) regardless of
where your project lives.
What's not included
- JPL ephemeris files — for highest-precision research use
- Additional numbered asteroids — only Hygiea (10) is bundled; thousands more are available separately
- Dates beyond ~5400 BC / 5400 AD — the library falls back to Moshier for dates outside the file range
The full set of ephemeris files (including additional asteroids and
extended date ranges) is available from
astro.com/ftp/swisseph/ephe.
Download any additional files you need into the same ephe/ directory.
Constants
Integer constants, not enums. Defined in lib/src/constants.dart.
| Prefix | Purpose | Example |
|---|---|---|
se |
Body IDs | seSun, seMoon |
seFlg |
Calculation flags | seFlgSpeed |
hsys |
House systems | hsysPlacidus |
seSidm |
Ayanamsa modes | seSidmLahiri |
seCalc |
Rise/set flags | seCalcRise |
seEcl |
Eclipse types | seEclTotal |
seNodBit |
Node/apsides flags | seNodBitMean |
seBit |
Rise/transit flags | seBitDiscCenter |
seHelFlag |
Heliacal flags | seHelFlagLongSearch |
seSidBit |
Sidereal mode bits | seSidBitEclT0 |
Isolate safety
dlopen deduplicates shared libraries by device+inode. Two isolates
loading the same .so path share C global state — sidereal mode,
ephemeris path, and topocentric coordinates all leak between them.
The fix: copy the .so to a unique temp path per isolate. Each copy
gets its own C globals.
// Copy the .so for this isolate
final tmpDir = Directory.systemTemp.createTempSync('swe_');
final libPath = '${tmpDir.path}/libswisseph_$id.so';
File(sharedLibPath).copySync(libPath);
final swe = SwissEph(libPath);
See test/isolate_test.dart for the full
pattern. The stress tests run up to
4.11 billion calculations
across 100 isolates covering all 88 public methods to prove this holds
at scale.
Limitations
- No Flutter plugin — this is a pure Dart package using native asset build hooks. Works with Dart CLI and can be used from Flutter, but is not a Flutter plugin.
- Web: no JPL ephemeris — JPL data files are not supported on web.
Swiss Ephemeris .se1 files can be loaded via
loadEpheFile(); Moshier works with no files. - Limited asteroid coverage — main asteroids (Ceres, Pallas, Juno, Vesta, Chiron, Pholus, Hygiea) are bundled. Thousands more numbered asteroids are available from astro.com but are untested with this package.
- C global state — the underlying C library uses global state. See Isolate safety above.
Tests
dart test
- 26 unit tests across date, calc, houses, ayanamsa, and isolate safety
- 545-value cross-validation suite against pyswisseph covering 13 bodies, 14 ayanamsas, 11 house systems, 7 locations, 7 dates
- Stress test 0.1: 3.06 billion calculations across 100 isolates
(
calcUt+houses, Moshier + Swiss Ephemeris, all 47 ayanamsas, heliocentric, barycentric, equatorial). Seetest/stress-test.md. - Stress test 0.2: 4.11 billion calculations across 100 isolates
covering all 88 public methods — positions, houses, ayanamsas,
date/time, fixed stars, eclipses, crossings, rise/set, heliacal
events, coordinates, nodes, orbital elements, and Gauquelin sectors.
Tests two ephemeris engines, 52 ayanamsa modes, 11 house systems,
8 locations (including polar), and 5,000 years of dates. Verifies
isolate isolation, API coverage, and expected error paths.
See
test/stress-test-0.2/stress-test-0.2.md.
License
This Dart package is licensed under AGPL-3.0-or-later.
The Swiss Ephemeris C library (vendored in csrc/) is copyright
Astrodienst AG and dual-licensed under AGPL-3.0 and a commercial
license. See astro.com/swisseph for
commercial licensing details.
Issues and contributions
File issues and merge requests on GitLab.
Libraries
- swisseph
- Isolate-safe Dart FFI bindings to the Swiss Ephemeris C library.