Open-source browser-based simulation engine. Any vehicle is a JSON file. Any mission is a JSON file.
Born 05:32, Sunday 15 March 2026, Zürich. Built with Claude Code.
Realism as respect.
Every aircraft modeled here carried a human being. Every engine sound is calibrated from real recordings. Every mission is set on the right day, over the right place, with the right names.
We do not take sides in history. A nineteen-year-old climbing into a Zero over the Pacific deserves the same accuracy and respect as the one climbing into a Corsair. OpenSim honors duty. Not victory. Not ideology. Duty.
→ MANIFEST.md · CODE_OF_CONDUCT.md
Spotters — open the live radar, watch real flights within 1000nm, color-coded by destination. Click any aircraft and fly it at its current position, altitude, and heading.
Historians — load a mission from 1918, 1942, or 1956. Read the classified briefing document. Hear the crew in the right language. Fly the aircraft with the right physics.
Warbird buffs — the R-2800 Double Wasp fires 18 cylinders in two rows. The DB 601 startup takes 71 seconds from cold. Open the radiator flap too late and the coolant boils; spring a coolant leak and the engine seizes. Every number came from a POH or technical manual. If you've stood next to a running radial at an airshow, you'll know immediately whether it's right.
Space nerds — fly the Falcon 9 Demo-2 from T-3:00 with real webcast audio. Watch stage separation. Track the booster back to LZ-1, or out to the droneship. Fly a 590 km orbit. Deorbit, reentry, splashdown. Apollo 8 to the Moon, Apollo 13 home again, Starship IFT-12 and IFT-13. The physics are real. SpaceX, Apollo, V-2, Vostok, H3 — every program that ever put something into orbit deserves a mission here. Scott Manley would check the Isp. Everyday Astronaut would check the fairing. Check them.
PPL students — circuits at LSZF and LSZG. Full checklists. GPWS callouts. Emergency procedures. Scored debrief: alignment, speed, touchdown point. Engine failure scenarios with virtual ATC Mayday acknowledgement. Real weather via live METAR. No instructor needed to run a session.
Airline pilots-to-be — gate to gate on real SIDs, STARs and approaches. LNAV and VNAV fly the route, the ND draws it, the MCDU and kneeboard brief it — including the missed approach. EGPWS watches the terrain.
Makers — build a Dragon capsule mockup with a table saw, a Bambu A1, and four screens. Wire an ESP32 to a physical altimeter. Add a 6-DOF motion platform. The WebSocket data stream is open.
Anyone with a browser — no install. No login. No fee. Works on a laptop, tablet, or Raspberry Pi.
OpenSim is not a game. It is a modular simulation engine that runs entirely in the browser with zero dependencies and no build step.
- Real aerodynamic physics — lift, drag, thrust, weight from first principles. Not kinematic approximations.
- Procedural sound — engine sounds synthesised from physics. No samples. Wind rises with airspeed. The DB 601 fires 12 cylinders. The NK-12 turboprop beats at 3.8Hz.
- Historically accurate comm chain — every voice passes through the correct radio equipment for its era. VHF aviation, NASA S-band, Soviet VHF, SpaceX IP backbone. Pre-recorded voices, archival recordings, or TTS — all through the same chain.
- Real terrain — Mapbox Terrain-RGB tiles (Copernicus DEM, 30m resolution) projected onto a flat-earth pinhole camera. Mountain silhouettes, elevation-based colour, atmospheric haze, space skybox with stars. 60 fps on M2. A default public token is included — replace it with your own free token in
display/terrain.jsif you're running a public deployment. - 3D wireframe aircraft at 60 fps — flat-shaded painter's algorithm in pure Canvas 2D. Back-face culling, depth sorting, light-direction shading. No WebGL. No Three.js. No dependencies. Chase cam · side cam · wing view · plume cam · booster cam. For Falcon 9: engine plumes, LOX vent clouds, grid fins, S2 Merlin Vacuum bell, landing legs that deploy.
- Any aircraft — envelope, performance, handling, sound, crew language, checklists in one JSON file
- Any mission — weather, ATC clearances, classified briefing documents, scripted failures, crew voices in one JSON file
- Data-driven airports — runways, taxiways, gates and terminals derived from OSM + OurAirports. Fly an approach into an airport the sim has never seen and the ground environment is already there.
- Gate-to-gate — pushback, ATC taxi clearances over the taxiway graph, vacate detection, gate assignment, and VDGS docking guidance — both directions of the ground phase.
- Real navigation — SIDs, STARs and approaches from FAA CIFP / X-Plane nav data, airways, navaids. One route (
core/route.js) feeds the briefing, the ND, the MCDU and the LNAV/VNAV autopilot. - EGPWS + CFIT — terrain collision from the Mapbox DEM, reactive and predictive terrain warnings, sink-rate callouts, terrain display on the ND.
- Data-driven cockpit views — each aircraft JSON lists the zones it has: overhead, left, centre, right, pedestal. An airliner has all five; a fighter has its two side consoles; a light aircraft has the pedestal.
- Aircraft systems — fuel, electrical, hydraulics, battery state of charge, a generic engine-cooling heat balance (liquid or air-cooled, driven by real outside-air temperature), autobrake, speed brakes, parking brake.
- Live radar — real flights via OpenSky Network, color-coded by destination, route lines to arrival airport, 150/400/1000nm range
- Runs anywhere — laptop, tablet, Raspberry Pi, custom cockpit panels
# Any static server works
python3 -m http.server 8080
open http://localhost:8080No build step. No framework. No dependencies. Open index.html and fly.
First time? → docs/quickstart.md — the screens you'll see, a first flight to watch, a first takeoff of your own.
| Key | Action |
|---|---|
↑ / ↓ |
Pitch up / down (ignored while the autopilot is engaged) |
← / → |
Roll left / right · nosewheel steering on the ground |
+ / − |
Throttle — thrust lever ±10% on turbofans, ±5 kt otherwise |
t / T |
Trim nose up / nose down |
z |
COM swap: active ↔ standby |
E |
Engine start (cold-start aircraft) |
Q |
Engine cutoff · fuel selector cycle (manual aircraft) |
H |
Carburettor heat toggle |
B |
Brakes (hold) |
s |
Speed brake: RET → ARM → FULL |
f / F |
Flaps extend / retract |
g |
Gear toggle (squat switch: no gear-up on the ground) |
1–5 |
Situation presets (disabled during active missions) |
F1–F4 |
Thrust detents (aircraft-specific) |
k |
Kneeboard (briefings + checklists) |
n |
Mini map (heading, track made good, wind) |
v |
Cycle view: instruments / combined / outside |
c |
Cycle outside camera: cockpit fwd → chase → side → plume cam → booster cam → ship cam |
o · l · m · r · d |
Cockpit views: overhead · left · centre · right · pedestal (where the aircraft has them) |
Tab |
Cycle display mode |
p |
Pause |
x |
Audio on/off |
j |
Jump — warp straight to the next mission event |
r |
Cycle role: PF → PM → INSTRUCTOR (aircraft without a right console) |
Space |
PTT (push to talk) |
w / W |
Time warp — rockets + glass cockpits: 1× → 2× → 5× → 10× → 100× → next event (W steps back) |
Esc |
Back to the mission selector |
Ctrl+Shift+L |
Download flight telemetry (JSONL; CSV for rockets) |
Ctrl+Shift+B |
Download the black box |
G1000 (C172): Click any switch in the left panel to toggle it. Click the COM1 frequency in the PFD top bar to show/hide the COM panel.
Gamepad: Logitech Extreme 3D Pro out of the box. axes[0]=roll · axes[1]=pitch · axes[2]=rudder · axes[5]=throttle · buttons[1]=flaps · buttons[2]=gear
| Aircraft | Engine | Notes |
|---|---|---|
| Airbus A350-900 | Rolls-Royce Trent XWB | HB-IFA, autopilot, FMGS |
| Airbus A340-300 | CFM56-5C4 × 4 | Edelweiss HB-JME — data-driven from airport-planning DWG |
| Boeing 777-300ER | GE90-115B | Singapore 9V-SWY — data-driven, stepped GE90 core nozzle |
| Boeing 737-800 | CFM56-7B | G-DOCB |
| Boeing 737 MAX 9 | CFM LEAP-1B | N932AK, scimitar winglets |
| Airbus A220-300 | Pratt & Whitney PW1500G | airBaltic livery, Airbus-style PFD/ND/ECAM |
| Embraer E190-E2 | Pratt & Whitney PW1900G | Helvetic Airways, E-Jets glass cockpit |
| Cessna 172S Skyhawk | Lycoming IO-360 · 180hp | G1000 glass cockpit, cold-dark startup, grass strip |
| Robin DR400/140B Dauphin | Lycoming O-320 · 160hp | Flugschule Grenchen checklists |
| Pipistrel Velis Electro HB-SYC | Pipistrel E-811 · 57.6 kW | EPSI panel, battery SOC management, Grenchen |
| Vought F4U-1A Corsair | Pratt & Whitney R-2800 · 2000hp | Bent-wing bird, Pacific theatre |
| Messerschmitt Bf 109 E-4 | Daimler-Benz DB 601 · 1175hp | AudioWorklet 12-cylinder impulse model, radiator flap + coolant temp |
| MiG-15 'Fagot' | Klimov VK-1 | Centrifugal compressor intake, splitter vane |
| Avro 504K | Le Rhône 9J · 110hp | Gyroscopic precession, rotary blip switch |
| Tupolev Tu-95MS Bear H | Kuznetsov NK-12MV × 4 · 44740kW | Russian crew voices, contra-rotation LFO |
| Antonov An-225 Mriya | ZMKB Progress D-18T × 6 · 1 377 kN | Ukrainian crew voices, 500 t, Hostomel 2022 |
| Vehicle | Notes |
|---|---|
| Falcon 1 | 27 000 kg, 2-stage, RatSat payload, Omelek Island |
| Falcon 9 v1.0 | 333 400 kg, 9 Merlins, expendable — no grid fins, no landing legs |
| Falcon 9 Block 5 | 549 054 kg, RTLS booster recovery, grid fins, landing legs, MECO→SECO→Keplerian orbit |
| Falcon 9 Block 5 (590 km) | Tuned for 590 km near-circular insertion, droneship (ASDS) booster recovery — Inspiration5 |
| Starship / Super Heavy | Full stack, Raptor sea-level + vacuum, engine skirt |
| Saturn V | Apollo lunar stack, F-1 first stage, translunar injection, LM |
| Vehicle | Notes |
|---|---|
| SO-101 Robot Arm | 6-DOF arm visualisation |
| Yertle Quadruped Dog | 12-joint quadruped visualisation |
Airliners here are not shaped by eye. Manufacturers publish airport-planning drawings (DWG) so airports can lay out gates and taxiways — every fuselage station, wing root, gear track and engine position dimensioned to the millimetre. OpenSim reads those numbers.
Each airliner JSON carries a dimensions block of real measurements, and the wireframe geometry is derived from it:
- fuselage diameter sets the model scale
- the wing-to-body fairing is a flat-bottomed super-ellipse from its DWG cross-section
- landing gear from track / wheelbase / strut lengths
- wing position from its root station — move it and the engines, pylons and flap-track fairings follow
- the engine nacelle is a parametric builder — fan-cowl ratio, nacelle profile, stepped core nozzle — so a GE90 reads differently from a CFM56
- cockpit windows as explicit polygons matched to the type
Truth in, truth out. Accuracy is intrinsic, and a wrong number shows up as a visibly wrong shape. The A340-300 HB-JME (Edelweiss) is the reference build; the 777-300ER and A350 follow the same method.
The same idea, one level up: an airport is not modelled by hand either. ICAO Annex 14 standardises how every airport is laid out — and the open data already maps it. So the entire ground environment is derived, not drawn:
- runways from OurAirports (CC0) — thresholds, true bearing, width
- taxiways from OpenStreetMap (ODbL) become a navigable graph — shared nodes are vertices, each way carries its designator (
A,B,C…) - stands / gates from OSM
aeroway=parking_position— the painted lead-in line gives the stop point and the nose-in heading for free - terminals + aprons from OSM
aeroway=terminalfootprints, extruded to wireframe massing - jet bridges reach from each gate to the nearest terminal wall; VDGS units, taxiway signs and lead-in lines all fall out of the geometry
Run scripts/build-*.py once to bundle a new airport's OSM data. Then you can fly an approach into an airport the sim has never seen — München, say — and the runway, your taxi-in route, the assigned gate, the VDGS and the taxiway signs are all there, because the standardisation is the model. The drawing is the compression; the sim decompresses it.
One taxiway graph powers the entire ground phase, in both directions.
Departure — request pushback at your stand; a tug walks the aircraft back onto the taxiway. Ground gives a taxi clearance routed over the graph — "taxi to holding point runway 22 via Foxtrot, November" — the green route draws on the map, you hold short of and cross runways on request, then line up and go.
Arrival — after touchdown, vacate detection latches once you turn off the runway and slow to taxi speed. That unlocks the REQUEST → Taxi to gate call. Ground assigns a gate from the arrival dock (random within the dock, or a fixed stand) and reads the full clearance — "taxi to stand E27 via Foxtrot, Delta" — which lands on the kneeboard scratchpad while the green route leads you in. At the stand a VDGS shows your aircraft type, an azimuth dot tracking your offset from the lead-in centreline, the closing distance, and STOP at the parking line. ENG MASTER OFF and the turbofan coasts down by inertia to a descending spool-down whine.
Autobrake (LO/MED/MAX) is armed from pushbuttons on the centre panel and disarms when you touch the brakes. Brake-to-Vacate, the jet-bridge connect and brake temperature are the next links in the chain.
| Mission | Aircraft | Era | What |
|---|---|---|---|
| ILS Approach LSZH RWY 28 | A350 | Modern | San Francisco → Zürich, live METAR, ATC clearances, approach brief |
| Takeoff · Genève → München | E190 | Modern | Cold start at gate 10, pushback, taxi to RWY 22 — then the first arrival at a brand-new airport: gate-to-gate into EDDM, VDGS docking |
| Cruise · Warsaw → Rīga | A220 | Modern | FL350 inbound EVRA, live METAR, Air Baltic crew voices |
| Dock E · Zürich | A340 | Modern | Edelweiss at a Dock E gate, engine start, pushback off the midfield satellite, taxi out |
| ILS Approach LEMD RWY 32L | 737 MAX 9 | Modern | Final into Madrid Barajas, data-driven gate-to-gate (temporary — the EGLL 27L approach returns) |
| Bozeman → Anchorage | 737 MAX 9 | Modern | Alaska, cold start at gate B7, depart over the Bridgers and the Rockies (temporary ILS 15 approach) |
| Singapore → London | 777-300ER | Modern | Singapore Airlines at Changi, evening departure out over the Strait |
| Cross-Country LSZG→LSGN | C172 | PPL | Cold-dark startup, Schnupperflug route, live METAR |
| VFR Pattern LSZF | C172 | PPL | Grass strip Speck-Fehraltorf, circuits, kneeboard |
| VFR Circuit LSZG | Robin DR400 | PPL | Grenchen, Flugschule checklists, live METAR |
| Electric Circuit HB-SYC | Velis Electro | PPL/Electric | EPSI panel, battery SOC, no magnetos |
| Airshow Ground Run | Bf 109 E-4 | 2025 | DB 601 startup, D-FEML at Hahnweide |
| Patrol — Marne 1918 | Avro 504K | 1918 | WWI rotary engine, Le Rhône blip switch |
| Operation Wolfskopf | Bf 109 E-4 | 1942 | Arctic, scripted engine failure, NIFLHEIM |
| The Wasp · Bougainville | F4U-1A Corsair | 1943 | Pacific patrol, R-2800, ARC-5 radio |
| No Bomber Lost · Rabaul | F4U-1A Corsair | 1943 | Ground attack, Rabaul harbour |
| Przechwycenie · Dęblin | MiG-15 | 1955 | Polish interceptor. Twelve years ago a Spitfire. Today a red fifteen |
| Aufklärungsflug Nordmeer | Tu-95MS Bear H | 1956 | Olenya AB, Soviet ATC, Cold War dossier |
| Mriya — Die letzte Reise | An-225 | 2022 | Hostomel, Ukrainian ATC, 3 February 2022 |
Story missions (Wolfskopf) run with a fixed scenario and skip the scenario chooser.
| Mission | Vehicle | Year | What |
|---|---|---|---|
| Apollo 8 — First to the Moon | Saturn V | 1968 | Borman, Lovell, Anders. TLI, first crewed lunar orbit, Earthrise |
| Apollo 13 — Houston, We've Had a Problem | Saturn V | 1970 | Oxygen tank explosion at 56 h. The LM becomes a lifeboat, free return home |
| Falcon 1 — Omelek Island | Falcon 1 | 2008 | First privately funded orbital rocket. 2-stage gravity turn, RatSat to LEO |
| CRS-1 — Engine Out | Falcon 9 v1.0 | 2012 | Engine failure at T+79s. Vehicle reaches orbit on 8 engines. Lofted ascent, no recovery |
| Crew Dragon Demo-2 | Falcon 9 Block 5 | 2020 | Behnken + Hurley. First crewed Dragon. Orbit-targeting guidance, boostback to LZ-1. Full webcast audio |
| IFT-12 — Starship V3 Debut | Starship | 2026 | S39 / B19. One Raptor out on the ship, guidance extends the burn. Indian Ocean splashdown |
| IFT-13 — First Mechazilla Ship Catch | Starship | 2026 | S40 / B20. Booster catch at Starbase, one orbit, reentry, ship catch attempt |
| Inspiration5 — Commander Leutwyler | Falcon 9 Block 5 | 2030 | Four civilians. 590 km orbit. Booster lands on the droneship. 3-day mission. Deorbit → reentry → splashdown |
| Mission | Vehicle | What |
|---|---|---|
| H2D · GPS Autonomy Test | Markus' Demonstrator | GPS autonomy |
| Workspace · Familiarisation | SO-101 Robot Arm | Arm visualisation, hardware-in-the-loop to an ESP32 via server/robot_bridge.js |
| Dog Lab · Visualisation | Yertle Quadruped Dog | Joint-by-joint visualisation |
Press Near me to fetch real flights via OpenSky Network. No account needed.
- Range: 150nm (local) · 400nm (regional) · 1000nm (intercontinental)
- Color coding: each destination airport gets a color. All flights going there share it.
- Routes: callsign lookup via adsbdb.com —
LSZH → JFK,ORD → EDDM - Click: spawn the sim at that aircraft's current position, altitude, and heading.
OpenSim uses a real aerodynamic force balance — point-mass wind axes:
L = q × S × CL(α, flaps)
D = q × S × (CD₀(flaps) + k × CL²)
T = throttle × Tmax × ρ/ρ₀ × enginePower
W = mass × g
dv/dt = (T·cos(α) − D − W·sin(γ)) / m
dγ/dt = (L − W·cos(γ)) / (m·v) − 0.4·γ + trim × 0.0015
ISA density: ρ = 1.225 × (1 − 2.2558e⁻⁵ × alt_m)^4.2559
Angular inertia — PD controller with momentum (τ_roll = 0.18s, τ_pitch = 0.30s). Stall — high-alpha snap + energy stall (progressive sink at low speed). Gyroscopic precession — rotary engines precess when pitch or roll rate changes.
Point-mass gravity turn, programmed FPA profile, extended ISA through 140 km. Thrust interpolated sea-level ↔ vacuum.
Orbit-targeting guidance — the upper stage steers to a target orbit and cuts off on velocity, not on a clock. Callouts fire on events, not timestamps. tools/booster-descent-sim.mjs simulates ascent and booster descent offline and reports each mission's reachability.
Staging — cold-staged vehicles show a visible separation gap during the coast (hot-stagers and the Saturn V don't). Upper-stage thrust spools up. Dragon separates from Stage 2 with drift and a Draco ΔV.
Orbital propagation — Velocity Verlet integrator in ECEF. Energy conserved < 0.01% over 30 minutes. Dragon / Stage 2 propagate independently after separation.
Deorbit + reentry — retrograde ΔV at deorbitT, drag below 140 km, drogue at 5 500 m, mains at 1 800 m, terminal ~6 m/s.
Booster recovery — flip → boostback → coast → entry burn → glide → landing burn, engine-first. Single engine proportional throttle v²/2h. Grid fins deploy after separation; 3D landing legs deploy over 5 s and cast a ground shadow. Two recovery modes, set per vehicle in the JSON:
- RTLS — boostback to the landing zone (Demo-2 → LZ-1).
- ASDS — no boostback; the booster lands on a droneship at the real landing point (Inspiration5). Predictive landing guidance, and a ship cam (mode 6) that shows the full booster coming down onto the deck.
The booster cam (mode 5) auto-activates during boostback, entry burn, landing burn, and landing, then restores the previous camera.
Time warp — w steps 1× → 2× → 5× → 10× → 100× → next mission event; j jumps straight to the next event. A 3-day Inspiration5 orbit passes in minutes.
The Cessna 172 uses a three-zone canvas layout matching the real G1000 NXi installation:
┌─────────────┬───────────────────────┬───────────────────────┐
│ Switch panel│ PFD │ MFD │
│ (14%) │ attitude · tapes │ engine strip │
│ │ HSI · ILS · COM │ EGT/CHT/OIL · fuel │
│ MASTER │ │ map overlay │
│ AVIONICS ├───────────────────────┴───────────────────────┤
│ FUEL PUMP │ Analog backup: ASI · AI · ALT │
│ MAGNETO │ (always powered — vacuum / standby) │
│ LIGHTS │ │
└─────────────┴───────────────────────────────────────────────┘
Cold-dark startup sequence:
- MASTER BAT → ON
- AVIONICS → ON (powers both screens)
- FUEL PUMP → ON (boost pump audible)
- MAGNETO → click to cycle: OFF → R → L → BOTH → START
- START fires the engine lifecycle — whirr → catch → idle → running
- MAGNETO snaps back to BOTH when engine catches
- FUEL PUMP → OFF after engine running
COM panel: click the COM1 frequency in the PFD top bar to show/hide the full COM + transponder panel. Real mission frequencies from the mission JSON. Appears on the right side of the screen.
All engine sound is synthesised. No samples.
| Layer | How |
|---|---|
| DB 601 / IO-360 / Le Rhône | AudioWorklet impulse model — cylinders fire at individual crankshaft angles |
| Lycoming startup lifecycle | Electric starter whirr → catch → idle → running (3-phase, ~8s) |
| Fuel boost pump | Dual sawtooth 241/256 Hz + bandpass noise 370 Hz + 11 Hz LFO, fade in/out |
| NK-12 turboprop | 3.8Hz LFO contra-rotation beat + slewTime 1.8s |
| GTF / HBF / LBF | Oscillator harmonics, engine-specific filter and gain |
| Wind | White noise → bandpass, gain ∝ speed² |
| Ground creak | Lowpass rumble, WoW × speed |
| Coolant hiss | Rises as enginePower drops |
| Carb ice / heat | Power reduction as ice builds; restoration on heat ON |
See docs/db601-synthesis.md for the full DB 601 physical impulse model.
Every voice in a mission passes through the correct radio equipment for its era. The voice source and the comm chain are decoupled:
[voice source] [commProfile chain]
ElevenLabs MP3 ──► bandpass + presence boost
Archival recording ──► noise floor + crackle
H4n Pro WAV ──► carrier hum + squelch tail ──► speakers
Web Speech TTS ──►
↑
environment bleed
(engine tap from sound.js)
| Profile | Era | Character |
|---|---|---|
vhf-aviation |
Standard ATC 118–137 MHz | Bandpass 350–3400 Hz, presence boost, squelch tail |
tower-quiet |
Ground station TX | Cleaner — quiet room, good equipment |
sband-apollo |
NASA S-band MSFN | More hiss — 380 000 km signal path |
vhf-vostok |
Soviet VHF 1961 | Narrow, harsh, heavy noise floor |
ip-spacex |
SpaceX IP backbone | Near-phone quality, minimal processing |
Cockpit profiles tap the live engine output and mix it into received comms. A Bf 109 pilot hears ATC with DB 601 underneath. A Dragon crew hears CAPCOM with Merlin rumble during ascent.
Three tiers, all through the same chain:
- ElevenLabs MP3 — pre-generated character voices (e.g. Ukrainian ATC: Olena voice)
- Archival recording — real mission audio. NASA audio is public domain. SpaceX webcasts.
- Own recording — Zoom H4n Pro, 24-bit WAV. For Inspiration5: Markus, Lydia, Pradeep, and MS2 record their own lines. The actual crew, in the actual simulator, years before launch.
{
"commProfile": "vhf-aviation",
"atcClearances": [
{ "t": 5, "audio": "audio/hostomel/atc_olena.mp3" },
{ "t": 8, "text": "Мрія, запуск двигунів дозволено.", "voice": "atc" }
]
}If an audio: file is missing, the mission falls back to TTS automatically.
See docs/audio-chain.md for full documentation including spectral analysis results.
Five independent voices, each with distinct character:
| Voice key | Role | Character |
|---|---|---|
crew |
CDR (Pilot Flying) | Daniel — calm, authoritative |
pm |
PLT (Pilot Monitoring) | Karen — professional, precise |
atc |
CAPCOM / ATC | Gordon — official radio |
narrator |
Webcast host (John) | Natural pace, technical |
narrator2 |
Webcast host (Lauren) | Enthusiastic, human moments |
Crew language set per aircraft via "crewLang": "ru-RU". Every utterance — GPWS, PM callouts, ATC clearances — in that language.
Named rocket events: supersonic · maxq · ceco · meco · stagesep · seco · orbit · booster_flip · booster_boostback · booster_landing · deorbit · blackout · signal · drogue · mains · splashdown
OpenSim streams live sim state over WebSocket from a Raspberry Pi hub. Any device on the network can consume it.
Physical cockpit mockup — four screens, four seats, each a browser tab. Build the frame from wood and 3D-printed panels. FabLab Winti has the tools. The software is already there.
6-DOF motion platform — G-force, pitch, roll, vertical acceleration are computed every tick. Publish the WebSocket format, makers wire their platform to the data stream. MaxQ pushes you back. Stage sep: weightlessness.
Physical instruments — ESP32 reads WebSocket state keys (S.alt, S.spd, S.hdg...) and drives stepper motors. 3D print the bezels, wire the needles. Altimeter, airspeed, VSI, RPM — all physical, all driven by the same physics engine.
The software is the platform. The hardware is the expression. MIT open source means anyone can build.
core/
state.js — single source of truth, all sim state
loop.js — rAF loop + tickSim(dt), the one simulation step (the test API's simStep runs it too)
physics.js — aerodynamic force balance, ground roll, brakes/autobrake, CFIT, cooling
thrust.js — engine thrust models
rocket.js — gravity turn, orbit-targeting guidance, staging, RTLS/ASDS, propagator, reentry
mission.js — loads aircraft + mission JSON, live METAR fetch
lifecycle.js — mission reset registry: modules register their reset, loadMission runs them
random.js — simulation random numbers (turbulence), seeded in test mode
vocab.js — the names data and code share (triggers, panels, engine sounds, …); typos throw
schema/ — data schemas for missions, catalog, aircraft; checked by npm test and at load
mission-start.js — named mission start positions (gates, runways)
catalog.js — mission picker catalogue, loaded from missions/catalog.json
aircraft-config.js — aircraft JSON → derived config
route.js — buildFullRoute: SID + airways + STAR + approach — one route for every consumer
airway-graph.js — en-route airway network
lnav.js · vnav.js — lateral + vertical navigation, leg sequencing
managed-speed.js · fma.js — managed speed targets, flight-mode annunciation logic
baro.js — barometric altimeter / QNH
taxi-graph.js — taxiway network graph + routing, derived from OSM
pushback.js — pushback path
fuel.js · electrical.js · hydraulics.js · battery.js — aircraft systems
failures.js — scripted failure event processor
crew.js — five voices, rocket events, GPWS/EGPWS, language-aware, radio dispatch
radio.js — commProfile chain: createRadioChain(), playThroughChain()
sound.js — procedural engine audio, startup lifecycle, engine bleed tap
*-processor.js — AudioWorklets: DB 605, Le Rhône, Lycoming, R-2800, radio crackle
input.js — keyboard · mouse · Gamepad API
liveflight.js — live radar (OpenSky) → spawn into a real flight
hil.js — hardware-in-the-loop bridge
telemetry.js · blackbox.js — flight recorder (2 Hz JSONL) + black box
app/
session.js — mission session: listeners/timers a mission registers; stop() removes them (next mission, Esc/Back)
display/
pfd.js · pfd_instruments.js — PFD + reusable instrument primitives (attitude, tape, HSI, FMA)
panel_renderer.js — drives Airbus PFD/ND/ECAM and E190 PFD/ND from panel JSON
ecam.js · fma.js — ECAM pages, FMA
overhead.js · leftconsole.js · centerconsole.js · rightconsole.js · pedestal.js
— data-driven cockpit views (O / L / M / R / D)
pushbutton.js — generic Airbus pushbutton
smooth.js — shared display-lag smoother (per-quantity time constant)
g1000.js — Garmin G1000: PFD, MFD, engine strip, switch panel, backup gauges
bf109.js · f4u1a.js · velis_epsi.js · clock.js — steam-gauge panels, EPSI, cockpit clock
dragon.js · rocket_display.js · dsky.js · lm_display.js
— Dragon crew seats, SpaceX telemetry, Apollo DSKY + LM
cislunar.js · earthrise.js — cislunar trajectory view, Earthrise
kneeboard.js — briefings, checklists, scratchpad, approach + missed approach
map.js — world map (rocket) + local mini-map + vehicle silhouettes
terrain.js — 3D outside view: Terrain-RGB, day/night sky, stars, water, space
terrain-color.js · night-lights.js — Blue Marble day colour, VIIRS night glow
outside.js — 3D wireframe renderer: painter's algorithm, flat shading, Canvas 2D
outside-*.js — per-family geometry (warbirds, widebody, rocket, space) + draw helpers
com.js — COM radio + transponder
robot_arm.js · yertle_quadruped.js — Lab visualisations
*-data.js — bundled data: runways, taxiways, stands, terminals, towers, navaids,
airways, procedures, satellites, windsocks
svg/ — vehicle silhouettes
panels/ — panel layout JSON: airbus-pfd/nd/ecam, e190-pfd/nd, g1000, dr400
aircraft/ — JSON vehicle definitions
missions/ — JSON mission definitions + catalog.json
audio/ — voice assets (not in git — generate locally, see docs/audio-chain.md)
docs/
architecture.md — how the pieces fit together
url-routing.md — mission URLs, deep links, browser history
panel-system.md — panel JSON + renderer
creating-missions.md — writing a mission
keybindings.md — full key reference
db601-synthesis.md — DB 601 physical impulse model, all parameters
sound-calibration.md — engine sound calibration
audio-chain.md — radio chain architecture, commProfile presets, pipeline
scripts/
build-*.py — bundle open data: runways, taxiways, stands, terminals, towers,
navaids, navdata, airways, CIFP procedures, satellites
rescale-aircraft.py — rescale aircraft geometry to real dimensions
render-*.mjs — offline engine synthesis (DB 601, R-2800) → WAV
analyze-*.py — spectrograms: DB 601 startup, R-2800, radio chain
tools/
booster-descent-sim.mjs — ascent + booster descent simulation, guidance + reachability
tests/ — Node unit tests (*.test.mjs / *.test.js) + Playwright specs (*.spec.js)
server/
hub.js — WebSocket hub (Node.js, runs on Raspberry Pi)
robot_bridge.js — hub ↔ SO-101 ESP32 bridge
npm install
npx playwright install chromium
npm testnpm test runs the Node unit tests first (tests/*.test.mjs, 113 tests, including a schema check of every mission, catalog entry and aircraft), then the Playwright browser specs (165 tests in 13 files), all headless: physics, fuel/engine/carb heat, thrust, sound synthesis and state machines, COM radio, FMA, managed speed, route building, VNAV, aircraft data, rocket staging and orbital mechanics. Runs are repeatable: in test mode simStep is the only clock and turbulence is seeded. Calibration specs run separately with npm run calibrate.
Running subsets, reading the output, the ?test=1 API, writing tests and the current known failures → docs/testing.md.
{
"id": "your-aircraft",
"name": "Your Aircraft",
"crewLang": "de-DE",
"envelope": { "cruiseSpd": 122, "maxSpd": 163 },
"performance": {
"mass": 1157, "wingArea": 16.2, "thrustMax": 1800,
"CL_0": 0.2, "CL_alpha": 5.0, "CL_max": 1.9,
"CD_0": 0.028, "inducedK": 0.055, "Vr": 55
},
"handling": { "rollRate": 30, "pitchRate": 5, "maxBank": 60 },
"sound": { "engineType": "lycoming-o360" }
}{
"id": "your-mission",
"aircraft": "aircraft/your-aircraft.json",
"commProfile": "vhf-aviation",
"weather": { "source": "live", "icao": "LSZH" },
"initialState": { "lat": 47.39, "lon": 8.78, "alt": 1788, "spd": 0, "hdg": 260 },
"departure": { "name": "Your Airfield", "elevation": 1788 },
"atcClearances": [
{ "t": 10, "audio": "audio/your-mission/atc_line1.mp3" },
{ "t": 30, "text": "Radar contact.", "voice": "atc" }
],
"failures": [
{ "trigger": { "type": "time", "t": 120 }, "type": "engine_power", "value": 0.0, "rampTime": 60 }
]
}Every flight recorded automatically. Ctrl+Shift+L downloads JSONL (CSV for rockets); Ctrl+Shift+B downloads the black box.
{"t":48.6,"alt":1789,"spd":68.7,"vs":11,"pitch":9.06,"roll":0,"hdg":260,
"enginePower":1,"flaps":0,"lat":47.385,"lon":8.776}Feed to pandas, plot, debrief approaches, or stream to an AI co-pilot.
OpenSim is a real training tool. Not a game with training features — a training tool that happens to run in a browser.
Every aviation mission has a scenario setup screen before you fly. Choose your conditions:
| Scenario | What happens |
|---|---|
| CLEAN | Normal flight. Practice circuits, navigation, communication. |
| PARTIAL | Engine degrades to 50% power after 60–300 seconds. Partial power emergency. |
| ROUGH | Engine bang + 30% power. Forced landing required. |
| FAILURE | Full engine failure. Glide, pick a field, land it. |
Timing is random (60–300s) or fixed. You set it up, then forget it. The failure comes when it comes.
After every flight — including crashes — the debrief shows two blocks:
Emergency Response (if a failure occurred)
- Survived
- Best glide speed — did you pitch for 65kt within seconds of the failure?
- Squawk 7700 — how many seconds after the failure?
- 121.5 tuned — how many seconds after the failure?
- Mayday called — did you key the mic?
Approach Data (every landing)
- Runway alignment — heading deviation in degrees
- Speed at touchdown — vs target approach speed
- Touchdown point — distance from threshold
Aviate → Navigate → Communicate
In an emergency: pitch for best glide first. Find a field. Then squawk 7700 — one knob, passive, radar sees you immediately. Then tune 121.5 and call Mayday.
When you tune 121.5 during an engine failure, virtual ATC responds:
"HB-CBX, Mayday acknowledged. Say position and souls on board."
That's the training. Do it enough times and the hands move before the brain catches up.
Press K to open the kneeboard. Every aircraft has:
- Departure briefing
- Normal checklists (startup through shutdown)
- Emergency checklists — engine failure, partial power, engine fire
In a C172 engine failure:
- Best glide — 65kt
- Field — select, into wind
- Fuel selector — BOTH
- Mixture — rich
- Carb heat — ON
- Throttle — full, then back to idle
- If no restart: Mayday on 121.5, squawk 7700
You don't rise to the occasion. You fall to your training.
Sim crashes are ones you walk away from. Run the engine failure scenario until the checklist runs itself. That's chair flying with feedback.
A 40-million-franc certified simulator trains the same reflex. OpenSim runs in a browser, costs nothing, and sends you a scored debrief.
Send the student a URL the night before a lesson. Assign a scenario — ROUGH, fixed timing 120s. Student flies three circuits, gets three debriefs. They arrive having already run the emergency. Ground time becomes review, not introduction.
The debrief scores are honest. "Squawk 7700 — not set" is not an opinion. It happened, or it didn't.
- PPL(A) training — circuits at LSZF and LSZG
- Kitfox electric digital twin — the sim IS the avionics
- Time machine — 1918 · 1942 · 1956 · Apollo 11 · Challenger · Demo-2
- FabLab cockpits — anyone with a laser cutter and a Bambu A1 builds a Dragon capsule
- Real crew training — Inspiration5 crew records their actual voices on an H4n Pro, years before launch
- Ghost aircraft replay — record instructor flight as JSONL, student flies alongside
- Hardware panels — RPi WebSocket bridge, ESP32 instruments, 6-DOF motion
Because a 40-million-franc simulator should not be the only way to train crew. Because anyone on earth with a browser should be able to fly. Because the same URL that runs on a MacBook runs on a Raspberry Pi in a FabLab in Nairobi. Because somewhere in the permafrost near Titovka, a man is still waiting to be found.
VotingTriad — Airbus-style envelope protection on three LilyGo T-CAN485 nodes (ESP32 + onboard CAN transceiver). BNO055 IMU + BMP390 barometer per node. Three nodes vote on vehicle state at 50 Hz: 3/3 → Normal Law · 2/3 → Degraded · 1/3 → Direct Law. CRSF parser for ExpressLRS at 420000 baud, RS485 fallback with time-division multiplexing if CAN goes down. Connects to OpenSim's HIL bridge over WebSocket — real RC sticks drive a simulated aircraft through the actual flight computer. An iron-bird rig on a breadboard.
Geographic data, fetched live or bundled from open datasets:
- OurAirports — runway thresholds, dimensions and designations (public domain). Bundled for mission airports in
data/runways-data.js; regenerate withscripts/build-runways.py. Also navaids and airport frequencies (scripts/build-navaids.py,scripts/build-navdata.py). - OpenStreetMap — runways, taxiways, aprons, holding positions, parking stands, terminal footprints and control towers via the Overpass API (© OpenStreetMap contributors, ODbL).
- FAA CIFP and X-Plane nav data — SIDs, STARs, approaches and airways for the mission airports (
scripts/build-cifp.py,scripts/build-airways.py). - Mapbox — terrain elevation (Terrain-RGB / Copernicus DEM) and water polygons.
- Natural Earth — 1:50m world coastlines for the briefing's route and SID/STAR charts (public domain). Bundled in
data/coastlines.js; regenerate withscripts/build-coastlines.py. - NASA Blue Marble (day ground colour) and VIIRS / Black Marble 2016 (night lights) — public domain.
MIT © 2026 Markus Leutwyler Built with Claude Code by Anthropic.
Developed with Claude. Things happen there you could not imagine.
