( options: BuildChromeArgsOptions, config?: Partial<Pick<EngineConfig, "browserGpuMode" | "disableGpu" | "chromePath">>, )
| 622 | const WEBGPU_FLAG = "--enable-unsafe-webgpu"; |
| 623 | |
| 624 | export function buildChromeArgs( |
| 625 | options: BuildChromeArgsOptions, |
| 626 | config?: Partial<Pick<EngineConfig, "browserGpuMode" | "disableGpu" | "chromePath">>, |
| 627 | ): string[] { |
| 628 | const platform = options.platform ?? process.platform; |
| 629 | const gpuDisabled = config?.disableGpu ?? DEFAULT_CONFIG.disableGpu; |
| 630 | const browserGpuMode = gpuDisabled |
| 631 | ? "software" |
| 632 | : (config?.browserGpuMode ?? DEFAULT_CONFIG.browserGpuMode); |
| 633 | // Chrome flags tuned for headless rendering performance. The set below is a |
| 634 | // fairly standard "headless-for-capture" configuration — similar profiles |
| 635 | // appear in Puppeteer's defaults, Playwright, Remotion, and Chrome's own |
| 636 | // headless-shell guidance. |
| 637 | const chromeArgs = [ |
| 638 | "--no-sandbox", |
| 639 | "--disable-setuid-sandbox", |
| 640 | "--disable-dev-shm-usage", |
| 641 | CANVAS_DRAW_ELEMENT_FEATURE_FLAG, |
| 642 | "--enable-webgl", |
| 643 | "--ignore-gpu-blocklist", |
| 644 | ...getBrowserGpuArgs(browserGpuMode, platform), |
| 645 | "--font-render-hinting=none", |
| 646 | "--force-color-profile=srgb", |
| 647 | `--window-size=${options.width},${options.height}`, |
| 648 | // Prevent Chrome from throttling background tabs/timers — critical when the |
| 649 | // page is offscreen during headless capture |
| 650 | "--disable-background-timer-throttling", |
| 651 | "--disable-backgrounding-occluded-windows", |
| 652 | "--disable-renderer-backgrounding", |
| 653 | "--disable-background-media-suspend", |
| 654 | // Reduce overhead from unused Chrome features |
| 655 | "--disable-breakpad", |
| 656 | "--disable-component-extensions-with-background-pages", |
| 657 | "--disable-default-apps", |
| 658 | "--disable-extensions", |
| 659 | "--disable-hang-monitor", |
| 660 | "--disable-ipc-flooding-protection", |
| 661 | "--disable-popup-blocking", |
| 662 | "--disable-sync", |
| 663 | "--disable-component-update", |
| 664 | "--disable-domain-reliability", |
| 665 | "--disable-print-preview", |
| 666 | "--no-pings", |
| 667 | "--no-zygote", |
| 668 | // Memory — scale GPU budget to available system RAM |
| 669 | `--force-gpu-mem-available-mb=${getGpuMemBudgetMb()}`, |
| 670 | "--disk-cache-size=268435456", |
| 671 | ...getLowMemoryFlags(), |
| 672 | // Disable features that add overhead |
| 673 | "--disable-features=AudioServiceOutOfProcess,IsolateOrigins,site-per-process,Translate,BackForwardCache,IntensiveWakeUpThrottling", |
| 674 | // Allow AudioContext to start without a user gesture in headless Chrome. |
| 675 | // Without this flag, any code path that constructs an AudioContext |
| 676 | // (including GSAP tweening an <audio> element's volume) triggers the |
| 677 | // autoplay policy and causes the AudioContext to stay suspended. The |
| 678 | // frame-capture loop then blocks waiting for it, deadlocking the render. |
| 679 | "--autoplay-policy=no-user-gesture-required", |
| 680 | ]; |
| 681 |
no test coverage detected