← HANGAR 12 THE GUIDE · HOW IT WAS BUILT
Design showcase · build notes · DWG H12-A4

An aircraft made of lines.

Hangar 12 sells twelve-minute flights over city traffic, so the site has to feel like an engineering document you can touch: one HTML file, one stylesheet, one script, and a wireframe eVTOL you can grab and rotate.

The idea

Urban air mobility asks people to board a new kind of aircraft, so the site's whole job is to make the machine feel inspectable. Instead of glossy renders, the hero is the engineering drawing itself — a procedurally-built wireframe H12-A4 on a carbon background, rotating slowly over a vertiport pad while a scan line sweeps it and spec callouts (RANGE 120 KM · CRUISE 240 KM/H · NOISE 62 dB(A) · PAX 4) track the actual parts. Everything else follows booking patterns proven in shipped products: a route-search card, vertical-timeline trip cards, a departures board with flight-status pills, and a reserve button that carries the concrete promise ("Reserve seat 2A — Fri 07:10 · $95"), the way Uber Reserve does.

The stack

Signature technique — the wireframe H12-A4

There is no model file. The airframe is generated from a station table — the same numbers that draw the SVG blueprint in the craft section. The fuselage is lofted the way real airframes are drawn: cross-section rings at each station, connected by eight longerons:

// [station x, radius] — nose to tail
const ST = [[1.95,.02],[1.8,.16],[1.55,.32],[1.2,.46],[.8,.54],[.3,.56],
            [-.2,.52],[-.7,.44],[-1.15,.32],[-1.55,.2],[-1.9,.1],[-2.15,.04]];

// one elliptical ring per station…
ST.forEach(([x, r]) => poly(ringPts(x, r, r * 0.82, 24), true, segTi));

// …and 8 longerons threading every ring
for (let k = 0; k < 8; k++) {
  const a = k / 8 * Math.PI * 2;
  poly(ST.map(([x, r]) => [x, Math.cos(a)*r, Math.sin(a)*r*0.82]), false, segTi);
}

Wing, booms, V-tail, skids and the vertiport pad are all built the same way and merged into a handful of LineSegments draw calls — the whole craft renders in about six. The four rotor-blade sets live in their own groups so they can spin (adjacent rotors counter-rotate, slightly different speeds so the motion never phase-locks). The camera is orthographic — no perspective, like a drawing, not a product shot.

The scan is a glow-colored ellipse of line segments sweeping the hull vertically, its opacity following a half-sine so it fades at the extremes:

const phase = (t * 0.16) % 1;
scan.position.y = -1.05 + phase * 2.1;
matScan.opacity = Math.sin(phase * Math.PI) * 0.45 + 0.04;

The spec callouts are real HTML, not textures. Four empty Object3D anchors sit on the wingtip, nose, rotor hub and cabin; every frame each anchor is projected to screen space and an SVG leader line is redrawn from the part to its label:

anchor.getWorldPosition(v3).project(camera);
const ax = ( v3.x * 0.5 + 0.5) * w;
const ay = (-v3.y * 0.5 + 0.5) * h;
leader.setAttribute('points', `${ax},${ay} ${elbowX},${labelY} ${edgeX},${labelY}`);

So the labels stay crisp, selectable and accessible while their leader lines chase the rotating airframe — the drawing annotates itself.

Details that matter

Ship it on GitHub Pages

Every path is relative and there is no build, so deploying is three commands:

gh repo create hangar-12 --public --source . --push
gh api --method POST /repos/USER/hangar-12/pages \
  -f 'source[branch]=main' -f 'source[path]=/'
# live at https://USER.github.io/hangar-12/ in ~1 min

A .nojekyll file keeps Pages from touching the folder. That's the whole deploy.


HANGAR 12 IS A DESIGN-SHOWCASE CONCEPT — THERE IS NO FLEET, NO BOOKING BACKEND AND NO TYPE CERTIFICATE. SEE THE REPOSITORY README FOR THE FULL DEMO-VS-REAL MAP.

← BACK TO HANGAR 12