An unofficial browser tribute to Elite (1984) by David Braben and Ian Bell. Harmless is the combat rating you start at; the ladder ends at E L I T E.
Authentic wireframe ships, the original byte-accurate procedural galaxy (Lave is system 7, as it should be), modern shader-driven suns and planets — and ship AI trained by neuroevolution self-play, flying both the pirates that hunt you and the traders that fight back.
![]() |
![]() |
Every planet is generated from the 1984 seeds — Diso's violet coastlines above, "Population: 4.1 Billion (Black Furry Felines)", exactly as the original's data tables intend. Below: three trained pirates converging in the combat viewer.
Docs index: Development log · Architecture tour · AI design · Training runs & results · Reproducing the training · Gap analysis vs the 1984 original · The reference ship catalogue · Every damage path · The combat trainer · Browser play trials · The Jameson Trials
npm install
npm run dev # http://localhost:5173 (landing page)
# http://localhost:5173/play (the game)
# http://localhost:5173/viewer (AI combat viewer)
# http://localhost:5173/gallery (all 38 released hulls)
# /manual · /novella (manual and story)
npm run build # lint + tests (via prebuild), then production build to dist/
npm run train -- attack --gens 400 # retrain the pirate AI (Node ≥ 22.6; see train/README.md)
npm run evaluate # held-out tournament for the current brains
npm test # invariant + simulation tests (no framework)
npm run elite-a # the reference-catalogue alignment gate (<1s)
npm run campaign # headless balance playtest: 40 careers × 60 legs
npm run campaign -- 4 45000 all # three career strategies, all the way to E L I T E
# inhabitant portraits (offline; images are committed, nothing runs in the browser)
node --experimental-strip-types tools/species-prompts.ts 1 --style crt --json > /tmp/g1.json
# styles: crt, lit, ink, plain — the model does the look
uv run tools/generate-species.py /tmp/g1.json --repo ../ultra-fast-image-gen --only Lave,Diso
# ^ starts that repo's server.py and keeps the model resident
uv run tools/posterise.py --size 256 --tones 6 # re-crush, no GPU neededTwo playtest harnesses back this up. npm run campaign plays hundreds of
full commander careers headlessly — real galaxy, market, living-galaxy and
contract code, with only flight abstracted — and reports whether the economy
actually works (wealth curve, bankruptcy rate, time to first upgrade,
equipment progression, piracy losses), failing the build if it doesn't. It
can also play a commander all the way to E L I T E (25,600 kills) in
about 20 seconds, under three different strategies — trader, hunter,
privateer — which is how the combat ladder below was measured.
The docked combat trainer (T at any station) replaced three console
harnesses. It logs a fight you actually flew — your accuracy and theirs,
damage by source, the geometry that decides whether an NPC can shoot at all,
and how you fly — and exports it as JSON. Scenarios are repeatable from a
seed, so the same fight can be flown against two different brains and
compared.
It exists because every bot-flown measurement in this project turned out to be
shaped by the bot: flying straight flatters one kind of AI, flying the defence
policy flatters another. npm run survivability is still the bot answer to
"can I survive a gang?"; the trainer's waves mode is the human one.
There's also an autonomous playtest agent (test/playtest.js): paste it
into the browser console with the game open and await __playtest.run({ legs: 8 }) sends a commander off to take contracts, trade, fight, jump and
dock on its own, asserting invariants as it goes and printing a report of
everything it exercised. It's how gameplay changes get regression-tested.
CI lints, tests, builds and runs the balance playtest on every push. The
live site deploys from Cloudflare Pages (build npm run build, output
dist) — and since npm runs prebuild before build, a commit that fails
lint or tests fails the deploy build rather than shipping.
Retraining overwrites the committed neural weights in
src/ai-training/brains/that the game imports —git checkout src/ai-training/brainsrestores them.
You start docked at Lave Station with 100.0 Cr, a full tank and 3 missiles.
Saving is a deliberate act and an automatic safety net, and the two can never overwrite each other. S at a station opens the commander file: name a save and it is yours to keep — the same name again replaces it, and it asks first. Alongside it the game keeps its own: a station autosave written every time you dock and every time you launch, and the last three flight autosaves, taken every 20 seconds. Close the tab mid-fight and you resume where you left off; die, and the station autosave puts you back where you launched from. Loading over the run you are in says so before it happens, names what it is about to cost, and can be backed out of.
There is a Space Trader's Flight Training Manual — how to trade, jump, dock and survive, with a first run worked out against the game's own market model — and The Long Way Out, an original novella with papers from the eight galaxies.
In-game, H at the station opens a six-page new pilot's briefing, and ? shows the controls at any time.
Two flight layouts ship. CLASSIC — the authentic 1984 keys — is the default; press B when docked to switch to MODERN (WASD), which is remembered per browser. Press ? any time for the in-game guide, which always shows the active layout.
| CLASSIC (default) | MODERN | Action |
|---|---|---|
| S / X | W / S | dive / climb — pitch (in both: ↓ arrow pulls up) |
, / . |
A / D | roll (arrows work in both) |
| SPACE | SPACE | accelerate |
/ |
X or / |
decelerate |
| A (or F) | F | fire laser (watch the temperature) |
The original's < > roll and / slow-down are live in both layouts, so
muscle memory from 1984 mostly survives either choice. Arrow keys always fly.
Mouse flight: press V in flight to pointer-lock the mouse into a self-centring analogue stick (left button fires). Touching the keyboard overrides it; ESC or V releases. This is the closest thing to the joystick the original supported.
| Key | Action |
|---|---|
| 1 2 3 4 | front / rear / left / right view |
| T / M / U | arm missile (locks in your sights) / fire / unarm |
| E / TAB | E.C.M. / energy bomb (if fitted) |
| J | torus jump drive (8×, stars streak; cuts out when mass-locked) |
| C | docking computer — flies you in; press again or touch the controls to take over |
| K | combat computer — the trained defence AI flies your ship (if fitted) |
| N / G | short range chart / galactic chart |
| H / ⇧H | hyperspace jump / galactic hyperdrive (if fitted) |
| B | distress beacon — GalCop tows you out of witch-space, for your cargo |
| Y / ⇧Y | jettison a tonne of cargo / five at once — pirates came for the goods, not for you |
| I | commander status |
| P | pause |
| V | mouse flight — pointer-locked analogue stick, left button fires |
| ? | controls guide |
Views on 1-4 (the original used F0-F3) and screens on letters (were F4-F9), because browsers claim the function keys.
| Key | Action |
|---|---|
| L | launch |
| M / C / E | market · contracts · equip ship |
| N / G / D / I | local chart · galactic chart · data on system · status |
| T | combat training simulator — practise a fight; nothing in it reaches your commander |
| H | new pilot's briefing |
| B | switch keyboard layout |
| S | commander file — named saves and autosaves (S saves the run · ENTER loads, asking first · D deletes · R changes what you are called) |
| X / Z | export · import a save file |
| Q | start a new commander (confirms first) |
↑↓ and ENTER work on the menu as well as the letter keys.
Free, at every station, on T. Pick a mode (one scored fight · endless
sparring against one hull · escalating waves), a scenario, a threat tier, an
optional seed, and optionally build the opposition yourself — hull, count,
tier, how they fly and fit, per group — plus a fit-out override for your own
ship. ENTER launches; L re-opens the last report; ESC or Q ends the
exercise. The panel is grouped — the fight, who flies what, your ship — with
LIVE BRAINS (COMMANDER) fenced off at the foot, because that one is still set
when you undock. A brain is picked by how it flies — CLOSES IN, HANGS BACK,
HOLDS OFF — with the weights file behind the name for anyone reading the
training log.
Waves escalates twice. First the numbers — one ship becomes six, an opportunist becomes an organised gang — and at wave 11 they stop, deliberately, because a ramp that keeps adding ships makes the score a fact about arithmetic rather than about flying. Then the fight keeps changing: they carry missiles at 12, E.C.M. at 14, one of them is a bounty hunter at 16, and at 18 two of the pirates stand down for a Thargoid and its Thargon — the toughest hull in the game, whose drone goes inert the moment it dies. From 18 on every wave is the same, so surviving three of them is a fact about flying. The cockpit strip carries what is out there, the banner names each new thing as it arrives, and the report quotes both with the reason. The furthest wave you ever reach is kept with the commander and shown on the panel; it is the one number a run leaves behind, and nothing in the career reads it.
A row over a list says where in it you are (5/12) and HOME/END go to
either end without walking there. Selecting a brain row prints what that brain
does in a fight, with the measured number that shows it — the shipped pirate
closes to a median 234 units at speed 216, the TODO 29 candidate hangs at 754
at speed 104 and snipes — so choosing one is a choice rather than a filename.
Every figure comes from train/flight-probe.ts or the evaluation tournament,
both archived under train/logs/.
It is the real game: real flight model, real trained brains, real guns. But
nothing that happens in it leaves it — no kills, no combat rating, no
credits, no legal status, no save write, and death ends the exercise rather
than the career. The single exception is stated as one: the furthest wave a
waves run reached, which is saved with the commander and read by nothing else. Afterwards you get a report (accuracy both ways, damage by
source, engagement ranges, time on each other's six, your own flight
envelope) which exports as JSON to the clipboard or a file, and lands on
window.__simLog for a console session or an agent to read.
←→ walks the recent records and ENTER holds two of them side by side — this, that, and the difference — because the method is an A/B: same seed, same scenario, two brains. It only subtracts them when they really are one fight flown twice. Two records on different seeds, scenarios, modes, waves, hulls or fit-outs are not an A/B, so it names exactly which fields differ and shows no difference column at all; different brains is the point, different anything else is a confound. C and X then take the pair, since the pair is the finding.
↑↓ select · B buy · V sell · ESC exit
Click a system to target it · arrows move the cursor · ENTER set hyperspace target · D data on system (the full statistics page with the original's procedurally generated planet description) · M market estimate · F find a system by name · ESC exit
Fly into the station's docking port with your wings matched to its rotation. An amber marker shows where the port is, with an arrow at the screen edge when it's behind you; it turns green and reads DOCKING PORT — LINED UP when you're on the axis and rolled to match. Get it wrong and you'll bounce off with shield damage — or buy the docking computer.
In a fight, a red arrow at the screen edge points at the nearest hostile you can't currently see.
The console lights an S while the station is in scanner range (its defences cover you there) and an E when an E.C.M. broadcast is detected — as on the original's dashboard.
- Trading — 17 commodities with the original price/quantity model; economies matter (buy food cheap at agriculturals, sell computers dear). 20t hold; precious metals/gems don't take hold space.
- Combat — pulse/beam/military lasers with heat, four mounts, homing missiles you arm and then lock by putting the target in your sights, on-screen target brackets with a lead marker, hull hits that cost you cargo and fittings, bounties, kill ratings from Harmless to E L I T E. Damage is the released game's own arithmetic: what a laser is worth is decoded from its byte, what it costs a target is that target's own defence, and your fore shield, aft shield and energy bank are three 255-point pools — see docs/ELITE-A.md and docs/DAMAGE-PATHS.md. Pirate numbers scale with the government type; shoot police or traders and you become a fugitive (police attack; fine on docking).
- Hyperspace — 7.0 LY fuel range, per-jump fuel cost by real chart distance, 5-second countdown.
- Death — ship destroyed → ENTER takes you back to the station autosave you launched from, with what you left with (unless an escape pod saves you, at the cost of your cargo), or S opens the commander file to pick a save of your own. The in-flight autosaves are dropped: dying costs you the run.
- Legal system — CLEAN → OFFENDER → FUGITIVE; police scan for contraband (slaves, narcotics, firearms), bounty hunters stalk offenders, fines on docking.
- A living system — traders warp in, do business at the station and jump out; pirates hunt them for cargo you can scoop; police hunt the pirates.
- Witch-space — mis-jumps drop you among Thargoids and their Thargon drones. High bounties, if you live. Out of fuel out there? Broadcast a distress beacon and GalCop will tow you clear — they'll take your cargo as the salvage fee.
- Mining & scooping — blast asteroids (mining laser drops ore canisters) and scoop drifting cargo with fuel scoops; sun-skim to refuel, watching the cabin temperature.
- A living galaxy — trade runs between all 256 systems while you play. Convoys depart from productive worlds, get taken by pirates in lawless space, and arrive in your system as real ships. Prices drift with supply, pirate hotspots emerge along dangerous routes, and the system data screen reports the news. The 1984 economy stays the baseline underneath — this layer only ever nudges it ±25%.
- Pirates as businesses — what waits for you on the way in depends on what you're visibly worth. An empty Cobra draws a couple of opportunists in Sidewinders; a full hold draws professionals; a fat, notorious one draws an organised gang flying a coordinated attack policy. A gang isn't five Fer-de-Lances, though — it's two ringleaders plus hangers-on in whatever they could afford, which is why they can be common without being hopeless. Only what a pirate can see counts — cargo, hold size, fitted laser, your reputation — never your bank balance, so banking the money and flying clean is a real strategy. Threat grows far slower than your ship does, so upgrades are felt rather than cancelled out. And since they came for the cargo, jettisoning it (Y) buys them off — proportionally: opportunists want a little, a gang that organised for you wants about a third of the prize. Selling big or dirty loads raises your profile here and in neighbouring systems, and it fades if you lie low. Your reputation cuts both ways: it scares off thieves after easy cargo, but once you're Dangerous roughly a third of the ships waiting for you came for the name rather than the hold. Ratings count difficulty too — a gang's Fer-de-Lance is worth five Sidewinders — so the ladder rewards the fights worth having.
- Contracts — every station runs a bulletin board with cargo runs, courier jobs and pirate-clearing bounties, available from your very first landing. Deadlines are measured in days, which pass as you jump. (The original made you earn your first mission with 16 kills; a new commander deserves somewhere to be.)
- Navy missions — prove yourself (16+ kills, galaxy 1) for the Constrictor hunt and the classified courier run.
- Don't shoot the station. Its hull shrugs off a laser, but GalCop notices: you're marked an offender and the station scrambles Vipers from the slot. Shooting those is how you become a fugitive.
- Encounters — destroyed ships eject escape capsules (scoop one and the survivor becomes, regrettably, cargo); stations scramble Vipers if you misbehave in their sight; rock hermits hide among the asteroids, dealing ore and asking no questions; derelict generation ships drift between the stars; and someone will sell you a Trumble for 2 credits, which is one of the worst decisions available to you.
- Trained ship AI — pirates and armed traders fly neural policies trained by self-play (docs/TRAINING-LOG.md); watch them fight in the combat viewer.
src/galaxy/— the genuine Elite galaxy algorithm: three twisted 16-bit seed words generate all 256 systems per galaxy (names, economy, government, tech level, market). Galaxy 1 is byte-identical to the original — system 7 is Lave.src/ships/— all 38 released hulls as explicit vertex/edge/face tables in the style of the original BBC data, generated from the vendored reference pack and exact to the source rather than drawn by eye. Thirty-one of them turn up in play (Cobra Mk III and Mk I, Sidewinder, Viper, Adder, Krait, Mamba, Fer-de-Lance, Python, Anaconda, Boa, Gecko, Moray, Worm, Shuttle and Shuttle Mk II, Transporter, Dragon, Monitor, Ophidian, Ghavial, Bushmaster, Rattler, Iguana, Chameleon, Thargoid, Thargon, Constrictor, missile, canister, escape capsule); the Coriolis and Dodo stations are the same tables at a larger presentation scale. All drawn as wireframe edges over a black occluding hull (classic hidden-line look), and all 38 browsable at/gallery.src/world/— shader sun (animated fbm surface, limb darkening, corona), shader planet (coastline contours, graticule, terminator, atmosphere rim — seeded per system), starfield, space dust, per-system scene assembly.src/game/— game orchestrator (modes, docking, hyperspace, combat), NPC AI (traders/pirates/police), commander state + saves.src/hud/,src/ui/— scanner/compass/gauges console and the full-page screens (station menu, market, chart, status).src/audio.ts— WebAudio synth in the spirit of the BBC sound chip, including the docking waltz. The Commodore 64 Elite played An der schönen blauen Donau while you docked; the tune is Strauss, 1866, and comfortably public domain, so it is synthesised here from note data rather than shipping audio from the original game — this repo contains no assets from Elite.src/ai-training/+train/— render-free combat simulator, tiny MLP policies (1.9k params, keyboard-style discrete actions) and a neuroevolution self-play trainer.src/ai-training/brains/holds exactly the three policies the game flies — a solo pirate, an organised gang and an armed trader — and the tests fail if a fourth appears; everything else this project trained is written up indocs/TRAINING-LOG.mdrather than shipped. The combat viewer (/viewer) replays matchups with the real wireframe ships, and every row in it flies one of those three or a stated control. Seedocs/AI-TRAINING.mdanddocs/TRAINING-LOG.md.- Rendering: three.js + UnrealBloom for the phosphor glow.
This is a non-commercial fan homage, released under the MIT license (see LICENSE). Elite (1984) was created by Ian Bell and David Braben and published by Acornsoft; the "Elite" trademark belongs to Frontier Developments plc. This project is affiliated with none of them. The galaxy generator and the ship tables both follow long-published descriptions of the original data — the hulls were hand-made approximations until 2026-08, and are now generated from a vendored analysis pack of the released ship files (docs/ELITE-A.md). This repo contains no assets from Elite.
docs/GAP-ANALYSIS.md tracks feature-by-feature parity with the original manual, and almost all of it is implemented — including the things this section used to list as outstanding: side laser mounts, mouse flight, the two-level living galaxy, the purchasable combat computer, and pack training (now at round 11).
Remaining: gamepad support, a player shipyard (all 15 flyable hulls are imported and resolve; nothing can yet change which one you fly — see the deferred list in docs/ELITE-A.md), and a decision on whether the coordinated pack brain should be every gang's default.
Combat balance is not settled, and this file quotes no figure for it on
purpose: the damage model moved to the released game's own arithmetic in
2026-08, so every number measured before that describes a different game.
Measure it, don't cite it — npm run campaign, npm run survivability, and
the docked combat trainer's JSON export.
One finding worth reading before touching combat AI, from docs/TRAINING-LOG.md: in a recorded human fight, pirates lined up on the player only a small fraction of the time and landed most of the shots they did take, so the balance rests on pursuit being imperfect — "better" pursuit is not automatically better. That is a shape, and it survives the change of units; the percentages in the log entry do not.



