Releases: hook-365/adsb-3d
v0.4.0: performance and UX push
A frontend + backend performance and UX push. The headline goal was
to make the Europe / Hetzner feed (~1500 contacts) feel as snappy
as the local feed in both initial load and steady state, and to
let survey aircraft and loitering tankers keep their full in-scope
trails instead of being cut off at 10 minutes.
Added
- Click to extend trail. Selecting any aircraft now lifts its
per-hex trail cap and triggers a 24 h history backfill, so a
single survey orbiting an airfield gets its full pattern even on
a busy feed. The per-hex cap survives the aircraft dropping out
and reappearing within the session. - URL deep-link isolation. Loading a shared
#hexlink now
also applies that hex as the search query, so the recipient
lands on just that aircraft and its trail. Clearing the search
box brings the rest of the fleet back. - Per-feed trail policy. The local feed defaults to unlimited
trail length with a 4 h history backfill window, so survey
aircraft and loitering tankers keep their full in-scope history.
Higher-density feeds keep the conservative 600-point cap and
30 min backfill. - Stationary sample dedup. Aircraft that haven't moved past the
jitter threshold only sample once per minute instead of once per
second, so a parked aircraft over 12 hours accumulates ~720
trail points instead of ~43k. - Altitude inheritance. The Aircraft record carries an
altFtKnownflag and the store keeps a per-hex last-known
altitude cache; transient frames that lackalt_baroand
alt_geomsubstitute the prior good altitude instead of
snapping the cone to ground. Historical backfill does the same
forward-inheritance walk duringparsePoints.
Changed
- Aircraft list is now virtualized. Only rows inside the
scroll viewport (plus a small overscan) get real DOM. Initial
load on Europe is dramatically faster and scroll feels native
at any fleet size. Filter / sort / search changes re-target
scroll to keep the selected row in view; snapshot-only updates
leave scrollTop alone. - Search filters the map. Typing in the panel search box
now hides non-matching aircraft from the scene as well as the
list, matching the behavior of the MIL / GROUND / AIR / EMERG
filter buttons. Selected aircraft are exempt. - Lazy-loaded feature modules. The voice panel, historical
playback, heatmap layer, and ACARS browser modal are dynamic
imports now. They no longer ship in the cold-load bundle for
deployments (or sessions) that don't use them. - Browser tab title reads
ADS-B 3D · {feed location}and
updates on feed switch. - Backend
track-service./tracks/{hex}and
/tracks/bulk/timelapseauto-downsample whenresolution=full
is requested over a window wider than 4 hours; targets ~7200
points across the window, so a 24 h selection-extension call
returns ~700 kB instead of ~8.6 MB. asyncpg poolmax_size
raised from 20 to 40 so multi-tab and bursty backfill traffic
no longer pushes the acquire timeout.
Fixed
- Invisible-aircraft clicks. Three.js's raycaster doesn't
honorGroup.visible = false, so invisible pick proxies inside
filtered-out aircraft were still registering hits. Clicking on
empty sky in MIL-filter mode no longer surfaces civilian
aircraft hiding underneath. - Trails dipping to ground. Transient upstream frames with
no altitude data used to snap the cone and trail to zero
altitude. The new altitude inheritance keeps the cone at its
last known altitude through the gap, both live and on backfill.
Performance
The reconciler is the biggest contributor; together these changes
take the steady-state per-frame cost on Europe from "noticeably
laggy" to "indistinguishable from local".
- Reconciler
syncFramegates per-aircraft work on per-hexrev
counters from the store. Most frames find every aircraft at the
same rev as last frame and skip the full refresh block. altitudeColorCached/altitudeColorStyleCachedreturn shared
Colorinstances and interned CSS strings bucketed by 250 ft
altitude steps.refreshTrail,refreshColor,refreshLabel
switched over, saving ~450 kColorallocations per second on
a busy feed.- Yaw quaternion math is cached against
lastTrackDegand shared
between the cone and the ground icon. updateLabelLODskips its per-entry pass when the camera hasn't
moved past a small epsilon since the last call.- Settings subscriber only walks the entry map when one of the
three visibility-relevant keys actually flipped. - Label frustum culling. Labels whose anchor is outside the
camera view are flipped tovisible = falseso
CSS2DRendererskips them; a panned-in view on Europe does
~150 DOM transform writes per render instead of ~1500. - Growable + incremental trail buffers. Trail buffer
allocations grow by doubling as needed. A fast path appends
only the new tail segments to existing buffer slots when the
trail's first sample is unchanged. A selected aircraft with a
multi-hour trail goes from rewriting the entire buffer every
refresh to writing one new segment.
Tests
127 → 145 unit tests across reconciler / store / filter / altitude
color / parsePoints altitude inheritance.
v0.3.0: WebXR support (Phases 1-5)
v0.3.0 brings the full WebXR pipeline online. Any connected headset can now open an immersive session directly from the page.
Enter VR in Settings opens an immersive-vr session with head tracking, controllers as laser pointers, and a world-space billboard above the selected aircraft. Trigger pulls raycast aircraft picks through the same selection path as a mouse click, so the detail panel and follow-cam stay in sync.
In-VR wrist menu on the left controller, tilted toward the eye. The right laser hovers rows and the trigger activates them: cycle Theme, cycle Basemap, toggle Range rings, Labels, or Alt lines. No need to lift the headset to change anything.
Locomotion via the thumbsticks and buttons. Left stick scales the world from tabletop to room-scale (persisted across sessions). Right stick snap-turns 30° per push. Right A/X button recenters the scope in front of you.
Enter AR on devices that support immersive-ar (Quest 3, Vision Pro, etc.). Sky and basemap drop away and aircraft float in your living room via passthrough.
See CHANGELOG.md for the full entry.
v0.2.0 — Color themes + FAA chart basemaps
v0.2.0 — color themes + FAA chart basemaps
Five built-in palettes (Midnight Glass, Daylight, Sectional Chart,
Phosphor CRT, High Contrast) with prefers-color-scheme auto mode and
live in-place switching across CSS and Three.js materials.
Five FAA aeronautical chart basemaps (US only): Sectional, Sectional
- Roads, Helicopter, IFR Low, IFR High — served via VFRMap with
auto-discovery of the 56-day chart cycle at container start.
See CHANGELOG.md for the full entry.
ADS-B 3D v0.1.0
First public release of ADS-B 3D — a real-time 3D visualization of ADS-B aircraft traffic. A ground-up TypeScript / Three.js rewrite of the original vanilla-JS app.
Highlights
- Live 3D view — per-second updates with a tar1090 altitude color palette; click-through aircraft detail cards with photos, filed routes, and autopilot data.
- Historical playback — scrub the last 1h / 24h / 7d at 1×–60× speed, with a 3D airway-density heatmap.
- ACARS — optional datalink message decoding with OOOI flight-phase tracking.
- Multi-feed — switch between multiple receivers in-place, no page reload.
- VHF voice scanner — optional call-based airband audio panel.
Ships as a single nginx Docker image plus optional track-service and acars-service.
Getting started
See the README for the quick start, and CHANGELOG.md for full detail.
Multi-arch images (linux/amd64, arm64, arm/v7) build from this tag and publish to GHCR:
ghcr.io/hook-365/adsb-3d:0.1.0ghcr.io/hook-365/adsb-track-service:0.1.0ghcr.io/hook-365/adsb-acars-service:0.1.0