---
name: hd-demo-recording
description: >
  THE recipe for recording a demo video of Hydrogen Desktop: screen-record an AI prompt
  being TYPED (visible human-jitter typing) into a Claude tab plus the AI's resulting
  output, then speed the footage 10x to 30x in video-post and lay an adom-tts voiceover
  on top, so a viewer sees exactly what using an app in HD feels like. Covers the whole
  pipeline end to end: driving HD's control API (in-workspace OR from a remote container
  via adom-desktop hd_api), the high-fps native recorder, multi-tab multi-prompt demos,
  getting the mp4 out (streamed binary, never base64), and the post-production handoff.
  Trigger words: demo video, record a demo, hd demo, demo recording, record the typing,
  screen record hd, record ai prompt, app demo, demo pipeline, speedup demo, tts overlay
  demo, show what the app feels like, hd-demo-recording.
---

# hd-demo-recording: record an HD demo video, end to end

The canonical Adom demo: the viewer watches an AI prompt get TYPED into Hydrogen
Desktop, watches the AI do the work, and hears a narrator explain it, all in a tight
sped-up clip. This skill is the full pipeline. It exists because every piece is easy
to get slightly wrong and the pieces live in four different tools.

The shape of every demo:

1. Start HD's native high-fps recorder (no picker, no sharing banner).
2. Type the prompt into a Claude tab with VISIBLE typing (the sexy part) and submit.
3. Let the AI's output stream on camera.
4. Stop the recorder, collect the mp4.
5. Post-production: speed up 10x to 30x (video-post), overlay adom-tts narration.

## Step 0: pick your calling transport

Everything below is HD control-API calls. Two contexts:

**Inside the HD workspace** (the WSL distro): the live base URL is in a file, never an
env var:

```bash
CTRL=$(cat ~/.adom/hd-control-url)
curl -s -X POST "$CTRL/claude/inject" -H 'Content-Type: application/json' -d '{...}'
```

**From a remote container** (hw, cloud): go through the adom-desktop relay's
pass-through verb. Never curl through the relay (quoting gets mangled) and never hunt
for the port (hd_api auto-discovers it):

```bash
adom-desktop hd_api '{"method":"POST","path":"/claude/inject","body":{...}}'
```

`hd_*` verbs auto-route to the desktop running HD; `desktop_*` and `pull_file` need
`--target <desktop name>` when more than one desktop is on the relay.

## Step 1: preflight (30 seconds, saves the take)

- `GET /health` answers with service `hydrogen-desktop-control` (not the SPA).
- `GET /capture/availability`: nothing else is recording; it also tells you which
  screenshot method coexists with a recording (CDP `POST /screenshot`, not the SSE path).
- `GET /recording/native/sources`: confirm the exact window name to capture.
- `GET /claude/auth-check`: Claude is signed in (a demo of a sign-in wall is not a demo).

## Step 2: start the recorder

```json
POST /recording/native/start
{"source":"self","cursor":true,"codec":"h264","maxSeconds":300,"reason":"demo take cap"}
```

- **`source:"self"` is THE way to record HD** (alias: `"hd"`). Never guess HD's window
  title: it carries the version and dev stamps, and title-guessing has recorded the
  WRONG app before (an Edge window titled "Claude"). `window:<substr>` matches other
  apps' windows; `"screen"` records the whole primary monitor.
- WGC per-window capture: high fps, background-safe, no picker, no "you are sharing
  your screen" banner.
- Overriding `maxSeconds` (default 600) REQUIRES a `reason` string, it shows in the
  UI toast if the cap fires. Omit both if the default cap is fine.
- `codec:"h264"` for web-playable output (h265 is smaller but not web-playable).
- `mic:true` only if you want live narration in the raw take; the usual pipeline adds
  TTS narration in post instead.
- Wait about 1 second after start so the first frames show the clean starting state.
- **Verify the take is ALIVE**: `GET /recording/native/status` while recording shows
  `frames` climbing within about 2 seconds. If it stays 0, the take is dead (target
  minimized or wrong window): stop now and fix, do not record 90 seconds of nothing.
  A 0-frame take errors at stop (500) instead of pretending success.

## Step 3: type the prompt(s) on camera

One call per prompt opens a NEW Claude tab, waits for its compose box, types with
human jitter, and submits:

```json
POST /claude/new-chat-and-send
{"text":"<the demo prompt>","typed":true,"submit":true,"delay_ms":35}
```

- `typed:true` is the visible typing (about `delay_ms` per char with 50 percent
  jitter). `typed:false` pastes instantly: fine for seeding, boring on camera.
- The call BLOCKS until typing completes: perfect for sequencing. If a relay call
  times out on a long prompt, the typing still finishes on HD's side. Do NOT re-fire.
  Confirm with `GET /claude/conversation-state` (turns went up), then continue.
  Keep prompts short enough to type in under about 20 seconds.
- Single-prompt demo into the CURRENT tab instead: `POST /claude/inject` with the
  same body shape.

**Multi-prompt demos** (the 5-tabs pattern): repeat the call once per prompt (each
opens its own tab), then flip through the finished answers on camera with the
BACKGROUND tab switch:

```json
POST /claude/tabs/activate  {"index":0}   // 0-based; or {"label":"substring"}
```

Verify each switch took with `GET /claude/tabs` (`active:true` moved). Pause a few
seconds per tab so the sped-up video gives each answer screen time.
`GET /claude/conversations` gives per-tab context_ids if you want to seed specific
tabs deterministically (`/claude/inject` with `context_index`).

## Step 4: stop and collect the mp4

```json
POST /recording/native/stop
```

The response carries `file` (relative), `fileAbsolute` (path INSIDE the WSL distro),
`fileWindows` (the `\\wsl$\...` UNC path), and `frames`. If your transport hiccups
and you lose the stop response, `GET /recording/native/status` when idle returns
`lastOutput` with the same paths, the take is recoverable.

- **In the workspace**: `fileAbsolute` is directly readable; you are done.
- **Remote container**: pull it as STREAMED BINARY, 1 MiB chunks, sha256-verified.
  Never base64 an mp4 through an exec channel:

```bash
adom-desktop --target <desktop> pull_file '{"filePaths":["<fileWindows value>"],"saveTo":"<dir>"}'
```

## Step 5: post-production

- **Speedup**: the `video-post` skill owns this. The house style is 10x for demos
  where the output matters, 30x for long AI work sessions. Keep the typing segment
  readable (video-post can ramp: normal speed on the typing, fast on the waiting).
- **Narration**: synthesize with `adom-tts say` (it pronounces Adom terms correctly
  and caches repeats), then video-post mixes it as the audio track. Write narration
  that explains what the viewer is SEEING at each sped-up moment.
- **Recording choreography** (captions, panel staging, what makes a take clean) lives
  in the `demo-recording` skill; read it for anything fancier than a single-window take.

## Known hazards

- **An HD restart kills the take AND the demo tabs.** The recorder dies with the HD
  process (the unfinalized temp mp4 is lost) and reopened Claude tabs reset. Do not
  run a demo while anyone (including another AI thread) is rebuilding/relaunching HD
  on the same machine; if a take mysteriously vanished, check whether HD restarted
  mid-run (window title timestamp or /buildinfo).
- Tab state reads (`GET /claude/tabs`) are DOM reads: labels can be briefly empty
  while the editor reloads. Prefer `GET /claude/conversations` for stable
  context_ids + titles.
- Screenshot of the Claude panel: `POST /screenshot {target:"claude"}` captures the
  editor surface containing it (the panel is a nested webview with no own CDP target).

## Safety and cleanup (non-negotiable)

- These endpoints are disruptive tier and their responses carry a `_safety` block:
  you are typing into the user's REAL Claude panel (each submitted prompt SPENDS a
  real turn; a 5-prompt demo spends 5) and recording their REAL screen. That is the
  point of a demo, but only do it for a demo the user asked for.
- ALWAYS stop a recording you started, even when a step fails: stop first, then
  report. `GET /recording/native/status` tells you if one is live.
- NEVER call `/claude/tabs/close-all` to clean up: it closes the user's own tabs
  too. There is no per-tab close; leave demo tabs open and tell the user they exist.
- Delete junk takes you pulled; do not litter recordings/ with failed attempts.

## Related

[[hd-api]] (transport + safety tiers), [[hd-recording]] (recorder endpoint reference),
demo-recording (choreography), video-post (speedup + publish), adom-tts (narration).
