Understanding preserveDrawingBuffer in WebGL
This article explains the purpose, technical mechanics, and practical
implications of the preserveDrawingBuffer attribute in
WebGL. It covers how this configuration affects frame rendering, why it
defaults to disabled, the performance trade-offs of enabling it, and the
specific scenarios—such as taking canvas screenshots or creating drawing
applications—where it becomes essential.
What Is preserveDrawingBuffer?
When initializing a WebGL context using
canvas.getContext('webgl', options) or
canvas.getContext('webgl2', options), you can pass a
configuration object containing the boolean attribute
preserveDrawingBuffer.
By default, preserveDrawingBuffer is set to
false.
// Default behavior
const gl = canvas.getContext('webgl', { preserveDrawingBuffer: false });
// Preserving the buffer
const gl = canvas.getContext('webgl', { preserveDrawingBuffer: true });Default Behavior
(preserveDrawingBuffer: false)
In default WebGL operation, the browser clears the drawing buffer automatically after compositing the canvas content with the rest of the web page.
Once the current execution frame finishes rendering and the
compositor displays the frame, the underlying color buffer is discarded
or swapped. If code outside the immediate rendering loop attempts to
read the canvas content—such as via canvas.toDataURL() or
canvas.toBlob()—the output is usually a completely blank,
black, or transparent image.
The primary benefit of this default behavior is performance. It allows the GPU and browser compositor to utilize efficient swap-chain and double-buffering mechanisms without copying pixel data from frame to frame. Mobile devices and integrated GPUs particularly benefit from this optimization, as it conserves memory bandwidth and battery life.
Setting
preserveDrawingBuffer: true
Setting preserveDrawingBuffer to true
instructs the browser not to automatically clear the color buffer.
Instead, the drawing buffer retains its pixel values until you
explicitly clear them using gl.clear() or overwrite them
with subsequent draw calls.
This setting is significant for two primary use cases:
- Canvas Captures and Screenshots: If you need to
generate images from the canvas at arbitrary times (for example,
triggered by a user clicking a "Save Image" button), setting
preserveDrawingBuffer: trueensures that the rendered content remains intact and accessible to methods liketoDataURL(). - Cumulative Rendering: Applications such as drawing tools, particle trails, or iterative simulations often rely on drawing new content directly on top of the previous frame without clearing the background.
Performance Trade-Offs
While convenient, setting preserveDrawingBuffer: true
introduces potential performance penalties:
- Memory Copy Overhead: The browser may be forced to copy the drawing buffer to an internal texture for compositing rather than simply flipping buffer pointers.
- Tile-Based Deferred Rendering (TBDR) Inefficiencies: Mobile GPUs frequently use tile-based rendering architectures. Preserving buffers prevents the GPU from discarding tile memory, requiring extra memory read and write cycles.
- Higher Memory Footprint: The browser must maintain dedicated memory structures to preserve the frame data indefinitely.
Recommended Best Practices
For optimal performance, leave preserveDrawingBuffer set
to false whenever possible:
- For Screenshots: Keep the attribute
false, but callcanvas.toDataURL()synchronously inside the samerequestAnimationFramecallback immediately after your draw calls, before execution returns to the browser compositor. - For Persistent Drawing or Accumulation: Consider rendering to an offscreen Framebuffer Object (FBO) backed by a texture. This gives you full control over buffer persistence without forcing the presentation buffer to incur compositing penalties.