/studio3d) is a real-time 3D rendering of a run’s operational state — the same
governed data the Business Hub’s dashboard pages render, staged as an office building: rooms for
Governance, the Evidence Vault, the Skills Library, Orchestrator HQ, and desk pods for builder and
validator agents, with an orchestrator “manager” model that walks the floor in response to real
observed events. It exists to make an unattended, long-running governed pipeline legible at a
glance — pending approvals read as a red desk light and a closed gate, not a row in a table. The
semantic (2D) DOM is built first and remains the canonical interaction tree; the 3D scene is a
layer on top that can fail or be turned off without losing any information.
This is a separate page/document from the dashboard’s 2D “Studio” tab (
ui/pages/studio.js, served
inside the main Hub SPA). /studio3d is its own standalone HTML document (ui/studio3d.js) with
its own bundle of client modules under ui/studio3d/.How it works
Document and module loading.studio3dHtml() in ui/studio3d.js writes an inline
<script type="importmap"> before any module loads, because the WebGL and WebGPU builds of
three cannot be mixed in one module graph:
?gpu=offalways pins the classic WebGL build.?gpu=forceexercises the WebGPU-node build’s WebGL2 backend fallback path, for verification on any browser.- Otherwise the choice follows
navigator.gpu— browsers without WebGPU get the classic build.
scene.js self-falls-back: if the adapter is refused, WebGPURenderer
runs on its WebGL2 backend rather than failing. Diagnostics expose which path is actually active via
gpuTier: 'webgl' (classic build), 'webgpu' (real WebGPU backend), or 'webgpu-node:webgl2'
(node build, WebGL2 fallback engaged).
Composition root (studio3d/app.js). createStudioDom() builds the semantic DOM first; a
WebSocket-driven transport (createStudioTransport) feeds it validated snapshots
(validateStudioSnapshot). Only if the browser supports WebGL2 (supportsWebGL2(), an actual
canvas.getContext('webgl2', ...) probe) does ensureScene() dynamically import('./scene.js')
and build the 3D scene; any failure — no WebGL2, an import error, or a runtime
onRendererState('semantic-fallback') callback — falls back to data-renderer="semantic", hiding
the canvas and showing the #studio-fallback banner with the semantic mirror still fully live. A
Semantic view toggle button lets a user force data-renderer="semantic-only" regardless of 3D
support.
Theme. currentTheme is 'twin' (Digital Twin, the default) or 'classic', persisted in
localStorage under rstack.studio.theme. Toggling rebuilds the scene from scratch
(scene.destroy() + re-ensureScene()) rather than live-restoring materials — “cheap and rare”
per the code comment, and the only path that can’t drift.
Rooms and layout (studio3d/topology.js). STUDIO_TOPOLOGY is a single frozen object
describing a rectangular cutaway floor: a north wing (Skills Library, Orchestrator HQ, Governance,
Evidence Vault), a central east-west corridor, and a south wing (Builder Bullpen, glass Validator
Lab, dispatch/reception). It owns positions, door coordinates (DOORS.library, .hq,
.governance, .vault, .bullpen, .lab), and deterministic corridor routes (corridorRoute(),
routePoints()) — purely spatial data with no runtime state, so it can’t drift from the server’s
projection. pipelineStageX() maps a stage index onto the compact department spine; department,
mission-board, builder-desk, and validator-desk slots are generated with row()/slot() helpers.
Click-a-room. scene.js’s enterRoom(ref) resolves an authored anchor + doorway per room id
(roomDoor() recognizes library, hq, governance, evidence, builder, validator,
dispatch), drops the camera just inside the doorway looking in, and lights an interior point
light. Pointer clicks are raycast (onPointerUp) against the scene: an agent/entity hit (any mesh
carrying userData.entityRef/entityRefs) takes priority over the room it stands in; otherwise a
hit on a room-tagged mesh or an ancestor group’s userData.roomRef resolves via roomRefFromHit()
and calls selectRoom().
Cinema camera director. directorShot() (called every DIRECTOR_HOLD_MS = 9000ms while
director.mode === 'cinema', the default mode) picks a shot from the same live projection the
semantic DOM renders, in priority order:
- If
projection.approval_summary.pending_countis truthy, frame the Governance room. - Else round-robin through sessions with
status'active'or'starting'that have a resolved workstation, holding each for one cycle (director.tourIndex). - Else fall back to the authored overview camera (
STUDIO_TOPOLOGY.overviewCamera/overviewTarget).
enterRoom(), or the Overview button exits cinema to
'explore' mode instantly (setDirectorMode). A Follow mode also exists: the camera preserves
its current offset and glides after one entity’s live position every frame
(updateFollowCamera()), stepping instead of gliding under reduced motion.
Agents as robots. Agent sessions render via reconciler.js/robot.js/locomotion.js as
walking figures that move along corridorRoute()-generated paths between desks (builder pods vs.
the validator lab, chosen by session.role in workstationSlot()), synthesizing stride animation
procedurally rather than depending on baked walk clips.
Reduced motion / diagnostics. app.js reads prefers-reduced-motion and a persisted
rstack.studio.motion override, applied via scene.setMotion(mode). onDiagnostics reports
qualityTier, gpuTier, draw calls, triangle count, active rigs/transitions/captions, and manager
position/action back onto app.dataset.* attributes for external inspection.
Try it
Open the Business Hub (npm run business) and navigate to /studio3d from a run scope, or link
directly:
- Click any room (Governance, Evidence Vault, Skills Library, Orchestrator HQ, Builder Bullpen, Validator Lab, Dispatch) to focus the camera and open its inspector panel.
- Use Take control / Cinema mode to toggle between the hands-free director and manual orbit controls; Overview returns to the authored wide shot.
- Classic look / Studio look switches the Digital Twin ↔ classic palette (rebuilds the scene).
- Semantic view forces the 2D DOM-only mirror, useful for accessibility review or when the 3D view can’t run.
The 3D module list (
office.js, robot.js, animator.js, captions.js, transitions.js,
behavior.js, geometry.js, assets.js, model.js, transport.js) was skimmed rather than
fully read for this page; room identities, the click-a-room mechanism, the cinema director’s shot
priority, and the WebGPU/?gpu= fallback behavior above are drawn directly from app.js,
topology.js, and the read sections of scene.js. Finer detail on robot rigging, captions, and the
office mesh-building code is not covered here.