Skip to main content
On this page

Browser pool

All browser-backed engines (Mermaid, Excalidraw, Draw.io) render through one Chromium instance managed by BrowserPool in src/pool.ts. Graphviz is WASM-only (@viz-js/viz) and does not touch the pool.

Why one browser

Launching Chromium takes 300-800 ms on first use. Reusing the same browser across diagram types makes large batches cheap and keeps memory predictable. The pool is lazy: it launches on first acquire() and disposes on idle or process exit.

Four pages

Three browser-backed engines need four pages because Mermaid’s mermaid.initialize() mutates the page globally:

Page Used by
mermaid-light Mermaid (light theme)
mermaid-dark Mermaid (dark theme)
excalidraw Excalidraw (both themes)
drawio Draw.io (both themes)

Excalidraw and Draw.io accept darkMode per-call without global mutation, so one page each is enough.

Reference counting

Every render calls pool.acquire() and pairs it with pool.release() in a try/finally block. The pool tracks outstanding references and:

  • Coalesces concurrent acquire() calls so only one browser launch happens under racing renders.
  • Sets an idle timer when the last reference is released. If no new acquire() arrives within 5 s, the pool disposes the browser. The timer is cancelled when a new render arrives first.

Cleanup

dispose() is wired to SIGINT, SIGTERM, and exit. It closes pages first, then the browser context, then the browser itself. Callers that manage their own runtime should use createRendererRuntime() to get an isolated pool and call runtime.dispose() explicitly.

Adding a new browser-backed engine

See the add-diagram-type skill — step 4 covers adding a new page method to BrowserPool.