Real-time ISS and satellite tracker with a sky-pointing AR overlay.
- Live ISS tracking — position, altitude, speed, and crew manifest, polled every 3s
- Satellite catalogue — searchable by name or NORAD ID, filterable by category (ISS, Starlink, GPS, weather, amateur) or by your own favorites
- Favorites — star any satellite to track it specifically; persists across launches
- Pass predictions — rise/peak/set times per satellite, with a naked-eye-visibility flag
- Pass alerts — opt-in local notification a configurable number of minutes before a satellite rises
- Next-pass countdown — Home shows a live countdown to the soonest pass among your favorites (falls back to the ISS if you haven't favorited anything yet)
- Sky AR view — point your phone at the sky to see satellites overlaid against a rendered sky view, sized by visual magnitude; tap one to pull up its detail card (altitude, elevation, speed, magnitude)
- 3D Earth / orbit viewer — Three.js globe with live orbit tracks and day/night texture
- Settings — distance/speed units, minimum pass elevation, notification lead time, max satellites rendered in AR, AR label size
- First-launch onboarding — a short primer (what the app does, why it needs location, why it needs motion sensors, optional alerts) before the permission prompts fire, shown once, no camera access involved
| Layer | Technology |
|---|---|
| Framework | NativeScript 9 + Angular 18 |
| State | RxJS BehaviorSubject services |
| Orbital math | satellite.js (SGP4/SDP4) |
| AR canvas | @nativescript-community/ui-canvas |
| 3D rendering | @nativescript-community/three (Three.js) |
| Sensors | @nativescript/sensors |
| Location | @nativescript/geolocation |
| Notifications | @nativescript/local-notifications |
| Persistence | JSON files in app documents folder (favorites, settings, scheduled alerts, onboarding flag) |
| Data | CelesTrak TLE API + wheretheiss.at / GitHub-mirrored ISS APIs |
| Theme | NASA Mission Control (custom SCSS) |
# 1. Install dependencies
npm install
# 2. Add textures (see src/assets/textures/README.md)
# Download from https://visibleearth.nasa.gov
# 3. Run on device
ns run android --device <id>
ns run ios --device <id>
# 4. Debug
ns debug android
ns debug iosOn first launch you'll see a short onboarding flow before the location/ notification prompts — see Onboarding below.
src/app/
├── app.module.ts # Root NgModule
├── app.component.html # Tab shell (3 tabs) + onboarding overlay
├── app-routing.module.ts # Named router outlets
│
├── onboarding/ # First-launch primer, shown once
│ └── onboarding.component # Welcome → features → location → sensors → alerts
│
├── home/ # HOME tab
│ ├── home.component # ISS dashboard, next-pass card, visible sats
│ ├── iss-tracker/ # NASA mission control view
│ ├── orbit-viewer/ # 3D Earth + Three.js
│ └── settings/ # Units, pass threshold, alerts, AR tuning
│
├── browse/ # BROWSE tab
│ ├── browse.component # Hub: satellites | passes
│ ├── satellite-list/ # Searchable catalogue, favorites star + filter
│ └── pass-predictions/ # Rise/peak/set schedule + alert bell
│
├── search/ # SKY AR tab
│ ├── search.component # Hub: AR | calibration
│ ├── ar-view/ # Canvas-rendered sky AR overlay, tap-to-select
│ └── calibration/ # Compass calibration wizard
│
├── core/
│ ├── api/
│ │ ├── iss.service.ts # ISS position/crew polling (3s)
│ │ └── satellite.service.ts # CelesTrak TLE + SGP4 observations
│ ├── tle/
│ │ ├── tle-parser.service.ts # 2-line element parser
│ │ └── sgp4.service.ts # satellite.js wrapper
│ ├── sensors/
│ │ └── sensor-fusion.service.ts # Complementary filter
│ ├── rendering/
│ │ ├── ar-engine.service.ts # 60 FPS AR canvas loop, tap-to-select
│ │ ├── earth-renderer.service.ts # Three.js Earth
│ │ └── sky-projection.service.ts # Az/El → screen coords
│ ├── geolocation/
│ │ └── geolocation.service.ts # @nativescript/geolocation
│ ├── storage/
│ │ └── favorites.service.ts # Favorited satellite IDs (JSON file)
│ ├── settings/
│ │ └── settings.service.ts # AppSettings (JSON file)
│ ├── notifications/
│ │ └── notifications.service.ts # Pass-alert scheduling (JSON file)
│ └── onboarding/
│ └── onboarding.service.ts # First-launch completion flag
│
└── shared/
├── models/index.ts # All TypeScript interfaces
├── constants/index.ts # API URLs, NASA colors, timing
└── unit-format.ts # km/mi and km/h/km/s conversion helpers
OnboardingComponent renders as a full-screen overlay above the tab UI —
it isn't a route, so there's no back-navigation path into it. AppComponent
decides once at startup (OnboardingService.shouldShow(), backed by a flag
file in the documents folder) whether to show it:
- First launch: overlay shows, and it — not
AppComponent— is responsible for triggering the location permission prompt (as part of its "Your location" step) and the notification permission prompt (as part of the optional "Pass alerts" step). This way the primer text appears before the system prompt, not after. - Every launch after that:
AppComponentrequests geolocation immediately at app root, exactly as it did before onboarding existed.
Declining location or notifications during onboarding doesn't block progress — the app falls back to a default location and simply leaves alerts off, both changeable later.
Home's gear icon opens a Settings screen (home/settings) backed by
SettingsService. Everything there is live, not cosmetic:
| Setting | Affects |
|---|---|
| Distance unit (km/miles) | Home altitude readout |
| Speed unit (km/h · km/s) | Home speed readout |
| Minimum pass elevation | Pass-predictions scan + Home's next-pass card |
| Pass alerts on/off | Whether the bell toggle in Pass Predictions can schedule anything |
| Alert lead time | How far before rise the notification fires |
| Max satellites shown (AR) | Label cap in the Sky AR draw loop |
| AR label size | Dot/label scale multiplier in Sky AR |
| Show debris objects | Stored, but not yet wired to a data source — CelesTrak debris groups aren't fetched by SatelliteService yet |
The following packages require plugin registration:
# Geolocation
ns plugin add @nativescript/geolocation
# Sensors (compass, gyro, accelerometer)
ns plugin add @nativescript/sensors
# Permissions helper
ns plugin add @nativescript/permissions
# Local notifications (pass alerts)
ns plugin add @nativescript/local-notifications
# Community canvas (AR drawing)
ns plugin add @nativescript-community/ui-canvas
# Three.js for NativeScript
ns plugin add @nativescript-community/threeNow declared in App_Resources/iOS/Info.plist (these were missing
entirely before this update — without them, iOS terminates the app the
moment it requests location or motion access, rather than showing a
permission prompt). The app has no camera requirement, so no
NSCameraUsageDescription entry is needed:
<key>NSLocationWhenInUseUsageDescription</key>
<string>Orbitar uses your location to calculate satellite positions and pass times.</string>
<key>NSMotionUsageDescription</key>
<string>Orbitar uses motion and orientation data to point the AR sky overlay in the direction you're facing.</string>Now declared in App_Resources/Android/src/main/AndroidManifest.xml
(earlier versions of this README claimed ACCESS_FINE_LOCATION was
already present — it wasn't; only storage/network permissions existed.
Fixed alongside this README update). The app has no camera requirement,
so no CAMERA permission is declared:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>POST_NOTIFICATIONS is the runtime permission Android 13+ requires for
pass alerts to prompt correctly.
- AR engine: runs at 60 FPS outside Angular zone;
CanvasView.invalidate()triggers draw - AR tap-to-select: hit-tests against the last-built frame only — no extra propagation work per touch
- Next-pass countdown: SGP4 scan runs via
setTimeout(0)off the critical path, and is paused via the samenavigatingFrom/navigatedTolifecycle as ISS polling whenever Home isn't the active tab - 3D Earth: adaptive frame rate; skips frames if camera stationary; max pixel ratio 1.0
- Satellite propagation: chunked in 200-sat batches via
setTimeout(0)to avoid main-thread blocks - Textures: progressive load — 1K first, 2K after 2s; clouds after 3s; night lights after 5s
- Orbit track: single pre-allocated
Float32Array— zero allocation on updates - Favorites / settings / alerts / onboarding state: plain JSON files in the app's documents folder, read once and cached in memory — not re-read from disk on every access
| Endpoint | Purpose | Update rate |
|---|---|---|
api.wheretheiss.at/v1/satellites/25544 |
ISS lat/lon | Every 3s |
corquaid.github.io/.../people-in-space.json |
Crew manifest | Once on start |
celestrak.org/.../FORMAT=tle |
TLE data | Cache 6h |
All endpoints are free, no API key required.
Note: open-notify.org was dropped for both endpoints — it's plain HTTP only,
which many carriers/mobile networks block, and its astros.json has no HTTPS
equivalent. wheretheiss.at mirrors the same ISS position data over HTTPS;
the crew list comes from a GitHub-hosted JSON mirror instead. See
shared/constants/index.ts for details.
- Debris tracking —
showDebrisObjectsexists as a setting but nothing fetches debris TLEs yet; no CelesTrak group is wired up for it - AR trail/ground-track overlay — not implemented; satellites render as a single point, not a path
- Share — no share-to-image/text action yet, for a pass or a satellite's current position
- SQLite dependency —
@nativescript-community/sqliteis listed inpackage.jsonbut unused; all local persistence (favorites, settings, notifications, onboarding) uses plain JSON files in the documents folder instead, matching a pattern already proven inTleCacheService
App_Resources/Android/src/main/AndroidManifest.xmlwas missingACCESS_FINE_LOCATION,ACCESS_COARSE_LOCATION, andPOST_NOTIFICATIONS— despite an earlier version of this README claiming they were already declared. Added.App_Resources/iOS/Info.plisthad no usage-description keys at all for location or motion — iOS terminates an app that requests either of these without the matching key present. Added both.- Found and removed a full duplicate
App_Resources/App_Resources/nested directory (same size and file count as the realApp_Resources/tree) — almost certainly a packaging artifact from however this project was zipped, not an intentional part of the source tree. - Removed the camera permission and
@nativescript/cameradependency: the Sky AR view is a rendered overlay on a solid-color canvas surface, not a live camera feed, so no camera access is needed. This included theCAMERAentry in the Android manifest, theNSCameraUsageDescriptionkey in the iOS Info.plist, the@nativescript/camerapackage inpackage.json, and the runtime permission request inar-view.component.ts.
$nasa-red: #FC3D21 // Worm/meatball red
$nasa-blue: #0D3B8E // Institutional blue (JSC)
$nasa-cyan: #00C8E0 // CRT phosphor (telemetry data)
$nasa-gold: #FFD23F // ISS / crew highlight
$nasa-green: #1DB954 // GO annunciator green
$nasa-amber: #E8A020 // CAUTION annunciator
$nasa-panel: #0A1628 // Instrument panel face