# Open the Attic Portal

**What you are learning:** Start with the product a developer actually wants. The browser will display and control an RTX experience, but the rendering and OpenUSD work stay on the server.

**Mission:** Prepare the Old Attic as a live, inspectable environment for a future robot application. Your first proof is the complete path from the supplied OpenUSD scene to responsive browser pixels.

**Why this matters for a robot-ready scene:** Before a robotics team can commission behaviors or sensors, it needs a dependable way to inspect the same composed world the machine will use. A live viewport proves that scene composition, camera state, rendering, streaming, and input all agree; a still image cannot prove that runtime path.

**Your 3D skills at work:** Scene assembly tells you whether the correct assets, hierarchy, materials, scale, and camera context survived composition. Real-time, VFX, and pipeline experience helps you recognize the difference between a beautiful frame and a healthy render loop - exactly the judgment needed to commission a physical-AI application.

## Portal Pulse: From USD Scene to Live Pixels

How this page’s lifecycle fits together. Step through the slides, then start the build below.

### Two Phases: Start Once, Then Repeat

Two Phases: Start Once, Then Repeat

The portal sets up once, then runs a loop.

<svg version="1.1" width="1.3em" height="1.3em" class="sd-octicon sd-octicon-cpu slide-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="M6.5.75V2h3V.75a.75.75 0 0 1 1.5 0V2h1.25c.966 0 1.75.784 1.75 1.75V5h1.25a.75.75 0 0 1 0 1.5H14v3h1.25a.75.75 0 0 1 0 1.5H14v1.25A1.75 1.75 0 0 1 12.25 14H11v1.25a.75.75 0 0 1-1.5 0V14h-3v1.25a.75.75 0 0 1-1.5 0V14H3.75A1.75 1.75 0 0 1 2 12.25V11H.75a.75.75 0 0 1 0-1.5H2v-3H.75a.75.75 0 0 1 0-1.5H2V3.75C2 2.784 2.784 2 3.75 2H5V.75a.75.75 0 0 1 1.5 0Zm5.75 11.75a.25.25 0 0 0 .25-.25v-8.5a.25.25 0 0 0-.25-.25h-8.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25ZM5.75 5h4.5a.75.75 0 0 1 .75.75v4.5a.75.75 0 0 1-.75.75h-4.5a.75.75 0 0 1-.75-.75v-4.5A.75.75 0 0 1 5.75 5Zm.75 4.5h3v-3h-3Z"></path></svg> One long-lived **`ovrtx`** renderer loads the composed OpenUSD stage.

<svg version="1.1" width="1.3em" height="1.3em" class="sd-octicon sd-octicon-eye slide-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="M8 2c1.981 0 3.671.992 4.933 2.078 1.27 1.091 2.187 2.345 2.637 3.023a1.62 1.62 0 0 1 0 1.798c-.45.678-1.367 1.932-2.637 3.023C11.67 13.008 9.981 14 8 14c-1.981 0-3.671-.992-4.933-2.078C1.797 10.83.88 9.576.43 8.898a1.62 1.62 0 0 1 0-1.798c.45-.677 1.367-1.931 2.637-3.022C4.33 2.992 6.019 2 8 2ZM1.679 7.932a.12.12 0 0 0 0 .136c.411.622 1.241 1.75 2.366 2.717C5.176 11.758 6.527 12.5 8 12.5c1.473 0 2.825-.742 3.955-1.715 1.124-.967 1.954-2.096 2.366-2.717a.12.12 0 0 0 0-.136c-.412-.621-1.242-1.75-2.366-2.717C10.824 4.242 9.473 3.5 8 3.5c-1.473 0-2.825.742-3.955 1.715-1.124.967-1.954 2.096-2.366 2.717ZM8 10a2 2 0 1 1-.001-3.999A2 2 0 0 1 8 10Z"></path></svg> A `Camera`, `RenderProduct`, and `LdrColor` `RenderVar` are connected.

<svg version="1.1" width="1.3em" height="1.3em" class="sd-octicon sd-octicon-broadcast slide-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="M8.75 8.582v5.668a.75.75 0 0 1-1.5 0V8.582a1.75 1.75 0 1 1 1.5 0Zm3.983-7.125a.75.75 0 0 1 1.06.026A7.976 7.976 0 0 1 16 7c0 2.139-.84 4.083-2.207 5.517a.75.75 0 1 1-1.086-1.034A6.474 6.474 0 0 0 14.5 7a6.474 6.474 0 0 0-1.793-4.483.75.75 0 0 1 .026-1.06Zm-9.466 0c.3.286.312.76.026 1.06A6.474 6.474 0 0 0 1.5 7a6.47 6.47 0 0 0 1.793 4.483.75.75 0 0 1-1.086 1.034A7.973 7.973 0 0 1 0 7c0-2.139.84-4.083 2.207-5.517a.75.75 0 0 1 1.06-.026Zm8.556 2.321A4.988 4.988 0 0 1 13 7a4.988 4.988 0 0 1-1.177 3.222.75.75 0 1 1-1.146-.967A3.487 3.487 0 0 0 11.5 7c0-.86-.309-1.645-.823-2.255a.75.75 0 0 1 1.146-.967Zm-6.492.958A3.48 3.48 0 0 0 4.5 7a3.48 3.48 0 0 0 .823 2.255.75.75 0 0 1-1.146.967A4.981 4.981 0 0 1 3 7a4.982 4.982 0 0 1 1.188-3.236.75.75 0 1 1 1.143.972Z"></path></svg> **`ovstream`** creates a WebRTC server and registers its callbacks.

<svg version="1.1" width="1.3em" height="1.3em" class="sd-octicon sd-octicon-stopwatch slide-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="M5.75.75A.75.75 0 0 1 6.5 0h3a.75.75 0 0 1 0 1.5h-.75v1l-.001.041a6.724 6.724 0 0 1 3.464 1.435l.007-.006.75-.75a.749.749 0 0 1 1.275.326.749.749 0 0 1-.215.734l-.75.75-.006.007a6.75 6.75 0 1 1-10.548 0L2.72 5.03l-.75-.75a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018l.75.75.007.006A6.72 6.72 0 0 1 7.25 2.541V1.5H6.5a.75.75 0 0 1-.75-.75ZM8 14.5a5.25 5.25 0 1 0-.001-10.501A5.25 5.25 0 0 0 8 14.5Zm.389-6.7 1.33-1.33a.75.75 0 1 1 1.061 1.06L9.45 8.861A1.503 1.503 0 0 1 8 10.75a1.499 1.499 0 1 1 .389-2.95Z"></path></svg> All of this happens once at startup - before the pulse begins.

### The Portal Pulse Loop

The Portal Pulse Loop

Input in, frame out - many times per second.

<svg version="1.1" width="1.3em" height="1.3em" class="sd-octicon sd-octicon-browser slide-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="M0 2.75C0 1.784.784 1 1.75 1h12.5c.966 0 1.75.784 1.75 1.75v10.5A1.75 1.75 0 0 1 14.25 15H1.75A1.75 1.75 0 0 1 0 13.25ZM14.5 6h-13v7.25c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25Zm-6-3.5v2h6V2.75a.25.25 0 0 0-.25-.25ZM5 2.5v2h2v-2Zm-3.25 0a.25.25 0 0 0-.25.25V4.5h2v-2Z"></path></svg> You drag inside the browser viewport.

<svg version="1.1" width="1.3em" height="1.3em" class="sd-octicon sd-octicon-broadcast slide-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="M8.75 8.582v5.668a.75.75 0 0 1-1.5 0V8.582a1.75 1.75 0 1 1 1.5 0Zm3.983-7.125a.75.75 0 0 1 1.06.026A7.976 7.976 0 0 1 16 7c0 2.139-.84 4.083-2.207 5.517a.75.75 0 1 1-1.086-1.034A6.474 6.474 0 0 0 14.5 7a6.474 6.474 0 0 0-1.793-4.483.75.75 0 0 1 .026-1.06Zm-9.466 0c.3.286.312.76.026 1.06A6.474 6.474 0 0 0 1.5 7a6.47 6.47 0 0 0 1.793 4.483.75.75 0 0 1-1.086 1.034A7.973 7.973 0 0 1 0 7c0-2.139.84-4.083 2.207-5.517a.75.75 0 0 1 1.06-.026Zm8.556 2.321A4.988 4.988 0 0 1 13 7a4.988 4.988 0 0 1-1.177 3.222.75.75 0 1 1-1.146-.967A3.487 3.487 0 0 0 11.5 7c0-.86-.309-1.645-.823-2.255a.75.75 0 0 1 1.146-.967Zm-6.492.958A3.48 3.48 0 0 0 4.5 7a3.48 3.48 0 0 0 .823 2.255.75.75 0 0 1-1.146.967A4.981 4.981 0 0 1 3 7a4.982 4.982 0 0 1 1.188-3.236.75.75 0 1 1 1.143.972Z"></path></svg> **`ovstream`** carries the input back; the callback queues a camera intent.

<svg version="1.1" width="1.3em" height="1.3em" class="sd-octicon sd-octicon-cpu slide-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="M6.5.75V2h3V.75a.75.75 0 0 1 1.5 0V2h1.25c.966 0 1.75.784 1.75 1.75V5h1.25a.75.75 0 0 1 0 1.5H14v3h1.25a.75.75 0 0 1 0 1.5H14v1.25A1.75 1.75 0 0 1 12.25 14H11v1.25a.75.75 0 0 1-1.5 0V14h-3v1.25a.75.75 0 0 1-1.5 0V14H3.75A1.75 1.75 0 0 1 2 12.25V11H.75a.75.75 0 0 1 0-1.5H2v-3H.75a.75.75 0 0 1 0-1.5H2V3.75C2 2.784 2.784 2 3.75 2H5V.75a.75.75 0 0 1 1.5 0Zm5.75 11.75a.25.25 0 0 0 .25-.25v-8.5a.25.25 0 0 0-.25-.25h-8.5a.25.25 0 0 0-.25.25v8.5c0 .138.112.25.25.25ZM5.75 5h4.5a.75.75 0 0 1 .75.75v4.5a.75.75 0 0 1-.75.75h-4.5a.75.75 0 0 1-.75-.75v-4.5A.75.75 0 0 1 5.75 5Zm.75 4.5h3v-3h-3Z"></path></svg> The renderer-owning thread updates the Camera and **`ovrtx`** steps the RenderProduct.

<svg version="1.1" width="1.3em" height="1.3em" class="sd-octicon sd-octicon-arrow-switch slide-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="M5.22 14.78a.75.75 0 0 0 1.06-1.06L4.56 12h8.69a.75.75 0 0 0 0-1.5H4.56l1.72-1.72a.75.75 0 0 0-1.06-1.06l-3 3a.75.75 0 0 0 0 1.06l3 3Zm5.56-6.5a.75.75 0 1 1-1.06-1.06l1.72-1.72H2.75a.75.75 0 0 1 0-1.5h8.69L9.72 2.28a.75.75 0 0 1 1.06-1.06l3 3a.75.75 0 0 1 0 1.06l-3 3Z"></path></svg> The app maps `LdrColor`, **`ovstream`** sends the frame, the browser displays it - then it repeats.

## Mission 1.a — Commission the Attic Portal

### 1. Inspect — Separate Live Pixels From Trusted World Evidence

#### NOTE
Need the files first? Download the assets, starter code, and agent skills from the [Download Course Materials](https://docs.nvidia.com/learning/physical-ai/physical-ai-agent-bootcamp/latest/lab-1-rtx-viewport/index.md#download-course-materials) section on the course Overview, then use the local paths listed below.

You’ll direct your agent to create the Attic Portal from the Old Attic OpenUSD scene, run the portal’s diagnostic check, and launch it. This USD scene is our own prepared file; you’ll orchestrate with your agent to call the skills each library needs to build the server, the React application, and the page layout, then connect them to the scene.

To give the agent the recipe it needs, use NVIDIA’s [Omniverse Realtime Viewer skill](https://github.com/NVIDIA/skills/tree/main/skills/omniverse-realtime-viewer). This packaged skill teaches the agent the right guidance for `ovrtx` rendering, `ovstream` delivery, browser input, and validation. The skill is not the renderer and it is not a ready-made application - it teaches the agent how the pieces work together, then steps away once the portal is built.

![Diagram titled What Omniverse Realtime Viewer Does, split into build time and run time. At build time, your mission brief (Goal, Skills, Context, Done when) feeds Codex, the orchestrator, which reads the Omniverse Realtime Viewer skill for version-specific guidance on stage loading, render lifecycle, browser input, stream messages, and validation, then builds one application. At run time, OpenUSD holds scene truth and prim paths, the Python application validates commands and publishes state, ovrtx is the RTX renderer worker, ovstream is the courier carrying WebRTC video and input, and the React browser client displays and sends input. The skill does not render or stream; it teaches the agent how to connect the libraries and preserve one app, one renderer, one stream.](images/lab-1-realtime-viewer-overview.png)

The skill does not know the museum mission or what makes this scene useful for robotics. Your prompt must explicitly ask the agent to compose two regions in the React app: the streamed viewport and an empty diagnostics panel beside it. “Empty” means the panel has a title and *MODULE NOT INSTALLED* status, but no live controls or invented telemetry. You’ll build it out one capability at a time in the next two missions.

The course files are located at:

- Realtime Viewer skill: `~/RTXViewport/skills/omniverse-realtime-viewer`
- Old Attic scene: `~/RTXViewport/Attic_Nvidia/old_attic.usd`
- Portal project the agent will create: `~/RTXViewport/Attic_Portal`

Before building, inspect the distinction you will need to approve. A responsive viewport proves that OpenUSD, `ovrtx`, `ovstream`, and the browser share a healthy runtime path. It does not, by itself, prove that scene units, axes, object identities, or camera frames are correct for a future robot application. Scene assembly and production-review experience are what let you recognize that difference.

### 2. Specify — Produce Your Portal Commissioning Brief

Decide the evidence you would require before treating a rendered world as trustworthy. Open the [Prompt Builder](https://docs.nvidia.com/learning/physical-ai/physical-ai-agent-bootcamp/latest/prompt-builder.md), choose the **Ray Trace Your Way to a Better Life** session, and select **Mission 1.a - Commission the Attic Portal**. Turn that decision into an addition for `Context` and an observable addition for `Done when`. The tested mission still owns the launch architecture; your answers tell the agent which world-quality evidence matters to you. If you want to move quickly, leave the optional fields blank and continue with the tested prompt below.

### 3. Build — Launch the Portal

![The Make It Yours Prompt Builder with the Ray Trace Your Way to a Better Life session and Mission 1.a - Commission the Attic Portal selected. The left Your Answers panel explains why the prompt matters and invites optional 3D decisions, while the right Your Prompt panel shows the live four-field contract - Goal, Skills, Context, and Done when - with Copy prompt, Download .txt, and Reset controls.](images/lab-1-prompt-builder-mission-1a.png)

### Show a finished prompt

```text
Goal:      Launch the prepared Attic Portal so the Old Attic appears as a live,
           interactive RTX viewport in the browser.
Skills:    Read and use ~/RTXViewport/skills/omniverse-realtime-viewer/SKILL.md
           for the launch and browser-streaming workflow.
Context:   The prepared app is at ~/RTXViewport/Attic_Portal and its OpenUSD scene
           is ~/RTXViewport/Attic_Nvidia/old_attic.usd. The app already includes its server,
           browser client, and launch tools.
Done when: The browser shows the live Old Attic; orbit, pan, and zoom respond; the R-17
           Signal Panel says MODULE NOT INSTALLED; and the agent gives me the browser URL.
```

### 4. Verify — Prove the Portal Pulse

![The live Attic Portal in the browser - the Old Attic scene with the cyan Memory Cube on a wooden trunk, a connected status badge, and the R-17 Signal Panel reading MODULE NOT INSTALLED.](images/lab-1-attic-portal-live.png)

When the browser opens, inspect the result as a commissioning review rather than accepting the first frame. Orbit, pan, and zoom once each. Every gesture should travel through the complete loop:

```text
Browser input → ovstream → application queue → active Camera
              → ovrtx steps the RenderProduct → LdrColor
              → ovstream video → Browser
```

![Screen recording of orbiting and framing the live browser-streamed RTX viewport of the Old Attic scene, showing the render-stream-input loop staying interactive as the camera moves.](videos/lab-1-attic-portal-walkthrough.webm)

The **agent and skills** guided the build. At runtime, **OpenUSD** describes the world, **`ovrtx`** renders it, **`ovstream`** transports input and frames, and the **browser** displays the result. Compare the live portal with both the provided checks and the brief you defined. If your evidence is missing, revise the brief; do not redefine success as “a window opened.”

## Validate: Attic Portal Online

Your portal is ready only when all of these are true:

- <input class="task-list-item-checkbox" disabled="disabled" type="checkbox"> The browser shows the attic - not a blank video element or static screenshot.
- <input class="task-list-item-checkbox" disabled="disabled" type="checkbox"> Orbit, pan, and zoom each produce a newly rendered view.
- <input class="task-list-item-checkbox" disabled="disabled" type="checkbox"> Server health becomes ready after the first valid rendered-and-converted frame.
- <input class="task-list-item-checkbox" disabled="disabled" type="checkbox"> The browser shows the expected *Signal Panel — MODULE NOT INSTALLED* placeholder for the next mission.

With the portal live, we’ll give the Memory Cube a reliable machine identity and command route in [Establish the Cube Link](https://docs.nvidia.com/learning/physical-ai/physical-ai-agent-bootcamp/latest/lab-1-rtx-viewport/establish-the-cube-link.md).
