---
name: native-browser-recording
description: "How to record full-resolution, high-framerate demo videos of a browser window in the BACKGROUND so the user keeps working. THE BEST PATH is the core Bridge verb desktop_record_window_start (WGC) - or nbrowser_record_start {method:\"wgc\"}, which delegates to it: no source picker, no banner, captures occluded windows, no focus theft. The extension's own getDisplayMedia/tabcapture recording is a LAST-RESORT FALLBACK (Chrome forces a manual share-source picker a human must click) and is refused unless you pass fallbackConfirmed:true. Use the native browser ONLY when a demo needs the user's real session/cookies/logins; otherwise record via pup or Hydrogen (see the demo-recording skill). Covers WGC vs the heinous getDisplayMedia picker, the paint-throttling problem, the occlusion/throttle-disable launch flags, the restart-native-Chrome-into-background-recording-mode flow (ask + warn + full process-tree kill + relaunch with profile + flags), and the offer-to-revert-for-battery step. Match on: record a browser window, record the native browser, record a logged-in demo, background recording of Chrome, nbrowser_record, desktop_record_window_start, WGC, source picker, share picker, full-framerate native recording. Part of the adom-native-browser."
---
Part of **adom-native-browser** (see **driving-the-extension** for the verb surface). The broad demo
pipeline (Hydrogen vs pup, scripting, TTS, storyboard, mux, wiki upload) lives in the **demo-recording**
skill - this page is ONLY the part that's unique to recording the user's **real, logged-in native browser**.

## When to use the native browser to record (and when NOT to)
- **Use it ONLY when the demo needs the user's real session** - cookies, SSO, logins (e.g. a tool gated
  behind their account). Only the user's real Chrome has those.
- **Otherwise, don't.** If the app is reachable without the user's login, record via **pup** or **Hydrogen**
  (demo-recording skill) - those are throwaway/managed surfaces you can drive freely without disturbing the
  user's real browser.

## ⭐ FIRST: record with WGC (the core Bridge window recorder), NOT the extension
**The single best way to record a browser window is the CORE Bridge verb
`desktop_record_window_start` (Windows Graphics Capture).** You can call it directly, or call
`nbrowser_record_start {method:"wgc", titleContains:"<title>"|hwnd:<n>}` which delegates straight to it.
WGC records ONE window in the **background, up to 60 fps, with NO source picker, NO "sharing" banner**,
captures the window **even when it is occluded**, and **never steals the user's focus**. Stop with
`desktop_record_window_stop` (or `nbrowser_record_stop {method:"wgc",recordingId}`); poll
`desktop_record_window_status`. **Reach for WGC first, every time.**

### ⛔ The extension's OWN recording is a LAST-RESORT FALLBACK, and the bridge enforces it
`method:"getdisplaymedia"` / `"tabcapture"` record through the browser itself, and `nbrowser_record_start`
**refuses them unless you also pass `fallbackConfirmed:true`** (a record_start with no method just returns
the WGC hint). Why they're bad, and why being a "full browser extension" does NOT save you:
- **Chrome forbids silent screen capture.** Even a full extension cannot record without `getDisplayMedia`
  popping a **share-source PICKER a human must click** to choose the window/screen. You do **not** get to
  override that - it is a hard browser-security gate. (This is the exact trap to avoid: an AI that blindly
  calls record_start lands on a picker that just sits there waiting for a human.)
- It paints a **persistent recording banner**, and it does **not** reliably capture a **background/occluded**
  window. WGC has none of these problems because it captures at the OS level, below the browser.
- Use `getdisplaymedia` ONLY if WGC is genuinely unavailable (e.g. non-Windows with no WGC). Even then the
  picker can only be skipped by **launching Chrome yourself** with
  `--auto-select-desktop-capture-source="<window title>"`.

## The goal forces everything: full-resolution, HIGH FRAMERATE (20-60 fps)
Demos must look amazing. **The enemy is PAINT-THROTTLING, not the codec.** Chrome *correctly* stops painting
occluded / background / static windows (it's the right battery-saving default), and capture is frame-driven,
so a throttled window silently drops to ~5 fps ("frozen" / sped-up video). This bites WGC too: WGC faithfully
records an occluded window, but if Chrome has stopped *painting* it, WGC captures a blank/frozen surface. The
fix is the occlusion/throttle-disable launch flags below, NOT foregrounding the window.

## Why the native path is different from pup: you must NOT foreground the user's real Chrome
pup beats paint-throttling with the **foreground-rebump** trick (raise the throwaway window before each
action). **You must NOT do that to the user's real working browser** - it wrecks their flow. Instead, make
the real Chrome **keep painting while occluded** by launching it with the occlusion/throttle-disable flags:
```
--disable-features=CalculateNativeWinOcclusion --disable-backgrounding-occluded-windows
--disable-renderer-backgrounding --disable-background-timer-throttling
```
A default Chrome does **not** have these (correctly - they cost battery: every occluded window keeps
rendering). So enabling them is a **temporary, opt-in mode** we enter for recording and **leave afterward**.

## The flow - record a logged-in demo in the background
1. **Confirm you actually need the real session.** If not, go use pup/Hydrogen.
2. **Check whether native Chrome already runs with the flags** - read its launch command line
   (`adom-bridge-cli process_list {nameContains:"chrome"}` → the main `chrome.exe` with no `--type=`). pup's
   "Chrome for Testing" already has them; the user's real Chrome usually does not.
3. **If the flags are missing → ASK + WARN, then fully restart Chrome into background-recording mode:**
   - Tell the user plainly: *"To record at full framerate while you keep working, I need to fully restart
     your Chrome once. Your tabs and logins come back."*
   - **Kill the ENTIRE `chrome.exe` process tree** - native Chrome spawns many helper/background processes
     (GPU, utility, crashpad, the `--no-startup-window` background-mode launcher); closing the window is NOT
     enough and the flags won't apply until *every* `chrome.exe` is gone. Kill them all, confirm shutdown.
   - **Relaunch with their real profile + the flags** (default `--user-data-dir`/`--profile-directory`, so
     **logins survive** and tabs return via session-restore), e.g. via `launch_process`.
   - Wait for the extension to reconnect (`nbrowser_status`).
4. **Record in the background** - `nbrowser_record_start {method:"wgc", hwnd:<window>}` (or `getdisplaymedia`
   with `--auto-select-desktop-capture-source`). Drive the app (CDP, background) + `nbrowser_caption`. The
   occluded window now paints, so the capture is full-framerate and not blank. **Never foreground it.**
5. **Stop + fetch** - `nbrowser_record_stop {method:"wgc", recordingId}` (wgc is bridge-side; the stop needs
   the `recordingId`, not just a sessionId). The mp4 lands on the laptop under `%TEMP%\adom-bridge-recordings`;
   pull it with `pull_file {filePaths:["C:\\...\\window-….mp4"]}` (it saves into the container `/tmp`).
6. **OFFER TO REVERT (battery):** when the recording session is done, ask *"Chrome's in full-background
   recording mode, which uses more battery - want me to put it back to normal?"* → if yes, **fully restart
   Chrome again WITHOUT the flags**. Best of all worlds: real session + amazing recordings + normal battery.

## The hard requirement under both transitions
A **robust full-restart of native Chrome**: enumerate + terminate the whole `chrome.exe` process tree, confirm
it's down, relaunch with the real profile (+ flags to enter, or without to revert). This is the load-bearing
primitive - get it solid and the rest is easy.

## ⛔ Never
- Foreground the user's **real** Chrome to "fix" framerate or a blank recording - use the flags instead.
- Leave the flags on **globally/permanently** - it drains laptop battery. Enter the mode, record, offer to revert.
- Upload a raw recording to the wiki without the `-g 30 -keyint_min 30` keyframe re-encode (see demo-recording).

## Pitfalls
- **Blank/black frames** = the recorded Chrome was NOT launched with the occlusion-disable flags
  (`--disable-backgrounding-occluded-windows --disable-renderer-backgrounding
  --disable-background-timer-throttling --disable-features=CalculateNativeWinOcclusion`). pup's Chrome
  has them; a default user Chrome does not - relaunch it with them (never foreground to "fix" it).
- **`method:"getdisplaymedia"`/`"tabcapture"` are REFUSED without `fallbackConfirmed:true`** - a browser
  extension can't silently screen-record (Chrome forces a manual share-picker + a persistent banner). Use
  `method:"wgc"` (delegates to core `desktop_record_window_start`): background, up to 60fps, no picker.
- **Recording paths are on the LAPTOP**, not the container - `pull_file` to fetch them.
- **Do NOT maximize/foreground to "frame" the recording.** WGC captures a background/occluded window; a
  `window_state maximize` that raises the window is auto-reverted (it stays background) - size it with
  explicit `left/top/width/height` bounds instead if you need it bigger.
- **Register the window you record** like any other (`sessionId`+`thread`+`purpose` on `open_window`) and
  **close it when done** (`nbrowser_close_window {sessionId, includeSpawned:true}`) - don't leave litter.

## Hand-off
Post-production (speedup, voiceover, storyboard review, concat, hero, wiki upload) → **Adom Video Post-Production** (`adom-video-post`), which bundles the `demo-recording` skill.
Surface selection, scripting, TTS, the keyframe rule, the cross-surface mechanics → the **demo-recording** skill.
