---
path: /studio/isis/3d-generation/turntable-viewer
surface: studio
domain: isis
auth:
  signed-in + studio entitlement (parent segment `3d-generation` is AAA-gated;
  nested route inherits — verify per-segment behavior in `middleware.ts`)
source: apps/oshun/web/src/app/studio/isis/3d-generation/turntable-viewer/page.tsx
status: walked
last_walked:
  '2026-05-29 automated runtime walk (Playwright headless) — render, /v1 data
  (2xx), console/page-errors, expected content, screenshot verified; live
  screen-reader, touch, offline, and telemetry-delivery checks pending a manual
  AT pass. Evidence: WALKTHROUGH/results/runtime-sweep-2026-05-29.md; body
  re-derived 2026-06-03 from current source (lane-console architecture)'
---

# Isis Turntable Viewer

## Purpose

Hybrid surface: the load-bearing, real-backed element is the **Orbit Frame
Planner** lane, which calls the Isis turntable orbit frame-plan generator to step
azimuth evenly across 360°, place the camera on the orbit circle per frame, and
report duration, azimuth step, and per-frame x/y/z positions via
`/v1/admin/isis/turntable-viewer`. The page also retains a legacy in-browser 3D
inspection shell (`BrowserMeshTurntableViewer`) driven entirely from local demo
scenes — orbit/pan/zoom, progressive mesh streaming, PBR channel isolation,
UV/wireframe overlays, variant comparison, and AR Quick Look — with no BFF call.

## Entry points

- Breadcrumb in `/studio/isis/3d-generation`: `Studio` → `Isis 3D Generation` →
  `Turntable Viewer`
- Sibling quick-actions: `/studio/isis/3d-generation`,
  `/studio/isis/3d-post-processing`
- Direct URL / bookmark (AAA entitlement applies via parent gate)

## Layout regions

`page.tsx` mounts `<ShellLayout active="studio">` and four panels: a breadcrumb
panel, the `StudioIsisTurntableViewerWorkspace`, the Route Map, and sibling
quick-actions.

- **Header**: shell header
- **Breadcrumb panel** (`data-isis-turntable-viewer-breadcrumbs`): Studio → Isis
  3D Generation → Turntable Viewer (`aria-current="page"`)
- **Workspace panel** (`data-isis-turntable-viewer-workspace`):
  - **Orbit Frame Planner lane** (`data-tt-lane`, `<h2 data-tt-heading>Orbit
    Frame Planner</h2>`) — the wired lane, rendered first
  - Eyebrow "Viewer Surface", `<h1>` (`WorkspaceHeading`) "Isis Turntable
    Viewer", summary paragraph, and an `Auto turntable: {autoState}` pill
    (`running` | `paused` | `off`)
  - Legacy capability summary grid (`data-isis-turntable-capability-summary`,
    `data-isis-turntable-capability=<id>`), stage shell
    (`data-isis-turntable-stage-shell` / `data-isis-turntable-stage`,
    `aria-label="Interactive 3d turntable viewport"`) with ~40 state
    data-attributes, a control rail, and section panels — all local/demo-driven
- **Route Map panel** (`data-isis-turntable-viewer-route-map`): `<h2>Route
  Map</h2>` over `STUDIO_ISIS_TURNTABLE_VIEWER_ROUTE_MAP` (4 entries)
- **Sibling quick-actions panel**: `/studio/isis/3d-generation`,
  `/studio/isis/3d-post-processing`, `/studio`

## States

### Orbit Frame Planner lane (wired)

- [ ] **Loading** — `outcome === null`; `data-tt-loading` "Loading turntable
      planner…"
- [ ] **Unauthorized** — 401/403 admin-scope fail-closed; `data-tt-unauthorized`
      ("Isis workspace scope required to plan the turntable.")
- [ ] **Error** — non-OK catalog response; `data-tt-error`
- [ ] **Ready (form)** — `outcome.status === 'ready'`; the JSON payload form
      renders
- [ ] **Result** — `data-tt-result` with `data-tt-headline` (frames · duration ·
      azimuth step) and `data-tt-frames` table; plan failure →
      `data-tt-plan-error`

### Legacy viewer shell (local/demo only)

- [ ] **Auto turntable running / paused / off** — `data-isis-turntable-auto-state`
- [ ] **Single / overlay / side-by-side comparison**,
      **progressive streaming**, **AR supported / fallback** — all driven from
      `BrowserMeshTurntableViewer` state, not the BFF
- [ ] **Gated** — inherited AAA gate via parent route

## Interactions

### Orbit Frame Planner lane (`data-tt-form`)

- [ ] **Orbit JSON payload** (`data-tt-payload`, textarea, `aria-label="turntable
      orbit json"`; defaults `{ frameCount, fps, radius, elevationDeg,
      startAzimuthDeg }`)
- [ ] **Plan orbit** (`data-tt-submit`, submit) — `POST
      /v1/admin/isis/turntable-viewer/plan`; renders summary + per-frame table
      (`data-tt-frame-row`, `data-tt-frame-azimuth`, `data-tt-frame-x`,
      `data-tt-frame-z`, `data-tt-step`)

### Breadcrumb

- [ ] **Studio** → `/studio`
- [ ] **Isis 3D Generation** → `/studio/isis/3d-generation`
- [ ] **Turntable Viewer** (`aria-current="page"`)

### Legacy viewer shell

- [ ] Orbit/pan drag + wheel zoom; comparison-mode, PBR, pointer-mode, overlay,
      auto-turntable, restart-streaming, fit-asset controls; view presets
      (`data-isis-turntable-preset`); hero-asset chips
      (`data-isis-turntable-hero-asset`) — all in-memory, backed by local demo
      scenes (`DEMO_SCENE` / `CANDIDATE_SCENE`)

### Sibling quick-actions

- [ ] **Open Isis 3D Generation workspace** → `/studio/isis/3d-generation`
- [ ] **Open Isis 3D Post Processing workspace** →
      `/studio/isis/3d-post-processing`
- [ ] **Back to Studio workspace index** → `/studio`

## Data & contracts

- **Reads**: `GET /v1/admin/isis/turntable-viewer` (lane catalog probe; gates the
  form). The legacy viewer reads only local demo scenes.
- **Writes**: `POST /v1/admin/isis/turntable-viewer/plan` (orbit frame plan). The
  legacy viewer's AR Quick Look uses a same-origin USDZ handoff, not the BFF.
- **Realtime**: none
- **Caching**: lane client fetch on mount, `cache: 'no-store'`,
  `buildBffAuthHeaders()`
- **Auth/role check**: lane is admin-scoped, fail-closed (401/403); route gated
  via the parent `3d-generation` AAA segment
- **Component sources**:
  - `apps/oshun/web/src/components/studio/StudioIsisTurntableViewerWorkspace.tsx`
  - `apps/oshun/web/src/components/studio/StudioIsisTurntableViewerRouteMap.ts`

## Cross-references

- Studio overview: [`../../studio-overview.md`](../../studio-overview.md)
- Parent route: [`studio-isis-3d-generation.md`](./studio-isis-3d-generation.md)
- Sibling routes:
  - [`studio-isis-3d-editing.md`](./studio-isis-3d-editing.md)
  - [`studio-isis-3d-post-processing.md`](./studio-isis-3d-post-processing.md)
  - [`studio-isis-gaussian-splatting.md`](./studio-isis-gaussian-splatting.md)

## Open questions / known gaps

- [ ] **Hybrid surface**: only the Orbit Frame Planner lane is BFF-wired; the
      large 3D viewer shell remains local/demo-driven (`DEMO_SCENE` /
      `CANDIDATE_SCENE`, hero asset chips from a local list) — confirm whether the
      shell should consume a real asset registry or be retired
- [ ] Confirm the nested route inherits AAA gating from `3d-generation`
- [ ] Route-map sub-paths (`/captures`, `/reviews`, `/presets`) are declared but
      not yet implemented as separate pages
