victor/boeing-747-opus-5
Boeing 747-400 — three.js
A parametric, procedurally generated Boeing 747-400. No imported meshes, no downloaded textures: every surface is lofted from cross-section tables and every texture is drawn into a canvas at load time. Drag to orbit, scroll to zoom.
The interesting part is not the aircraft, it is that the model was built against a self-verifying rig rather than by eye.
Numeric verification
npm run measure renders the airframe as a binary silhouette through an orthographic camera with an exact pixel-to-metre mapping, extracts the outline from the framebuffer, and derives real measurements from it. Each is compared against published 747-400 geometry with a stated tolerance.
metric target measured delta tol status
----------------------------------------------------------------
length 70.66m 70.69 +0.03 ±0.35 PASS
span 64.44m 64.53 +0.09 ±0.5 PASS
height 19.41m 19.41 0 ±0.35 PASS
fuselageWidth 6.5m 6.67 +0.17 ±0.2 PASS
fuselageMaxHeight 8.1m 8.07 -0.03 ±0.25 PASS
stabSpan 22.17m 22.19 +0.02 ±0.6 PASS
wingRootLE 27.4m 27.43 +0.03 ±1.2 PASS
wingTipTE 57.4m 56.88 -0.52 ±1.5 PASS
finTipStation 66.5m 66.9 +0.4 ±2 PASS
noseGearStation 18.6m 18.6 0 ±1.2 PASS
wheelbase 25.6m 25.57 -0.03 ±1.2 PASS
mainTrack 12.4m 11.85 -0.55 ±1 PASS
wingSweepLE 40° 39.51 -0.49 ±2.5 PASS
dihedral 7° 7.42 +0.42 ±1.5 PASS
----------------------------------------------------------------
14/14 within toleranceVisual verification
npm run shoot drives headless Chromium through 25 named camera views: six long-lens survey angles, four hero three-quarters, and fifteen close details covering the nose, cockpit, both engines, tail, all three gear positions, wing root, winglet, upper deck, belly and flap-track canoes. Views are defined by the region size in metres they should frame, not by a camera distance, so shots stay identically composed across iterations and can be diffed directly.
Two switches make defects findable rather than merely visible:
--mode clayreplaces every material with neutral grey, so silhouette and surface flow can be judged apart from paint.--isolate <regex>hides everything whose object path does not match, so one subassembly can be inspected without the rest of the aircraft occluding it.
Several real bugs were only findable this way: pylons that measured correctly but looked detached, a bypass duct breaking through the re-tapered fan cowl as a ragged dark band, flap track fairings grazing the razor-thin trailing edge into z-fighting, and a mis-written cross product in the surface-normal helper that silently mis-oriented every surface-mounted detail.
Geometry
Axes: +X forward, +Y up, +Z starboard. "Station" is distance aft of the nose tip, so x = NOSE_X - s.
- `lib/loft.js` is the core primitive. It lofts a ring sequence into an indexed mesh with analytic normals from central differences across the grid, which keeps the surface smooth across the UV seam that
computeVertexNormals()would crease. - `aircraft/profile.js` models the fuselage as star-shaped polar sections: a body ellipse smoothly unioned (polynomial smooth-max) with the upper-deck lobe and the wing-body fairing. That smooth-max is what produces the 747's rounded shoulder where the hump meets the tube instead of a hard crease. Getting it right meant using the real numbers: the fuselage is 6.50 m wide and 8.10 m tall, so the hump crown stands 1.60 m proud, not the 2 m a first guess suggests.
- `lib/surface.js` lofts lifting surfaces from streamwise aft-loaded sections. Each station carries a cant angle, which is what lets the wing blend continuously into a near-vertical winglet through a single loft.
- `aircraft/engine.js` builds the nacelle from one continuous profile that starts at the fan face inside the inlet, runs forward, wraps the highlight lip and continues aft down the outside. Lofting it in one pass gets the inner duct's inward-facing normals for free.
Materials
Aircraft paint is a two-layer system in reality, a pigmented basecoat under a polyurethane clearcoat, so it is modelled with MeshPhysicalMaterial and a clearcoat lobe. The livery is generated procedurally: because the fuselage UV map is (station, arc length up from the belly), paint is authored in (station, height) space and converted per column, which keeps the cheatline on a true constant-height line all the way around the nose and tail.
Cabin windows are instanced geometry rather than texture. At 100+ per side they are what the eye uses to read fuselage scale, and painted ones go soft at any usable resolution. Cockpit windows are patches sampled from the section geometry itself, since a flat plate tangent to the hard-curving nose lifts off at its corners and pokes through the silhouette.
The visible sky is an analytic gradient dome; a separate half-float equirectangular texture feeds PMREM for image-based lighting. Driving the background from that same data texture produced a flat neutral grey, a float-texture sampler mismatch, and the dome is better regardless: no resolution limit, no banding, and the horizon tunes independently of the lighting.
Running it
The deployed page is the built bundle. To work on it:
npm install
npm run dev # interactive viewer
npm run measure # numeric check against published geometry
npm run shoot # 25-angle render sweep and contact sheets~192k triangles, 205 kB gzipped.
