Skip to content

RFC-013 — ISS Explorer · 3D model pipeline & module pickability

Orrery · Closed RFC · v0.1 · May 2026

Status: Closed (accepted 2026-05-05 — ADR-040, ADR-041, ADR-042) Author: product Closes into: ADR-040 (model source + asset pipeline), ADR-041 (module-pick strategy + LOD), ADR-042 (low-end-device fallback) Slice gate: v0.4 Why this is an RFC: PRD-010 promises a rotatable 3D ISS where every module is clickable, panelled, and historically attributed. The technical questions cluster around the model (where do we get a 3D ISS that's accurate, cleanly licensed, and small enough to ship?), picking (mesh-per-module is conceptually clean but explodes the tris budget — what's the right granularity?), and fallback (we promised a low-end-device path; is that a list view, a 2D diagram, or a simplified 3D model?).


Context

The ISS is the largest, most complex, longest-occupied human spacecraft ever flown. Existing 3D model resources:

  • NASA-3D Resources — multiple official ISS GLTF / OBJ models, NASA Open Source Agreement (close to public-domain).
  • NASA Eyes on the Solar System — high-fidelity model used in NASA's WebGL apps; not directly redistributable.
  • Wikimedia Commons / Sketchfab community — many community models, mixed quality and licences (CC-BY most common).
  • Custom Blender authoring — ~3 weeks to author a low-poly station from reference photos.

PRD-010 wants:

  • 16 pressurised modules individually clickable
  • < 4 MB compressed initial load
  • Renders at ≥ 30 fps on 2020-baseline mobile
  • Visually faithful to the reference photo (the audience knows what the ISS looks like)
  • Dark-theme native (matte white module skins, gold MLI, blue solar panels)

These are achievable but not trivially. The tris budget alone is the first hard constraint.


Open Questions

OQ-1 — Where does the model come from?

OptionSourceLicenceTris budgetAuthoring cost
A. NASA-3D Resources GLB, simplifiedNASA repo, Blender decimation passNASA Open Source Agreement (very permissive)Original ~500k tris → simplified to ~50k~3 days simplification + cleanup
B. Custom Blender model from scratchHand-authoredProject-owned (cleanest)Target ~30k tris~3 weeks
C. Sketchfab / community CC-BYCommunity model with commercial redistribution licenceCC-BY (with attribution)Variable~1 day import + cleanup
D. Procedural — generate cylinders + boxes for modules from specCode-driven from iss-modules.jsonProject-owned~5k tris~2 days authoring + spec

Recommendation: A. NASA-3D Resources is the cleanest pedigree for an accurate model; the licence is permissive; the simplification pass is well-trodden Blender work. Option D is tempting (procedural would be an instant fallback for OQ-3) but produces a "block diagram" rather than the recognisable station — fails the "audience knows what the ISS looks like" test.

The simplified GLB ships at static/models/iss/iss-low.glb; original-source attribution recorded in static/models/iss/CREDITS.md per PA §principles.


OQ-2 — Module pickability: mesh-per-module or pick atlas?

PRD-010 wants 16 modules individually clickable. There are two architectural options.

OptionHow it worksTris costPer-module operationsMemory
A. Mesh-per-moduleEach module is a separate THREE.Mesh in a Group; Raycaster intersects against the module list+0 (same total geometry, just split)Trivial (mesh.userData.moduleId)One draw call per module = 16 draw calls vs ~1
B. Single mesh + UV pick atlasOne unified mesh; a colour-coded atlas texture marks each module's UV island; pick by reading the picked pixel's colour-0 (single mesh, single draw call)Read pixel from offscreen render; map colour → moduleIdOne draw call but offscreen-render-on-pick adds latency
C. Mesh-per-module + truss as one merged meshHybrid: pressurised modules individual; trusses + arrays merged+0Same as A~17 draw calls (16 modules + 1 truss complex)

Recommendation: C. Mesh-per-pressurised-module gives the click target. Trusses + solar arrays are visual scenery in V1 (PRD-010 explicitly defers per-truss pickability) and can be a single merged mesh. Total ~17 draw calls is well within budget.


OQ-3 — Low-end-device fallback (PRD-010 §scope §technical)

PRD-010 promises ?view=list falls back to an alphabetical list with the same panel structure. Implementation question: how do we detect a low-end device, and what does "list mode" look like exactly?

OptionDetectionList UX
A. URL-only (?view=list)User opt-in or default for known-bad UAAlphabetical list of cards; tap → panel
B. URL + heuristic auto-fallbackIf WebGL not supported OR navigator.deviceMemory < 4 OR initial render < 20 fps → switch to list automaticallySame list UX
C. URL + a small "switch to list view" toastRender an "if 3D is slow, switch to list" banner the first time the user lands; dismissed via toastSame list UX

Recommendation: A + B combined. URL parameter is the explicit override. Heuristic auto-fallback (no WebGL, low memory, or sustained sub-20 fps after 2 seconds) flips to list view automatically. C is a polish-pass that we can layer on later.

ADR-042 records the detection rules so they're testable.


OQ-4 — Earth backdrop in V1?

PRD-010 V1 commits to a black-space backdrop with Earth deferred to V1.5. Worth re-checking: a faint Earth limb behind the station is visually striking and reinforces "low-Earth orbit." Cost is small (sphere + texture, no animation).

OptionCostRisk
A. Black space backdrop only (per PRD)0Visual feels disembodied
B. Static Earth limb at the bottom of the viewport+1 dayAdds 1 sphere + a 2K texture (~1.5 MB compressed); some performance hit on low-end
C. Animated Earth (slow rotation matching real ISS orbital period scaled down)+2 daysDistracts from the model

Recommendation: B for V1. A static Earth limb is a 90% visual win for a 10% cost. Ship it with the existing 2k_earth_daymap.jpg asset (same as /earth / /explore). Place it permanently below the station's centre of mass; the user always sees the station "above" Earth as in canonical photos.


OQ-5 — Truss segments pickable in V1?

PRD-010 V1 commits to pressurised modules pickable, trusses non-pickable. The truss has its own engineering history (P0–P6, S0–S6, Canadarm2). User research note: most casual users don't ask about specific truss segments by name; they ask about Canadarm.

OptionV1 scope
A. Trusses non-pickable (PRD V1)Pressurised modules only
B. Canadarm2 separately pickable, trusses still grouped+1 module to clickable list
C. Per-segment trusses+12 click targets — overkill for V1

Recommendation: B. Canadarm2 is iconic enough to deserve its own panel (history: built by MDA / CSA in 2001, the only Canadian-engineered piece on the station, used for every visiting-vehicle capture). Per-segment trusses defer to V2.


OQ-6 — LOD (level of detail) strategy

A 50k-tri model at 60 fps on a 2020 phone is achievable but tight. LOD = swap to a lower-poly version when zoomed out.

OptionCostBenefit
A. Single LOD (one model at all distances)0Simplest
B. Two-level LOD — full at near, ~10k-tri version at far+1 day authoring + Three.js LOD plumbingSmoother low-end performance
C. Three-level LOD+2 daysMarginal gain over B

Recommendation: A for V1, B for V1.1 if perf metrics fall short. Three.js's built-in LOD is straightforward to add later. Don't optimise prematurely.


OQ-7 — Where does ISS sit in the nav?

Current nav (post v0.4 + new pages from PRD-009): EXPLORE → MISSIONS → PLAN → FLY → EARTH → MOON → MARS.

Adding ISS:

  • After EARTH (preserves "LEO context" — Earth then ISS in LEO)
  • Before EARTH (treat ISS as a thing in space, distinct from "the Earth screen")
  • After MARS (keeps the planetary cluster intact, ISS as the tail)

Recommendation: After EARTH. Final order: EXPLORE → MISSIONS → PLAN → FLY → EARTH → ISS → MOON → MARS. ISS is in LEO; placing it next to /earth groups them as the "near-Earth" cluster. The simulator screens (PLAN/FLY) live before, the destination catalogue (MOON/MARS) lives after.

ADR-040 to lock the nav order alongside the asset-pipeline decision.


Decision criteria

We accept the resolution when:

  1. The 3D model loads in < 4 MB compressed (gzipped GLB).
  2. Initial render reaches ≥ 30 fps on a baseline 2020 mobile device (Pixel 4 / iPhone 11 / Galaxy A50 class).
  3. All 16 pressurised modules + Canadarm2 are individually pickable with no false-positives on the truss complex.
  4. ?view=list fallback path renders all panel content without WebGL.
  5. Heuristic auto-fallback flips to list view without flicker on detected low-end devices.
  6. Earth backdrop (if shipped) doesn't crash low-end devices.
  7. Model + texture credits are recorded in CREDITS.md per PA §principles.

Closure path

  • ADR-040 — Asset pipeline: diagrammatic proxy + optional future NASA GLB swap-in. Earth backdrop using existing 2k_earth_daymap.jpg. Nav slot after EARTH.
  • ADR-041 — Module pickability: mesh-per-pressurised-module + Canadarm2 + merged-truss. ~17 draw calls.
  • ADR-042 — Low-end-device fallback: ?view=list URL override + heuristic auto-fallback (no WebGL, low memory, sustained sub-20 fps).

Multi-LOD (OQ-6 option B), per-truss pickability (OQ-5 option C), animated Earth (OQ-4 option C) all deferred to V1.5 / V2.


Orrery · RFC-013 · v0.1 · Closed.

Orrery — architecture documentation · MIT · No tracking