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 adom-desktop 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-browser-extension."

Part of adom-browser-extension (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 adom-desktop window recorder), NOT the extension

The single best way to record a browser window is the CORE adom-desktop 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-desktop 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-desktop-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.