How Hyperframes Manages a Chrome Browser Pool for High-Throughput Rendering
Hyperframes maintains a single shared Chrome instance across parallel rendering workers using reference counting, capture-mode validation, and promise deduplication to eliminate the overhead of launching separate browser processes per task.
The Hyperframes video rendering engine drives headless Chrome via Puppeteer to generate frames from React compositions. When operating at scale—such as in render farms or CI pipelines spawning dozens of workers—launching a distinct Chrome binary per worker would overwhelm system resources. To solve this, the engine implements a lightweight browser pool in packages/engine/src/services/browserManager.ts that reuses one Chrome instance while ensuring thread safety and mode compatibility.
Global Pool State and Configuration
The pool relies on module-level variables that persist for the duration of the Node.js process. As defined at the top of browserManager.ts, these track the shared instance, its current usage, and the active capture mode:
pooledBrowser: Stores the singleBrowserinstance shared across workers.pooledBrowserRefCount: Tracks how many workers currently hold the pooled browser.pooledCaptureMode: Records whether the pooled instance uses Begin-frame or screenshot mode to ensure compatibility._pooledBrowserLaunchPromise: Caches the launch promise to deduplicate concurrent acquisition requests._autoBrowserGpuModeCache: Caches GPU capability probes to avoid repeated detection.
Pooling is controlled by the enableBrowserPool configuration option, which defaults to true via DEFAULT_CONFIG.enableBrowserPool in packages/engine/src/config.ts.
Acquire Logic and Mode Validation
Workers request a browser via acquireBrowser(chromeArgs, config), which implements a multi-step reuse strategy:
- Pool enabled check: The function first verifies
config.enableBrowserPool(defaulting totrue). - Existing instance validation: If
pooledBrowserexists and.connectedis true, the function compares the requested capture mode (viaresolveRequestedCaptureMode(config)) againstpooledCaptureMode. - Reference increment: When modes match,
pooledBrowserRefCountincrements and the existingBrowserobject returns immediately, avoiding any launch overhead.
If the requested mode differs from the pooled instance—for example, requesting screenshot mode when the pool holds a Begin-frame-only browser—the system does not evict the pool. Instead, it launches a dedicated Chrome instance for that specific caller. This prevents deadlocks that would occur if a screenshot task received a browser optimized only for Begin-frame capture.
Deduplicating Concurrent Launches
Under high contention, multiple workers may call acquireBrowser() simultaneously before the first Chrome instance finishes launching. To prevent redundant browser startups, the implementation uses promise caching:
When no pooled browser exists and multiple requests arrive, the first caller triggers launchBrowser(chromeArgs, config) and stores the resulting promise in _pooledBrowserLaunchPromise. Subsequent callers await this same promise rather than initiating their own launch. Once the promise resolves, the resolved Browser and its captureMode populate the pool variables, the ref-count initializes to 1, and the launch promise clears.
launchBrowser() itself, defined in the same file, selects the appropriate Chrome binary (chrome-headless-shell on Linux for Begin-frame mode) and constructs flags via packages/engine/src/services/buildChromeArgs.ts.
Releasing and Draining the Pool
When a worker finishes rendering, it calls releaseBrowser(browser, config):
- Non-pooled browsers: Close immediately.
- Pooled browsers: Decrement
pooledBrowserRefCount. When the count reaches0, the instance closes and pool variables reset tonull.
For graceful shutdowns or between independent render jobs, the drainBrowserPool() function ensures clean cleanup:
- Awaits any pending
_pooledBrowserLaunchPromiseand closes the resulting browser if it resolved. - Closes the active
pooledBrowserinstance and clears global state.
This prevents zombie Chrome processes from lingering after the Node.js process exits.
Implementation Examples
Below are the core workflows for acquiring and releasing browsers from the pool:
Acquiring a browser with automatic pool reuse:
// packages/engine/src/services/browserManager.ts
export async function acquireBrowser(
chromeArgs: string[],
config?: Partial<EngineConfig>,
): Promise<AcquiredBrowser> {
const enablePool = config?.enableBrowserPool ?? DEFAULT_CONFIG.enableBrowserPool;
// Return existing pooled browser if mode matches
if (enablePool && pooledBrowser) {
if (!pooledBrowser.connected) {
// Reset stale pool logic omitted
} else {
const requested = resolveRequestedCaptureMode(config);
if (pooledCaptureMode === requested) {
pooledBrowserRefCount += 1;
return { browser: pooledBrowser, captureMode: pooledCaptureMode };
}
}
}
// Deduplicate concurrent launches
if (enablePool && _pooledBrowserLaunchPromise) {
const result = await _pooledBrowserLaunchPromise;
return result;
}
// Launch new browser and populate pool
const launchPromise = launchBrowser(chromeArgs, config);
if (enablePool && !pooledBrowser) {
_pooledBrowserLaunchPromise = launchPromise;
const result = await launchPromise;
pooledBrowser = result.browser;
pooledCaptureMode = result.captureMode;
pooledBrowserRefCount = 1;
_pooledBrowserLaunchPromise = null;
return result;
}
return launchPromise;
}
Releasing a browser back to the pool:
// packages/engine/src/services/browserManager.ts
export async function releaseBrowser(
browser: Browser,
config?: Partial<EngineConfig>,
): Promise<void> {
const enablePool = config?.enableBrowserPool ?? DEFAULT_CONFIG.enableBrowserPool;
if (!enablePool) {
await browser.close().catch(() => {});
return;
}
if (pooledBrowser && pooledBrowser === browser) {
pooledBrowserRefCount = Math.max(0, pooledBrowserRefCount - 1);
if (pooledBrowserRefCount === 0) {
await browser.close().catch(() => {});
pooledBrowser = null;
}
return;
}
// Dedicated browser cleanup
await browser.close().catch(() => {});
}
Draining the pool on process exit:
// packages/engine/src/services/browserManager.ts
export async function drainBrowserPool(): Promise<void> {
const pending = _pooledBrowserLaunchPromise;
_pooledBrowserLaunchPromise = null;
if (pending) {
await pending.then(r => r.browser.close()).catch(() => {});
}
if (pooledBrowser) {
const b = pooledBrowser;
pooledBrowser = null;
await b.close().catch(() => {});
}
}
Related Components
The same pooling logic appears in packages/producer/src/services/browserManager.ts for the producer CLI, ensuring consistent resource management across the monorepo. Configuration defaults reside in packages/engine/src/config.ts, while Chrome argument generation—including mode-specific flag stripping for pooled instances—lives in packages/engine/src/services/buildChromeArgs.ts.
Summary
- Single instance sharing: The
pooledBrowservariable maintains one Chrome instance per process, shared viaacquireBrowser()andreleaseBrowser(). - Capture mode safety: Mode validation prevents incompatible task assignments, spawning dedicated browsers only when modes conflict.
- Concurrency protection:
_pooledBrowserLaunchPromiseensures only one Chrome launches even under high contention. - Automatic cleanup: Reference counting destroys the browser when the last worker releases it;
drainBrowserPool()handles forced cleanup. - Default enabled: Pooling activates automatically via
DEFAULT_CONFIG.enableBrowserPoolunless explicitly disabled.
Frequently Asked Questions
What happens if two workers need different capture modes?
If a worker requests a capture mode (e.g., screenshot) that differs from the pooled instance's mode (e.g., Begin-frame), the pool is not evicted. Instead, that worker receives a dedicated browser instance launched specifically for its mode. This ensures that no task receives a browser incapable of its required rendering method, preventing deadlocks.
How does the pool prevent launching multiple Chrome instances simultaneously?
The _pooledBrowserLaunchPromise variable caches the launch promise when the first caller initiates Chrome startup. Subsequent concurrent callers detect this promise and await it rather than triggering new launches. Once the promise resolves, the pool populates and the promise clears, eliminating redundant browser startups under high load.
When does the pooled Chrome instance actually close?
The pooled browser closes only when releaseBrowser() decrements the reference count to zero, or when drainBrowserPool() forces cleanup. Each call to acquireBrowser() increments pooledBrowserRefCount, and each releaseBrowser() decrements it. When the count hits zero, the system closes the browser and nullifies the pool variables.
Can I disable the browser pool for specific renders?
Yes. Pass enableBrowserPool: false in the EngineConfig when calling acquireBrowser(). When disabled, each call launches a fresh Chrome instance that closes immediately upon releaseBrowser(), bypassing the shared state entirely. This is useful for debugging or when complete process isolation is required.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →