# How God's Eye View Uses satellite.js for Real-Time Orbit Rendering

> Discover how God's Eye View uses satellite.js to render real-time satellite orbits. Learn to parse TLE data, calculate ECI positions, and transform for Cesium rendering.

- Repository: [Bilawal Sidhu/gods-eye-view](https://github.com/bilawalsidhu/gods-eye-view)
- Tags: how-to-guide
- Published: 2026-09-13

---

**God's Eye View leverages satellite.js to parse CelesTrak Two-Line Elements (TLE) into SGP4 propagation records, calculating real-time Earth-Centered Inertial (ECI) positions that are transformed into geodetic coordinates for Cesium rendering.**

The open-source project **bilawalsidhu/gods-eye-view** visualizes satellite constellations in a web-based 3D globe by integrating the **satellite.js** library with CesiumJS. The implementation combines SGP4 orbital mechanics with a baked-geometry technique to render smooth, flicker-free orbit rings that stay synchronized with Earth's rotation.

## Parsing TLEs and Initializing SGP4 Records

The pipeline begins in [`src/data/satellites.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/satellites.js) by converting raw TLE strings into propagator objects. The `twoline2satrec` function parses the two-line element sets into an SGP4 record (`satrec`) that contains the orbital elements required for propagation.

```javascript
// src/data/satellites.js – dense catalog load
import { twoline2satrec } from 'satellite.js';

const satrec = twoline2satrec(entry.line1, entry.line2);

```

This initialization occurs once during data ingestion, typically while loading dense catalogs like the Starlink constellation. The resulting `satrec` object persists in memory and serves as the input for all subsequent position calculations.

## Real-Time Position Propagation

For every render frame, the application calculates the satellite's current latitude, longitude, and altitude using the `propagatePosition` function. This method chains several **satellite.js** APIs to convert from ECI coordinates to geodetic values.

```javascript
// src/data/satellites.js – propagatePosition()
function propagatePosition(satrec, date) {
  const posVel = propagate(satrec, date);               // SGP4 propagation step
  const gmst = gstime(date);                           // Greenwich Mean Sidereal Time
  const geo = eciToGeodetic(posVel.position, gmst);    // ECI → Geodetic conversion
  
  return {
    longitude: degreesLong(geo.longitude),
    latitude:  degreesLat(geo.latitude),
    altitude:  geo.height * 1000,
  };
}

```

The `propagate` function returns an ECI position vector valid for the specified date. The `gstime` calculation provides the Earth rotation angle required to align the inertial frame with the Earth-fixed frame, while `eciToGeodetic` performs the coordinate transformation. Helper functions `degreesLong` and `degreesLat` normalize the radian outputs to degrees for Cesium consumption.

## Baking Static Orbit Geometry

To eliminate per-frame geometry regeneration, the system pre-computes a full orbital ring using `computeOrbitPath`. This function samples the satellite's position at regular intervals across one orbital period, locking the **GMST** to a fixed reference date to ensure the polyline closes perfectly.

```javascript
// src/data/satellites.js – computeOrbitPath()
function computeOrbitPath(satrec, referenceDate) {
  const periodSec = orbitalPeriodSeconds(satrec);
  const stepSec   = periodSec / ORBIT_PATH_STEPS; // 180 steps by default
  const fixedGmst = gstime(referenceDate);        // Lock GMST for the entire ring

  for (let i = 0; i <= ORBIT_PATH_STEPS; i++) {
    const t = new Date(referenceDate.getTime() + i * stepSec * 1000);
    const posVel = propagate(satrec, t);
    const geo = eciToGeodetic(posVel.position, fixedGmst);
    positions.push(Cesium.Cartesian3.fromDegrees(
      degreesLong(geo.longitude),
      degreesLat(geo.latitude),
      geo.height * 1000
    ));
  }
  return positions;
}

```

By holding `fixedGmst` constant during the sampling loop, the resulting positions represent the orbit in a rotating reference frame attached to the Earth. This creates a closed, static polyline that accurately depicts the ground track without requiring dynamic vertex updates.

## Synchronizing Orbits with Earth's Rotation

The baked orbit ring remains visually accurate through a model matrix transformation rather than geometry reconstruction. The `_showOrbitPath` method creates a Cesium `Primitive` with the pre-calculated positions, storing the GMST value used during baking.

```javascript
// src/data/satellites.js – _showOrbitPath()
function _showOrbitPath(noradId, color) {
  const bakeDate = new Date();
  const basePositions = computeOrbitPath(satrec, bakeDate);
  
  const primitive = new Cesium.Primitive({
    geometryInstances: new Cesium.GeometryInstance({
      geometry: new Cesium.PolylineGeometry({
        positions: basePositions,
        width: noradId === ISS_NORAD ? 2.5 : 2.0,
        vertexFormat: Cesium.PolylineColorAppearance.VERTEX_FORMAT,
      }),
      attributes: {
        color: Cesium.ColorGeometryInstanceAttribute.fromColor(color.withAlpha(0.6)),
        depthFailColor: Cesium.ColorGeometryInstanceAttribute.fromColor(color.withAlpha(0.35)),
      },
    }),
    appearance: new Cesium.PolylineColorAppearance({ translucent: true }),
    depthFailAppearance: new Cesium.PolylineColorAppearance({ translucent: true }),
    asynchronous: false,
    allowPicking: false,
  });
  
  _viewer.scene.primitives.add(primitive);
  _orbitPaths.set(noradId, { primitive, gmstAtBake: gstime(bakeDate) });
}

```

Each second, the `_updateOrbitPathRotations` function calculates the rotation delta between the bake-time GMST and the current GMST, applying this as a model matrix transformation to the primitive. This approach eliminates the flickering artifacts associated with regenerating Entity-based polylines every tick while maintaining sub-second accuracy.

```javascript
// src/data/satellites.js – _updateOrbitPathRotations()
function _updateOrbitPathRotations(nowDate) {
  for (const path of _orbitPaths.values()) {
    orbitFrameModelMatrix(path.gmstAtBake, nowDate, path.primitive.modelMatrix);
  }
}

```

The `orbitFrameModelMatrix` utility (lines 441-452 in [`src/data/satellites.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/satellites.js)) constructs a rotation matrix that accounts for the Earth's rotation since the geometry was baked, keeping the orbit ring aligned with the satellite's actual ground track.

## Complete Implementation Example

The following snippet demonstrates the complete workflow for tracking a satellite manually, mirroring the production implementation:

```javascript
import { twoline2satrec, propagate, gstime, eciToGeodetic, degreesLong, degreesLat } from 'satellite.js';
import * as Cesium from 'cesium';

// 1. Parse TLE (ISS example)
const tleLine1 = '1 25544U 98067A   24278.55555556  .00001234  00000-0  29233-4 0  9991';
const tleLine2 = '2 25544  51.6430  12.3456 0001234  23.4567  45.6789 15.48812345678901';
const satrec = twoline2satrec(tleLine1, tleLine2);

// 2. Propagate to current time
const now = new Date();
const { position } = propagate(satrec, now);
const gmst = gstime(now);
const geo = eciToGeodetic(position, gmst);

// 3. Render current position
const viewer = new Cesium.Viewer('cesiumContainer');
viewer.entities.add({
  position: Cesium.Cartesian3.fromDegrees(
    degreesLong(geo.longitude),
    degreesLat(geo.latitude),
    geo.height * 1000
  ),
  point: { pixelSize: 8, color: Cesium.Color.YELLOW }
});

```

To render the full orbital ring, sample positions across one period using `computeOrbitPath`, then attach a pre-render callback to maintain synchronization via `orbitFrameModelMatrix`.

## Summary

- **Data Ingestion**: Uses `twoline2satrec` in [`src/data/satellites.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/satellites.js) to parse TLEs into SGP4 records during catalog loading.
- **Coordinate Transformation**: Chains `propagate`, `gstime`, and `eciToGeodetic` to convert ECI vectors to geodetic coordinates for Cesium.
- **Geometry Baking**: Pre-computes orbit polylines with a fixed GMST reference to create closed, static geometry rings.
- **Rotation Synchronization**: Updates orbit alignment via model matrix transformations rather than geometry regeneration, eliminating render flicker.
- **Integration**: Combines **satellite.js** propagation with Cesium `Primitive` objects for high-performance visualization of thousands of satellites.

## Frequently Asked Questions

### Why does God's Eye View bake orbit geometry instead of regenerating it every frame?

Baking the orbit path once with a fixed GMST creates a closed, static polyline that represents the satellite's ground track relative to the rotating Earth. By applying a rotation matrix to the primitive each second via `_updateOrbitPathRotations`, the system avoids the CPU overhead and visual flickering associated with recalculating hundreds of vertices per satellite every frame. This technique enables smooth 60fps rendering even with large constellations like Starlink.

### What is the role of GMST in satellite.js orbit rendering?

**Greenwich Mean Sidereal Time (GMST)** represents the rotation angle of the Earth relative to the inertial frame. In [`src/data/satellites.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/satellites.js), `gstime` calculates this angle for a given date, enabling the transformation of ECI coordinates (output by SGP4 propagation) into Earth-fixed geodetic coordinates. For orbit baking, locking the GMST ensures the polyline represents a consistent ground track, while the model matrix rotation accounts for the time elapsed since baking.

### How accurate is the SGP4 propagation used in God's Eye View?

The **satellite.js** implementation provides standard SGP4 propagation accuracy, typically within a few kilometers for Low Earth Orbit satellites near the epoch of the TLE data. The visualization accuracy depends on the freshness of the TLE data in the CelesTrak catalog. The code does not apply additional perturbation corrections beyond the standard SGP4 model implemented in the library.

### Can this approach work with 3D libraries other than Cesium?

Yes. While [`src/data/satellites.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/data/satellites.js) uses Cesium `Primitive` objects and `Cartesian3` coordinates, the core **satellite.js** functions (`twoline2satrec`, `propagate`, `eciToGeodetic`) return standard JavaScript numbers and objects. The baked orbit positions are simple longitude/latitude/altitude arrays that can feed into Three.js, Babylon.js, or custom WebGL renderers by adapting the coordinate conversion and matrix math to the target framework's conventions.