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:
- 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).
- 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.
- Use the Azimuth and Elevation sliders to orient your antenna.
- Match the yellow avionics crosshair with the orbital targets (green spheres on the Navball surface).
- Once aligned, the signal locks, the target explodes, and your score increases.
- 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