How We Cut Our Browser Video Renderer's Frame Time by 80%
A 30-second product promo — 1,050 frames at 1920×1080 — was rendering at roughly one frame per second. In the browser, on a machine that plays modern games without blinking. The same job on our headless render server took 10 minutes.
This is the writeup of how we found four distinct problems, fixed them in a single commit, and got scene medians down from ~0.5–1.0s to ~90–165ms per frame — with pixel-level proof that the output didn't change. Well, almost: the verification pass also caught a real bug.
The renderer in 60 seconds
Our engine renders a video by drawing every frame onto a canvas, then feeding the bitmaps to a WebCodecs VideoEncoder (H.264 in MP4). Layers are DOM elements — text, images, shapes with real CSS — so rasterizing a layer means calling domToCanvas from modern-screenshot, which serializes the element's subtree into an SVG <foreignObject> and rasterizes it. Effect layers additionally go through a WebGL compositor for blur, distortion, and CSS-filter passes. The exact same bundle runs in two places: your browser tab for preview and local export, and headless Chromium on our Railway workers for API renders.






