Navball -:| The Game |:-

Building a 3D retro-style Navball satellite-tracking arcade game with Three.js

Have you ever tried to visualize three-dimensional spatial coordinates on a flat screen and felt that a simple 2D dial just wasn’t cutting it?

While developing control software for dual-axis altazimuth antenna rotators, I faced this exact UI design challenge. To solve it, I turned to the golden era of space flight simulators and avionics: the Navball (formally known as the Flight Director Attitude Indicator, or FDAI).

I ended up building Navball -:| The Game |:- a fully interactive, browser-based arcade game rendered in WebGL, complete with real-time synthesized Amiga-style chiptunes.

You can play the live game right now at www.madexp.it/navball.html.

Here is a deep dive into how I built it, the hardware-pivoted math behind the rotators, and how I synthesised a retro soundtrack natively in the browser.

The Core Concept: Alt-Az Rotators and “Over-Travel”

In satellite tracking (especially Low Earth Orbit or LEO satellites), standard 0° to 360° Azimuth and 0° to 90° Elevation limits present two major mechanical headaches:

  1. The Cable Wrap: Antennas have coax cables dangling from them. If a satellite spirals past North, a standard rotor might need to spin a full 360° in the opposite direction to avoid ripping the cables out. To prevent this, professional rotors feature “over-travel” capabilities (usually 0° to 450°, allowing 90° of overlap beyond North).
  2. The Zenith Flip: When a satellite passes directly overhead (90° elevation), a standard rotator has to instantly swing the azimuth by 180° to keep tracking. By extending the elevation from 0° up to 180° (continuous zenith passage), the antenna can simply “flip backward” over the zenith to track the pass seamlessly.

I mapped these exact real-world mechanics directly into the game’s controls:

  • Azimuth: 0° to 450° (with a dedicated OVERLAP cable-status warning light triggering past 360°).
  • Elevation: 0° to 180° (representing continuous sky-to-sky tracking).

3D WebGL Visualization & Mobile Portrait Responsiveness

To render the Navball, I utilized Three.js.

The sphere features the classic aviation split-color style: light blue for the positive/sky hemisphere and light brown for the ground/negative hemisphere.

A major challenge during development was screen responsiveness. On desktop screens, a fixed camera distance looked great. However, on mobile portrait screens, the sphere was zoomed in so aggressively that it broke the game’s UI.

To solve this, I wrote a dynamic camera projection rig that calculates the WebGL camera distance based on the screen’s real-time aspect ratio:

function updateCameraAndSizing() {
    const aspect = window.innerWidth / window.innerHeight;
    camera.aspect = aspect;
    
    // If the screen is vertical (portrait mobile aspect < 1), 
    // dynamically pull the camera back on the Z-axis
    if (aspect < 1) {
        camera.position.z = 6.2 / aspect; 
    } else {
        camera.position.z = 10.5; // Desktop/Landscape default
    }
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);
}

This guarantees the Navball stays perfectly scaled and beautifully framed on any device, whether you’re playing on an ultrawide monitor or a compact smartphone.

100% Code-Synthesized Amiga Chiptunes (Paying Homage to Chris Hülsbeck)

One of my favorite constraints of this project was zero external assets. I wanted the game to be completely self-contained in a single, lightweight HTML file. This meant I couldn’t load any .mp3 or .wav sound files.

Instead, I built a modular chiptune synthesizer from scratch using the native Web Audio API.

If you are a fan of old-school Amiga games like Turrican, you’ll recognize the tricks I used to squeeze a rich sci-fi soundtrack out of raw browser oscillators:

  • The Hülsbeck Arpeggiator: In the 1990s, trackers had limited audio channels. To fake chords, musicians rapidly alternated the frequency of a single oscillator. I programmed a fast, sawtooth-based arpeggiator that sweeps through minor chords (Em → C → G → D) every 25 milliseconds.
  • Procedural Drums: The kick drum is a sine wave with a rapid pitch drop (frequency modulation), while the snare and hi-hats are generated by loading a custom-allocated buffer of white noise and cutting it with rapid volume decay envelopes.
  • Progression/Song States: The track isn’t just a static loop; it features an Intro with minimal drums, a driving Main Theme, a syncopated half-time Industrial Bridge in Am, and a high-octave Cyber Climax.

How to Play

Your objective is simple: align your crosshair with the target nodes before the time runs out.

  1. Use the Azimuth and Elevation sliders to orient your antenna.
  2. Match the yellow avionics crosshair with the orbital targets (green spheres on the Navball surface).
  3. Once aligned, the signal locks, the target explodes, and your score increases.
  4. Be fast: Every two completed levels, the timer drops by 5 seconds (scaling all the way down to a chaotic 10-second limit!).

What’s Next? (Physical Hardware Integration)

Because the game’s calculations rely on pure mathematical coordinate translations (Euler angle tracking matrices), the core JavaScript logic is ready to act as a front-end client interface for physical antenna hardware.

My next goal is to feed real telemetry via a WebSockets server directly from an Arduino or ESP32-based rotator controller. The virtual Navball will rotate in real-time, mirroring the exact spatial attitude of the actual physical antenna on my roof!

Give the game a spin, crank up your speakers to enjoy the synthesized chiptunes, and let me know your high score in the comments!

👉 Play here: www.madexp.it/navball.html