Summary
On Windows, every hyperframes render opens a visible console window per browser launch (78 shots = 78 windows), stealing focus and making the machine unusable for anything else during a render.
This is not one of the windowsHide gaps already tracked in #3476 / #3500 / #3851 / #3960 / #3996 — those all cover hyperframes' own spawns. This one comes from @puppeteer/browsers, and it is already fixed upstream. 0.8.44 just resolves to the one broken version.
Root cause
hyperframes@0.8.44 declares "puppeteer-core": "^25.8.0". In practice that resolved here to puppeteer-core@25.9.0 → @puppeteer/browsers@3.2.1.
@puppeteer/browsers@3.2.1 lib/launch.js spawns the browser with both flags:
opts.detached ??= true; // line 157
...
childProcess.spawn(this.#executablePath, this.#args, {
detached: opts.detached, // line 173
windowsHide: true, // line 174
...
});
On Windows libuv ORs DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP (from detached) together with CREATE_NO_WINDOW (from windowsHide) into the CreateProcessW creation flags. Those are mutually exclusive: DETACHED_PROCESS wins, the child inherits no console, and chrome-headless-shell.exe — a console-subsystem binary (IMAGE_SUBSYSTEM_WINDOWS_CUI) — gets a freshly allocated, visible console. windowsHide: true is dead code in that combination.
Upstream: puppeteer/puppeteer#15410, introduced by #15339, reverted by #15415, released in @puppeteer/browsers@3.2.2.
Evidence
Controlled A/B on the same binary, same machine, only detached differs. External EnumWindows + IsWindowVisible monitor sampling at 25 ms:
MODE=detached ({detached:true, windowsHide:true}) browser procs=4 NEW visible windows=1 <- class=ConsoleWindowClass proc=chrome-headless-shell
MODE=hidden ({detached:false, windowsHide:true}) browser procs=4 NEW visible windows=0
Live capture during an actual hyperframes render, before any workaround:
class=CASCADIA_HOSTING_WINDOW_CLASS proc=WindowsTerminal
class=PseudoConsoleWindow proc=chrome-headless-shell
class=CASCADIA_HOSTING_WINDOW_CLASS proc=WindowsTerminal
class=PseudoConsoleWindow proc=chrome-headless-shell
(There is a second, independent aggravating factor worth knowing about: when Windows' Default terminal application is Windows Terminal — the Windows 11 default — the console handoff ignores CREATE_NO_WINDOW entirely, so console children pop a real Windows Terminal window even where windowsHide is correctly set. Switching to Console Host removes that layer; the @puppeteer/browsers bug above survives the switch and still produces ConsoleWindowClass windows.)
Environment
- Windows 11 Pro 26200, Node 24
hyperframes@0.8.44, puppeteer-core@25.9.0, @puppeteer/browsers@3.2.1
- Reproduced via
hyperframes render --quality high and hyperframes snapshot
Suggested fix
Require puppeteer-core >= 25.10.0 (the first release that pulls @puppeteer/browsers@3.2.2). ^25.8.0 silently permits the one broken version. #3933 already bumps @puppeteer/browsers to 3.2.2 — this is the reason it matters on Windows.
Workaround for anyone hitting this now
Patch the resolved copy in the npx cache, @puppeteer/browsers/lib/launch.js:
- opts.detached ??= true;
+ opts.detached ??= process.platform !== 'win32';
Verified: renders run with zero new visible windows afterwards.
Summary
On Windows, every
hyperframes renderopens a visible console window per browser launch (78 shots = 78 windows), stealing focus and making the machine unusable for anything else during a render.This is not one of the
windowsHidegaps already tracked in #3476 / #3500 / #3851 / #3960 / #3996 — those all cover hyperframes' own spawns. This one comes from@puppeteer/browsers, and it is already fixed upstream.0.8.44just resolves to the one broken version.Root cause
hyperframes@0.8.44declares"puppeteer-core": "^25.8.0". In practice that resolved here topuppeteer-core@25.9.0→@puppeteer/browsers@3.2.1.@puppeteer/browsers@3.2.1lib/launch.jsspawns the browser with both flags:On Windows libuv ORs
DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP(fromdetached) together withCREATE_NO_WINDOW(fromwindowsHide) into theCreateProcessWcreation flags. Those are mutually exclusive:DETACHED_PROCESSwins, the child inherits no console, andchrome-headless-shell.exe— a console-subsystem binary (IMAGE_SUBSYSTEM_WINDOWS_CUI) — gets a freshly allocated, visible console.windowsHide: trueis dead code in that combination.Upstream: puppeteer/puppeteer#15410, introduced by #15339, reverted by #15415, released in
@puppeteer/browsers@3.2.2.Evidence
Controlled A/B on the same binary, same machine, only
detacheddiffers. ExternalEnumWindows+IsWindowVisiblemonitor sampling at 25 ms:Live capture during an actual
hyperframes render, before any workaround:(There is a second, independent aggravating factor worth knowing about: when Windows' Default terminal application is Windows Terminal — the Windows 11 default — the console handoff ignores
CREATE_NO_WINDOWentirely, so console children pop a real Windows Terminal window even wherewindowsHideis correctly set. Switching to Console Host removes that layer; the@puppeteer/browsersbug above survives the switch and still producesConsoleWindowClasswindows.)Environment
hyperframes@0.8.44,puppeteer-core@25.9.0,@puppeteer/browsers@3.2.1hyperframes render --quality highandhyperframes snapshotSuggested fix
Require
puppeteer-core >= 25.10.0(the first release that pulls@puppeteer/browsers@3.2.2).^25.8.0silently permits the one broken version. #3933 already bumps@puppeteer/browsersto 3.2.2 — this is the reason it matters on Windows.Workaround for anyone hitting this now
Patch the resolved copy in the npx cache,
@puppeteer/browsers/lib/launch.js:Verified: renders run with zero new visible windows afterwards.