Skip to content

Animated WebGL canvas escapes border-radius/clip-path in Firefox (layer compositor overlay) #288

Description

@RobertoVillegas

Summary

In Firefox with the WebRender layer compositor (hardware overlay promotion), an animated WebGL canvas escapes border-radius, overflow: hidden, and clip-path — including its own border-radius — after its fade-in completes. The corners of the container are painted over with a square canvas.

Frozen canvases (one frame) are unaffected. The bug is not in this library, but this library is an easy way to hit it: any animated shader inside a rounded container.

Environment

  • Firefox (macOS, GPU compositing active — WebRender layer compositor / native surfaces)
  • @paper-design/shaders-react 0.0.80

Steps to reproduce

  1. Open Firefox on macOS with hardware acceleration enabled (layer compositor active).
  2. Render an animated shader (speed > 0) inside a container with border-radius and overflow: hidden, with a fade-in (opacity transition) on a wrapper.
  3. Wait for the fade-in to complete.

Minimal repro without this library (same mechanism):

<!doctype html>
<style>
  body { background: #faf8f1; }
  .shell { width: 600px; height: 400px; border-radius: 40px; overflow: hidden; background: #111; }
  .wrap { height: 100%; opacity: 1; transition: opacity 800ms; }
  canvas { width: 100%; height: 100%; display: block; border-radius: inherit; }
</style>
<div class="shell"><div class="wrap"><canvas id="c"></canvas></div></div>
<script>
  const gl = document.getElementById("c").getContext("webgl2");
  requestAnimationFrame(function frame(t) {
    const s = 0.5 + 0.5 * Math.sin(t / 1000);
    gl.clearColor(0.2 * s, 0.15 * s, 0.5 * s, 1);
    gl.clear(gl.COLOR_BUFFER_BIT);
    requestAnimationFrame(frame);
  });
</script>

Expected

Corners stay rounded.

Actual

Once the fade-in finishes, the canvas paints as a square over the corner (verified by pixel measurement: canvas-colored pixels 3px from the corner vertex, shader pattern visible inside the cut region).

Why only the animated shader

  • The only canvas that breaks on our page is the animated one (speed: 0.35). Frozen shaders (speed: 0 + fixed frame, e.g. the footer blend layers) are rasterized once and composited as normal images, so ancestor clips always apply.
  • While the fade-in runs (opacity < 1), the subtree renders into an intermediate surface and clips apply. When opacity reaches 1, Firefox promotes the continuously-presenting canvas to a hardware overlay, which ignores rounded clips.

Root cause (Firefox)

  • Bug 2002563 — "Video content inside position:fixed container ignores border-radius when using new layer compositor" (VERIFIED FIXED, Firefox 148, Dec 2025). Same mechanism for video overlays; the fix was "Add overlay's rounded clip rects handling to WebRender layer compositor".
  • Bug 2005103 — follow-up: "Use overlay with video rendering with rounded rects if possible".
  • Bug 1958603 — WebGL/WebGPU overlay support (IDCompositionTexture) landed Jan 2026, after the video rounded-clip fix.
  • Bug 2038378 — "Re-enable WebGL/WebGPU overlay on Windows" (open): WebGL overlay was disabled again on Windows.

So the WebGL overlay path appears to lack the rounded-clip handling that video overlays got in Firefox 148.

Workaround

Keep the shader wrapper's opacity slightly below 1 (e.g. 0.9999) so the subtree always composites through an intermediate surface and never gets promoted to an overlay. Visually indistinguishable; confirmed working.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions