The Problem: Slow Browser Video Rendering

Rendering a 30-second, 1920x1080 video promo, comprising 1,050 frames, was agonizingly slow. The browser-based video renderer was struggling, producing approximately one frame per second. This performance bottleneck was particularly jarring given that the same machine could effortlessly handle modern games. The disparity was stark: a headless render server completed the same task in 10 minutes, while the browser version crawled at a rate that made it impractical for real-time previews or rapid iteration.

The team identified four critical issues contributing to this poor performance. Addressing these problems in a single commit led to a dramatic improvement, reducing scene medians from a sluggish 0.5–1.0 second per frame to a much more manageable 90–165 milliseconds. Crucially, pixel-level verification confirmed that the visual output remained consistent, with the optimization pass even uncovering a previously unnoticed rendering bug.

Diagram illustrating the browser video rendering pipeline before optimization

Understanding the Renderer's Workflow

The engine constructs a video by rendering each individual frame. This involves drawing the visual content onto an HTML canvas. The resulting bitmaps are then encoded using the WebCodecs API, specifically targeting a VideoEncoder, typically H.264 for MP4 output. The complexity arises from how layers are handled: each layer is essentially a DOM element. This means that rendering a single layer requires rasterizing DOM elements, a process that involves calling functions like domToCanvas. The subsequent composition of these rasterized layers into a final frame, and then encoding that frame, forms the core of the rendering pipeline. Each step in this process, from DOM manipulation to canvas drawing and final encoding, presented opportunities for optimization.

The Four Bottlenecks Identified

The investigation pinpointed four primary areas where performance was being unnecessarily sacrificed:

  • Excessive DOM Manipulation: The renderer was inadvertently triggering expensive DOM reflows and recalculations for elements that did not actually change. This was akin to re-tiling an entire floor when only one tile needed replacing.
  • Inefficient Canvas Drawing Operations: Certain canvas drawing commands were being used in ways that were not optimal. This included redundant clearing operations and overly complex drawing paths that could be simplified.
  • Suboptimal WebCodecs Configuration: The parameters used for the VideoEncoder were not tuned for the specific content or target frame rate. Defaults that worked for general use were penalizing this specialized rendering task.
  • Unnecessary Layer Re-rasterization: The system was re-drawing layers that had not changed since the previous frame. This was a significant waste of CPU cycles, especially for static elements within a scene.

The Fixes: A Unified Commit

The team implemented a series of targeted fixes, addressing each bottleneck. The approach was to ensure that changes were minimal and verifiable, particularly to avoid regressions in visual quality.

Optimizing DOM Interactions

The first major fix involved a more intelligent approach to DOM manipulation. Instead of blindly re-rendering or re-calculating layout for all layers on every frame, the system was modified to track changes. Only layers that had actually been modified since the last frame were subjected to the expensive domToCanvas process. This required implementing a change detection mechanism that could accurately identify modified DOM elements. This is similar to how a version control system tracks file changes; only altered files are processed.

Streamlining Canvas Operations

Further optimizations were made to the canvas drawing routines. This included:

  • Reducing redundant canvas clearing operations. In many cases, only the modified parts of the canvas needed to be redrawn, rather than clearing the entire canvas and redrawing everything.
  • Simplifying complex drawing paths. Where possible, intricate vector paths were simplified or replaced with more efficient drawing primitives.
  • Leveraging hardware acceleration where available. Ensuring that canvas operations were taking advantage of GPU acceleration provided by the browser.

Tuning WebCodecs Parameters

The configuration for the VideoEncoder was refined. Instead of relying on generic settings, the parameters were tailored to the specific needs of the video promo. This involved adjusting bitrate, keyframe intervals, and other encoding profiles to balance quality and performance for the H.264 codec. The goal was to find the sweet spot that minimized file size and encoding time without introducing noticeable artifacts. This is akin to selecting the right tool for a specific job, rather than using a general-purpose hammer for every task.

Preventing Redundant Re-rasterization

The most impactful change was the introduction of a caching mechanism for rendered layers. Before re-rasterizing a layer, the system now checks if the layer's content or properties have changed. If a layer remains static from one frame to the next, its previously rendered bitmap is reused, completely bypassing the expensive DOM-to-canvas conversion. This is particularly beneficial for videos with static backgrounds, text overlays, or logos that persist across many frames.

Code snippet showing the change detection logic for DOM layers

Verification and Bug Discovery

A critical part of the process was verification. The team developed a pixel-by-pixel comparison tool to ensure that the optimized output was visually identical to the original. This rigorous check validated the effectiveness of the optimizations. During this verification phase, the tool uncovered a subtle bug: a specific CSS filter effect was not being applied correctly in the original renderer, leading to minor visual discrepancies that were masked by the overall slow performance. The fix for this bug was included alongside the performance optimizations, demonstrating the value of a robust verification process.

The Outcome: A 5x to 10x Speedup

The results were dramatic. The frame time median dropped from approximately 0.5–1.0 seconds to 90–165 milliseconds. This represents an improvement of roughly 5x to 10x, effectively cutting the frame time by up to 80%. This transformation turned an impractical rendering process into one that is now significantly faster and more efficient within the browser environment. The ability to iterate quickly on video assets directly in the browser is a substantial productivity gain for creators and editors.