swisseph-wasm
Version:
High-precision Swiss Ephemeris WebAssembly library for astronomical calculations in JavaScript
865 lines (764 loc) • 79.7 kB
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SwissEph WebAssembly - Interactive Demo</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
tailwind.config = {
theme: {
extend: {
colors: {
cosmic: {
50: '#f0f9ff',
500: '#3b82f6',
900: '#1e3a8a'
}
}
}
}
}
</script>
<style>
.planet-symbol { font-size: 1.2em; }
.loading { animation: spin 1s linear infinite; }
@keyframes spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
.gradient-bg { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
.code-block {
background: #1e293b;
color: #e2e8f0;
font-family: 'Courier New', monospace;
font-size: 0.875rem;
overflow-x: auto;
}
.aspect-line { stroke-width: 2; opacity: 0.7; }
.planet-dot { r: 4; }
.house-line { stroke: #6b7280; stroke-width: 1; opacity: 0.5; }
</style>
</head>
<body class="bg-gray-50 min-h-screen">
<!-- Header -->
<header class="gradient-bg text-white py-6 shadow-lg">
<div class="container mx-auto px-4">
<h1 class="text-4xl font-bold text-center mb-2">🌟 SwissEph WebAssembly - Interactive Demo</h1>
<p class="text-center text-blue-100 mb-4">Professional Swiss Ephemeris Calculator & Astrology Tools</p>
<div class="text-center">
<a href="https://github.com/prolaxu/swisseph-wasm" target="_blank"
class="inline-flex items-center px-4 py-2 bg-white bg-opacity-20 rounded-lg hover:bg-opacity-30 transition-all">
<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 0C4.477 0 0 4.484 0 10.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0110 4.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.203 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.942.359.31.678.921.678 1.856 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0020 10.017C20 4.484 15.522 0 10 0z" clip-rule="evenodd"></path>
</svg>
View on GitHub
</a>
<a href="https://www.npmjs.com/package/swisseph-wasm" target="_blank"
class="inline-flex items-center px-4 py-2 bg-white bg-opacity-20 rounded-lg hover:bg-opacity-30 transition-all ml-2">
<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 24 24">
<path d="M0 7.334v8h6.666v1.332H12v-1.332h12v-8H0zm6.666 6.664H5.334v-4H3.999v4H1.335V8.667h5.331v5.331zm4 0v1.336H8.001V8.667h5.334v5.332h-2.669v-.001zm12.001 0h-1.33v-4h-1.336v4h-1.335v-4h-1.33v4h-2.669V8.667h8v5.331z"/>
</svg>
NPM Package
</a>
<a href="api-explorer.html"
class="inline-flex items-center px-4 py-2 bg-white bg-opacity-20 rounded-lg hover:bg-opacity-30 transition-all ml-2">
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"/>
</svg>
API Explorer
</a>
</div>
</div>
</header>
<!-- Main Content -->
<div class="container mx-auto px-4 py-8">
<!-- Loading Indicator -->
<div id="loading" class="text-center py-8">
<div class="loading inline-block w-8 h-8 border-4 border-blue-500 border-t-transparent rounded-full"></div>
<p class="mt-2 text-gray-600">Loading Swiss Ephemeris...</p>
</div>
<!-- Main App (hidden initially) -->
<div id="app" class="hidden">
<!-- Navigation Tabs -->
<div class="mb-8">
<nav class="flex flex-wrap gap-1 bg-white rounded-lg p-1 shadow-md">
<button onclick="showTab('current')" id="tab-current" class="tab-button active">🌍 Current Positions</button>
<button onclick="showTab('birth')" id="tab-birth" class="tab-button">🎂 Birth Chart</button>
<button onclick="showTab('compare')" id="tab-compare" class="tab-button">⚖️ Sidereal vs Tropical</button>
<button onclick="showTab('time')" id="tab-time" class="tab-button">⏰ Time Functions</button>
<button onclick="showTab('houses')" id="tab-houses" class="tab-button">🏠 House Systems</button>
<button onclick="showTab('aspects')" id="tab-aspects" class="tab-button">📐 Aspects</button>
<button onclick="showTab('api')" id="tab-api" class="tab-button">🔧 API Explorer</button>
<button onclick="showTab('chart')" id="tab-chart" class="tab-button">📊 Visual Chart</button>
</nav>
</div>
<!-- Current Positions Tab -->
<div id="tab-content-current" class="tab-content">
<div class="bg-white rounded-lg shadow-md p-6">
<h2 class="text-2xl font-bold mb-4 text-gray-800">🌍 Current Planetary Positions</h2>
<p class="text-gray-600 mb-4">Real-time positions of planets as seen from Earth</p>
<button onclick="calculateCurrentPositions()" class="bg-blue-500 hover:bg-blue-600 text-white px-6 py-2 rounded-lg mb-4 transition-colors">
Calculate Now
</button>
<div id="current-results" class="hidden">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4" id="current-planets"></div>
<div class="mt-4 p-4 bg-gray-50 rounded-lg">
<p class="text-sm text-gray-600" id="current-timestamp"></p>
</div>
</div>
</div>
</div>
<!-- Birth Chart Tab -->
<div id="tab-content-birth" class="tab-content hidden">
<div class="bg-white rounded-lg shadow-md p-6">
<h2 class="text-2xl font-bold mb-4 text-gray-800">🎂 Birth Chart Calculator</h2>
<div class="bg-green-50 border border-green-200 rounded-lg p-3 mb-4">
<p class="text-sm text-green-800">
<strong>💡 Tip:</strong> Birth location (latitude/longitude) is essential for accurate house calculations and local time corrections.
Default coordinates are set to Nepal (27.8667°N, 84.9167°E) with Nepal Standard Time (+5.75 UTC).
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Birth Date</label>
<input type="date" id="birth-date" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Birth Time</label>
<input type="time" id="birth-time" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Timezone Offset (hours from UTC)</label>
<input type="number" id="birth-timezone" value="5.75" min="-12" max="12" step="0.25" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Birth Latitude</label>
<input type="number" id="birth-latitude" value="27.8667" step="0.0001" min="-90" max="90" placeholder="e.g. 27.8667 (Nepal)" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Birth Longitude</label>
<input type="number" id="birth-longitude" value="84.9167" step="0.0001" min="-180" max="180" placeholder="e.g. 84.9167 (Nepal)" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Coordinate System</label>
<select id="birth-system" onchange="toggleAyanamsa('birth')" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
<option value="tropical">Tropical</option>
<option value="sidereal">Sidereal</option>
</select>
</div>
<div id="birth-ayanamsa-container" class="hidden">
<label class="block text-sm font-medium text-gray-700 mb-2">Ayanamsa</label>
<select id="birth-ayanamsa" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
<option value="0">Fagan/Bradley</option>
<option value="1" selected>Lahiri</option>
<option value="2">De Luce</option>
<option value="3">Raman</option>
<option value="4">Ushashashi</option>
<option value="5">Krishnamurti</option>
<option value="6">Djwhal Khul</option>
<option value="7">Yukteshwar</option>
<option value="8">J.N. Bhasin</option>
<option value="21">Galactic Center (Gil Brand)</option>
<option value="27">True Chitra</option>
<option value="28">True Revati</option>
</select>
</div>
</div>
<button onclick="calculateBirthChart()" class="bg-green-500 hover:bg-green-600 text-white px-6 py-2 rounded-lg mb-4 transition-colors">
Calculate Birth Chart
</button>
<div id="birth-results" class="hidden">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4" id="birth-planets"></div>
<div class="mt-4 p-4 bg-gray-50 rounded-lg">
<p class="text-sm text-gray-600" id="birth-info"></p>
</div>
</div>
</div>
</div>
<!-- Sidereal vs Tropical Tab -->
<div id="tab-content-compare" class="tab-content hidden">
<div class="bg-white rounded-lg shadow-md p-6">
<h2 class="text-2xl font-bold mb-4 text-gray-800">⚖️ Sidereal vs Tropical Comparison</h2>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Date</label>
<input type="date" id="compare-date" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Time (UTC)</label>
<input type="time" id="compare-time" value="12:00" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Planet</label>
<select id="compare-planet" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
<option value="0">Sun ☉</option>
<option value="1">Moon ☽</option>
<option value="2">Mercury ☿</option>
<option value="3">Venus ♀</option>
<option value="4">Mars ♂</option>
<option value="5">Jupiter ♃</option>
<option value="6">Saturn ♄</option>
<option value="7">Uranus ♅</option>
<option value="8">Neptune ♆</option>
<option value="9">Pluto ♇</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Ayanamsa</label>
<select id="compare-ayanamsa" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
<option value="0">Fagan/Bradley</option>
<option value="1" selected>Lahiri</option>
<option value="2">De Luce</option>
<option value="3">Raman</option>
<option value="4">Ushashashi</option>
<option value="5">Krishnamurti</option>
<option value="6">Djwhal Khul</option>
<option value="7">Yukteshwar</option>
<option value="8">J.N. Bhasin</option>
<option value="21">Galactic Center (Gil Brand)</option>
<option value="27">True Chitra</option>
<option value="28">True Revati</option>
</select>
</div>
</div>
<button onclick="comparePositions()" class="bg-purple-500 hover:bg-purple-600 text-white px-6 py-2 rounded-lg mb-4 transition-colors">
Compare Positions
</button>
<div id="compare-results" class="hidden">
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="p-4 bg-orange-50 rounded-lg border border-orange-200">
<h3 class="font-bold text-orange-800 mb-2">🌞 Tropical Position</h3>
<div id="tropical-result" class="text-lg"></div>
</div>
<div class="p-4 bg-blue-50 rounded-lg border border-blue-200">
<h3 class="font-bold text-blue-800 mb-2">⭐ Sidereal Position</h3>
<div id="sidereal-result" class="text-lg"></div>
</div>
</div>
<div class="mt-4 p-4 bg-gray-50 rounded-lg">
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 text-center">
<div>
<p class="text-sm text-gray-600">Ayanamsa (Lahiri)</p>
<p class="text-lg font-bold" id="ayanamsa-value"></p>
</div>
<div>
<p class="text-sm text-gray-600">Difference</p>
<p class="text-lg font-bold" id="difference-value"></p>
</div>
<div>
<p class="text-sm text-gray-600">Julian Day</p>
<p class="text-lg font-bold" id="julian-day-value"></p>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Time Functions Tab -->
<div id="tab-content-time" class="tab-content hidden">
<div class="bg-white rounded-lg shadow-md p-6">
<h2 class="text-2xl font-bold mb-4 text-gray-800">⏰ Time & Date Functions</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Date</label>
<input type="date" id="time-date" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Time (UTC)</label>
<input type="time" id="time-time" value="12:00" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>
</div>
<button onclick="calculateTimeFunctions()" class="bg-indigo-500 hover:bg-indigo-600 text-white px-6 py-2 rounded-lg mb-4 transition-colors">
Calculate Time Functions
</button>
<div id="time-results" class="hidden">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<div class="p-4 bg-blue-50 rounded-lg">
<h3 class="font-bold text-blue-800 mb-2">Julian Day</h3>
<p class="text-lg" id="julian-day"></p>
</div>
<div class="p-4 bg-green-50 rounded-lg">
<h3 class="font-bold text-green-800 mb-2">Sidereal Time</h3>
<p class="text-lg" id="sidereal-time"></p>
</div>
<div class="p-4 bg-purple-50 rounded-lg">
<h3 class="font-bold text-purple-800 mb-2">Delta T</h3>
<p class="text-lg" id="delta-t"></p>
</div>
<div class="p-4 bg-yellow-50 rounded-lg">
<h3 class="font-bold text-yellow-800 mb-2">Day of Week</h3>
<p class="text-lg" id="day-of-week"></p>
</div>
<div class="p-4 bg-red-50 rounded-lg">
<h3 class="font-bold text-red-800 mb-2">Calendar Date</h3>
<p class="text-lg" id="calendar-date"></p>
</div>
<div class="p-4 bg-indigo-50 rounded-lg">
<h3 class="font-bold text-indigo-800 mb-2">Swiss Eph Version</h3>
<p class="text-lg" id="version-info"></p>
</div>
</div>
</div>
</div>
</div>
<!-- House Systems Tab -->
<div id="tab-content-houses" class="tab-content hidden">
<div class="bg-white rounded-lg shadow-md p-6">
<h2 class="text-2xl font-bold mb-4 text-gray-800">🏠 House Systems Calculator</h2>
<div class="bg-blue-50 border border-blue-200 rounded-lg p-3 mb-4">
<p class="text-sm text-blue-800">
<strong>Note:</strong> This demo uses simplified house calculations for demonstration.
For precise house cusps, use the full Swiss Ephemeris house calculation functions in your application.
</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Date</label>
<input type="date" id="houses-date" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Time (UTC)</label>
<input type="time" id="houses-time" value="12:00" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">House System</label>
<select id="house-system" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
<option value="P">Placidus</option>
<option value="K">Koch</option>
<option value="O">Porphyrius</option>
<option value="R">Regiomontanus</option>
<option value="C">Campanus</option>
<option value="A">Equal (Ascendant)</option>
<option value="E">Equal</option>
<option value="W">Whole Sign</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Latitude</label>
<input type="number" id="houses-lat" value="27.7172" step="0.0001" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Longitude</label>
<input type="number" id="houses-lon" value="85.3240" step="0.0001" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>
</div>
<button onclick="calculateHouses()" class="bg-orange-500 hover:bg-orange-600 text-white px-6 py-2 rounded-lg mb-4 transition-colors">
Calculate Houses
</button>
<div id="houses-results" class="hidden">
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4" id="houses-grid"></div>
<div class="mt-4 p-4 bg-gray-50 rounded-lg">
<p class="text-sm text-gray-600" id="houses-info"></p>
</div>
</div>
</div>
</div>
<!-- Aspects Tab -->
<div id="tab-content-aspects" class="tab-content hidden">
<div class="bg-white rounded-lg shadow-md p-6">
<h2 class="text-2xl font-bold mb-4 text-gray-800">📐 Planetary Aspects Calculator</h2>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Date</label>
<input type="date" id="aspects-date" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Time (UTC)</label>
<input type="time" id="aspects-time" value="12:00" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Orb Tolerance (degrees)</label>
<input type="number" id="aspects-orb" value="8" min="1" max="15" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Coordinate System</label>
<select id="aspects-system" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
<option value="tropical">Tropical</option>
<option value="sidereal">Sidereal (Lahiri)</option>
</select>
</div>
</div>
<button onclick="calculateAspects()" class="bg-pink-500 hover:bg-pink-600 text-white px-6 py-2 rounded-lg mb-4 transition-colors">
Calculate Aspects
</button>
<div id="aspects-results" class="hidden">
<div class="mb-4">
<h3 class="text-lg font-bold mb-2">Major Aspects Found:</h3>
<div id="aspects-list" class="space-y-2"></div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4" id="aspects-grid"></div>
</div>
</div>
</div>
<!-- API Explorer Tab -->
<div id="tab-content-api" class="tab-content hidden">
<div class="bg-white rounded-lg shadow-md p-6">
<h2 class="text-2xl font-bold mb-4 text-gray-800">🔧 SwissEph API Explorer</h2>
<p class="text-gray-600 mb-6">Test any SwissEph function with custom parameters</p>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Function Category</label>
<select id="api-category" onchange="updateFunctionList()" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent mb-2">
<option value="basic">🌟 Basic Calculations</option>
<option value="time">⏰ Time Functions</option>
<option value="houses">🏠 Houses & Angles</option>
<option value="ayanamsa">🔮 Ayanamsa & Sidereal</option>
<option value="eclipse">🌑 Eclipses & Occultations</option>
<option value="stars">⭐ Fixed Stars</option>
<option value="phenomena">🌙 Phenomena & Visibility</option>
<option value="coordinates">📐 Coordinate Transformations</option>
<option value="utility">🔧 Utility Functions</option>
<option value="advanced">🚀 Advanced Features</option>
</select>
<label class="block text-sm font-medium text-gray-700 mb-2">Function</label>
<select id="api-function" onchange="updateApiParams()" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
<!-- Functions will be populated by JavaScript -->
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Parameters (JSON)</label>
<textarea id="api-params" rows="3" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent font-mono text-sm">[2460218.5, 0, 2]</textarea>
</div>
</div>
<div class="mb-6">
<button onclick="executeApiCall()" class="bg-teal-500 hover:bg-teal-600 text-white px-6 py-2 rounded-lg transition-colors">
Execute Function
</button>
<button onclick="clearApiResults()" class="bg-gray-500 hover:bg-gray-600 text-white px-4 py-2 rounded-lg ml-2 transition-colors">
Clear
</button>
</div>
<div id="api-results" class="hidden">
<h3 class="text-lg font-bold mb-2">Result:</h3>
<pre id="api-output" class="code-block p-4 rounded-lg mb-4"></pre>
<h3 class="text-lg font-bold mb-2">JavaScript Code:</h3>
<pre id="api-code" class="code-block p-4 rounded-lg"></pre>
</div>
</div>
</div>
<!-- Visual Chart Tab -->
<div id="tab-content-chart" class="tab-content hidden">
<div class="bg-white rounded-lg shadow-md p-6">
<h2 class="text-2xl font-bold mb-4 text-gray-800">📊 Visual Astrological Chart</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Date</label>
<input type="date" id="chart-date" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Time (UTC)</label>
<input type="time" id="chart-time" value="12:00" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Chart Type</label>
<select id="chart-type" class="w-full p-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent">
<option value="wheel">Astrological Wheel</option>
<option value="linear">Linear Positions</option>
<option value="aspects">Aspect Grid</option>
</select>
</div>
</div>
<button onclick="generateChart()" class="bg-purple-500 hover:bg-purple-600 text-white px-6 py-2 rounded-lg mb-4 transition-colors">
Generate Chart
</button>
<div id="chart-results" class="hidden">
<div class="flex justify-center mb-4">
<canvas id="astro-chart" width="400" height="400" class="border border-gray-300 rounded-lg"></canvas>
</div>
<div id="chart-legend" class="grid grid-cols-2 md:grid-cols-5 gap-2 text-sm"></div>
</div>
</div>
</div>
</div>
<!-- Error Display -->
<div id="error-display" class="hidden bg-red-50 border border-red-200 rounded-lg p-4 mb-4">
<h3 class="text-red-800 font-bold mb-2">Error</h3>
<p id="error-message" class="text-red-700"></p>
</div>
<!-- Footer -->
<footer class="mt-12 text-center">
<!-- Main Footer -->
<div class="text-gray-500 mb-3">
<p class="text-xs mb-1">⚡ Powered by Swiss Ephemeris WebAssembly |
<a href="https://github.com/prolaxu/swisseph-wasm" class="text-blue-500 hover:underline">Open Source</a>
</p>
<p class="text-xs">🇳🇵 Built with ❤️ by Laxman Adhikari from Nepal</p>
</div>
<!-- Support Section -->
<div class="mb-3">
<p class="text-xs text-gray-600 mb-2">
☕ Enjoying this free tool?
<a href="https://ko-fi.com/prolaxu" target="_blank"
class="text-blue-500 hover:text-blue-600 font-medium">Support on Ko-fi</a>
</p>
</div>
<!-- Bottom Links -->
<div class="text-xs text-gray-400 border-t border-gray-200 pt-3">
<p>
<a href="https://github.com/prolaxu/swisseph-wasm" class="hover:text-blue-500">Documentation</a> •
<a href="https://github.com/prolaxu/swisseph-wasm/issues" class="hover:text-blue-500">Report Issues</a> •
<a href="https://www.astro.com/swisseph/" class="hover:text-blue-500">Swiss Ephemeris</a>
</p>
</div>
</footer>
</div>
<script type="module">
import SwissEph from '../src/swisseph.js';
let swe = null;
let initialized = false;
// Initialize Swiss Ephemeris
async function initSwissEph() {
try {
swe = new SwissEph();
await swe.initSwissEph();
initialized = true;
// Hide loading, show app
document.getElementById('loading').classList.add('hidden');
document.getElementById('app').classList.remove('hidden');
// Set default dates
setDefaultDates();
} catch (error) {
showError('Failed to initialize Swiss Ephemeris: ' + error.message);
}
}
// Set default dates to today
function setDefaultDates() {
const today = new Date();
const dateStr = today.toISOString().split('T')[0];
const timeStr = today.toTimeString().split(' ')[0].substring(0, 5);
document.getElementById('birth-date').value = dateStr;
document.getElementById('birth-time').value = timeStr;
document.getElementById('compare-date').value = dateStr;
document.getElementById('time-date').value = dateStr;
// Set timezone to local offset
const timezoneOffset = -today.getTimezoneOffset() / 60;
document.getElementById('birth-timezone').value = timezoneOffset;
}
// Tab management
window.showTab = function(tabName) {
// Hide all tab contents
document.querySelectorAll('.tab-content').forEach(tab => {
tab.classList.add('hidden');
});
// Remove active class from all buttons
document.querySelectorAll('.tab-button').forEach(btn => {
btn.classList.remove('active');
});
// Show selected tab and mark button as active
document.getElementById(`tab-content-${tabName}`).classList.remove('hidden');
document.getElementById(`tab-${tabName}`).classList.add('active');
};
// Error handling
function showError(message) {
document.getElementById('error-message').textContent = message;
document.getElementById('error-display').classList.remove('hidden');
setTimeout(() => {
document.getElementById('error-display').classList.add('hidden');
}, 5000);
}
// Planet symbols and names
const planetInfo = {
0: { name: 'Sun', symbol: '☉', color: '#FFA500' },
1: { name: 'Moon', symbol: '☽', color: '#C0C0C0' },
2: { name: 'Mercury', symbol: '☿', color: '#87CEEB' },
3: { name: 'Venus', symbol: '♀', color: '#FFB6C1' },
4: { name: 'Mars', symbol: '♂', color: '#FF6347' },
5: { name: 'Jupiter', symbol: '♃', color: '#9370DB' },
6: { name: 'Saturn', symbol: '♄', color: '#2F4F4F' },
7: { name: 'Uranus', symbol: '♅', color: '#40E0D0' },
8: { name: 'Neptune', symbol: '♆', color: '#4169E1' },
9: { name: 'Pluto', symbol: '♇', color: '#8B4513' }
};
// Zodiac signs
const zodiacSigns = [
'Aries ♈', 'Taurus ♉', 'Gemini ♊', 'Cancer ♋', 'Leo ♌', 'Virgo ♍',
'Libra ♎', 'Scorpio ♏', 'Sagittarius ♐', 'Capricorn ♑', 'Aquarius ♒', 'Pisces ♓'
];
function formatPosition(longitude) {
const sign = Math.floor(longitude / 30);
const degree = longitude % 30;
return `${degree.toFixed(2)}° ${zodiacSigns[sign]}`;
}
// Toggle ayanamsa visibility
window.toggleAyanamsa = function(prefix) {
const systemSelect = document.getElementById(`${prefix}-system`);
const ayanamsaContainer = document.getElementById(`${prefix}-ayanamsa-container`);
if (systemSelect.value === 'sidereal') {
ayanamsaContainer.classList.remove('hidden');
} else {
ayanamsaContainer.classList.add('hidden');
}
};
// Calculate current positions
window.calculateCurrentPositions = async function() {
if (!initialized) return;
try {
const now = new Date();
const jd = swe.julday(
now.getUTCFullYear(),
now.getUTCMonth() + 1,
now.getUTCDate(),
now.getUTCHours() + now.getUTCMinutes() / 60
);
const planetsContainer = document.getElementById('current-planets');
planetsContainer.innerHTML = '';
for (let i = 0; i <= 9; i++) {
const position = swe.calc_ut(jd, i, swe.SEFLG_SWIEPH);
const info = planetInfo[i];
const planetCard = document.createElement('div');
planetCard.className = 'p-4 bg-gradient-to-br from-blue-50 to-indigo-50 rounded-lg border border-blue-200';
planetCard.innerHTML = `
<div class="flex items-center mb-2">
<span class="planet-symbol mr-2">${info.symbol}</span>
<span class="font-bold text-gray-800">${info.name}</span>
</div>
<p class="text-lg text-blue-800">${formatPosition(position[0])}</p>
<p class="text-sm text-gray-600">${position[0].toFixed(6)}°</p>
`;
planetsContainer.appendChild(planetCard);
}
document.getElementById('current-timestamp').textContent =
`Calculated at: ${now.toUTCString()} (JD: ${jd.toFixed(6)})`;
document.getElementById('current-results').classList.remove('hidden');
} catch (error) {
showError('Error calculating current positions: ' + error.message);
}
};
// Calculate birth chart
window.calculateBirthChart = async function() {
if (!initialized) return;
try {
const date = document.getElementById('birth-date').value;
const time = document.getElementById('birth-time').value;
const timezone = parseFloat(document.getElementById('birth-timezone').value);
const latitude = parseFloat(document.getElementById('birth-latitude').value);
const longitude = parseFloat(document.getElementById('birth-longitude').value);
const system = document.getElementById('birth-system').value;
if (!date || !time) {
showError('Please enter both date and time');
return;
}
if (isNaN(latitude) || isNaN(longitude)) {
showError('Please enter valid latitude and longitude');
return;
}
const [year, month, day] = date.split('-').map(Number);
const [hour, minute] = time.split(':').map(Number);
const utcHour = hour + minute / 60 - timezone;
const jd = swe.julday(year, month, day, utcHour);
// Set sidereal mode if needed
if (system === 'sidereal') {
const ayanamsa = parseInt(document.getElementById('birth-ayanamsa').value);
swe.set_sid_mode(ayanamsa, 0, 0);
}
const flags = system === 'sidereal' ?
swe.SEFLG_SWIEPH | swe.SEFLG_SIDEREAL :
swe.SEFLG_SWIEPH;
const planetsContainer = document.getElementById('birth-planets');
planetsContainer.innerHTML = '';
for (let i = 0; i <= 9; i++) {
const position = swe.calc_ut(jd, i, flags);
const info = planetInfo[i];
const planetCard = document.createElement('div');
planetCard.className = 'p-4 bg-gradient-to-br from-green-50 to-emerald-50 rounded-lg border border-green-200';
planetCard.innerHTML = `
<div class="flex items-center mb-2">
<span class="planet-symbol mr-2">${info.symbol}</span>
<span class="font-bold text-gray-800">${info.name}</span>
</div>
<p class="text-lg text-green-800">${formatPosition(position[0])}</p>
<p class="text-sm text-gray-600">${position[0].toFixed(6)}°</p>
`;
planetsContainer.appendChild(planetCard);
}
document.getElementById('birth-info').textContent =
`Birth Chart (${system}): ${date} ${time} UTC${timezone >= 0 ? '+' : ''}${timezone} | Location: ${latitude.toFixed(4)}°N, ${longitude.toFixed(4)}°E (JD: ${jd.toFixed(6)})`;
document.getElementById('birth-results').classList.remove('hidden');
} catch (error) {
showError('Error calculating birth chart: ' + error.message);
}
};
// Compare sidereal vs tropical
window.comparePositions = async function() {
if (!initialized) return;
try {
const date = document.getElementById('compare-date').value;
const time = document.getElementById('compare-time').value;
const planetId = parseInt(document.getElementById('compare-planet').value);
if (!date || !time) {
showError('Please enter both date and time');
return;
}
const [year, month, day] = date.split('-').map(Number);
const [hour, minute] = time.split(':').map(Number);
const jd = swe.julday(year, month, day, hour + minute / 60);
// Tropical position
const tropical = swe.calc_ut(jd, planetId, swe.SEFLG_SWIEPH);
// Sidereal position
const ayanamsa = parseInt(document.getElementById('compare-ayanamsa').value);
swe.set_sid_mode(ayanamsa, 0, 0);
const sidereal = swe.calc_ut(jd, planetId, swe.SEFLG_SWIEPH | swe.SEFLG_SIDEREAL);
const ayanamsaValue = swe.get_ayanamsa(jd);
document.getElementById('tropical-result').innerHTML = `
<p class="font-bold">${formatPosition(tropical[0])}</p>
<p class="text-sm text-gray-600">${tropical[0].toFixed(6)}°</p>
`;
document.getElementById('sidereal-result').innerHTML = `
<p class="font-bold">${formatPosition(sidereal[0])}</p>
<p class="text-sm text-gray-600">${sidereal[0].toFixed(6)}°</p>
`;
document.getElementById('ayanamsa-value').textContent = `${ayanamsaValue.toFixed(6)}°`;
document.getElementById('difference-value').textContent = `${(tropical[0] - sidereal[0]).toFixed(6)}°`;
document.getElementById('julian-day-value').textContent = jd.toFixed(6);
document.getElementById('compare-results').classList.remove('hidden');
} catch (error) {
showError('Error comparing positions: ' + error.message);
}
};
// Calculate time functions
window.calculateTimeFunctions = async function() {
if (!initialized) return;
try {
const date = document.getElementById('time-date').value;
const time = document.getElementById('time-time').value;
if (!date || !time) {
showError('Please enter both date and time');
return;
}
const [year, month, day] = date.split('-').map(Number);
const [hour, minute] = time.split(':').map(Number);
const jd = swe.julday(year, month, day, hour + minute / 60);
const sidTime = swe.sidtime(jd);
const deltaT = swe.deltat(jd);
const dayOfWeek = swe.day_of_week(jd);
const calDate = swe.revjul(jd, swe.SE_GREG_CAL);
const version = swe.version();
const days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
document.getElementById('julian-day').textContent = jd.toFixed(6);
document.getElementById('sidereal-time').textContent = `${sidTime.toFixed(6)} hours`;
document.getElementById('delta-t').textContent = `${deltaT.toFixed(2)} seconds`;
document.getElementById('day-of-week').textContent = days[dayOfWeek];
document.getElementById('calendar-date').textContent =
`${calDate.year}-${calDate.month.toString().padStart(2, '0')}-${calDate.day.toString().padStart(2, '0')} ${calDate.hour.toFixed(2)}`;
document.getElementById('version-info').textContent = version;
document.getElementById('time-results').classList.remove('hidden');
} catch (error) {
showError('Error calculating time functions: ' + error.message);
}
};
// Calculate houses
window.calculateHouses = async function() {
if (!initialized) return;
try {
const date = document.getElementById('houses-date').value;
const time = document.getElementById('houses-time').value;
const houseSystem = document.getElementById('house-system').value;
const lat = parseFloat(document.getElementById('houses-lat').value);
const lon = parseFloat(document.getElementById('houses-lon').value);
if (!date || !time) {
showError('Please enter both date and time');
return;
}
const [year, month, day] = date.split('-').map(Number);
const [hour, minute] = time.split(':').map(Number);
const jd = swe.julday(year, month, day, hour + minute / 60);
// Calculate sidereal time and ARMC for house calculatio