Main libraries: three.js, camera-controls, zustand, Comlink, Dexie, flatbuffers,
meshoptimizer, react-arborist (trees) and @xyflow/react (node editor). Styling uses
Tailwind 4. Plugins live in npm workspaces under packages/plugins/*, and
npm run gen:plugins generates registry.generated.ts.
sequenceDiagram
autonumber
participant U as User
participant H as scene handler
participant A as REST API
participant S as Object storage
participant L as setupModelLoader
participant C as model cache worker (IndexedDB)
participant V as three.js scene
U->>H: open source (tree / asset browser / upload)
H->>A: request derived GLB (convert job if missing)
A-->>H: presigned URL (after the job, if one was needed)
H->>S: GET GLB
H->>L: GLB bytes
L->>L: parse glTF + ADA_EXT_data
L->>C: cache hierarchy · draw ranges · face ranges
L->>V: batched meshes + picking ranges
ADA_EXT_data provides the object hierarchy and per-object draw ranges, so one merged mesh
can still be picked, hidden and coloured object by object (utils/mesh_select).
sequenceDiagram
autonumber
participant H as load_fea_streaming.ts
participant A as REST /fea/*
participant S as Object storage
participant F as fea/streaming/*
H->>A: fea/manifest (poll until the bake job is done)
A-->>H: fea.manifest.json
H->>S: fea.mesh.glb (+ edges, elements, beam solids)
H->>F: build session: mesh + manifest stores
loop on step / field change
F->>S: range GET of one step from fea.FIELD.bin
F->>F: paintNodeField / paintElemField (fixed colour range from the manifest)
F->>F: warp (deformed shape) · beamSolids worker
end
Because each field blob is step-major with a fixed header, one step is one HTTP range
request. Colour ranges come pre-computed from the manifest, so the legend stays fixed while
scrubbing through steps.
A transient result (a time history, for example an OpenCourant .radanim) opens on its last
frame. The step slider becomes a timeline, and Play steps through it at true time scale
(utils/scene/fea/timeHistory.ts). Step loads run one at a time, and the latest request wins.
Any result can be exported as a video (utils/scene/fea/animationExport/): MP4 through
WebCodecs and mediabunny, or GIF through gifenc. You choose resolution, aspect, frame rate, and
whether to include the legend and orientation gizmo. The encoders load only when used.
Without a worker (or for local files), pyodide_converter.ts lazily starts
pyodide_worker.js: Pyodide with ifcopenshell (wasm) and trimesh for IFC → GLB, and the
adacpp wasm wheel for STEP → GLB. The worker is recycled above ~1.4 GB of heap. The browser
can also run the FEA bake and upload the artefacts through the REST API.