---
title: "Fixing Chromium virtual backgrounds on NVIDIA + Wayland"
date: 2026-05-04
tags: [linux, chromium, nvidia, wayland, webrtc, bazzite, troubleshooting]
summary: Meet's background blur turns your video into a solid white rectangle on NVIDIA + Wayland Chromium. The cause is a Chromium ↔ NVIDIA EGL interop bug in the canvas captureStream path. Two flags work around it; pick based on monitor setup.
aliases: [chromium-virtual-background, chromium-wayland-white-frames, meet-virtual-background-nvidia]
---

Switched on background blur in a Meet call and turned into a flat white rectangle. Camera works fine without the effect. The moment any background filter (blur or virtual background) is applied, everyone on the call (me in the self-preview included) sees a solid white frame where my video should be. Reproduces in any Chromium-based browser (Chromium, Vivaldi). Firefox is fine. Setup that hits this: NVIDIA proprietary driver (tested on 595.71.05, open kernel module branch, RTX 4090), KDE Plasma 6 on Wayland ([[bazzite-overview|Bazzite]]), Flatpak Chromium 147.

## Why it breaks

Meet's virtual background pipeline runs per frame:

```
camera frame
  → MediaPipe TFLite WebGL segmentation
  → WebGL composite shader (frame + background, masked)
  → canvas.captureStream() → MediaStream
  → WebRTC encoder
```

The `captureStream()` hop has to share the WebGL output texture cross-process to the GPU process where the encoder runs. Chromium does this through its [SharedImage](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/gpu/shared_image.md) abstraction, which on Linux maps to EGLImage / dmabuf. On NVIDIA's Wayland EGL implementation, the shared image arrives at the encoder side either uninitialised or wrongly synchronised, so reads come back as all-1s. Solid white frames.

The shader runs and the composite is correct. The readback path is the broken thing.

Firefox doesn't hit this because it doesn't use ANGLE or Chromium's SharedImage. Its `captureStream` goes through a different path (often a CPU readback) that never exercises the broken NVIDIA EGL interop.

## Fix

Two flags work. Pick one based on monitor setup.

### Option 1: `--ozone-platform=x11` (preferred on a desktop with same-scale monitors)

Runs Chromium under XWayland. GPU compositing stays on, WebGL/canvas/video decode stay full-speed, PipeWire screen sharing still works (it's independent of Ozone). Cost:

- XWayland is integer-scale only. No fractional HiDPI, no proper per-monitor scaling.
- Drag-and-drop and clipboard with native Wayland apps can be slightly flaky.

Fine on a desktop with one or two monitors at the same scale. Annoying if you mix a 4K-at-200% panel with a 1080p-at-100% one.

### Option 2: `--disable-gpu-compositing` (preferred on laptops or mixed-scale monitors)

Wayland-native integration kept. Compositor runs on CPU. WebGL and canvas individual draws still hit the GPU; only the final composite is software. Cost:

- Continuous extra CPU usage.
- Less smooth scrolling on heavy pages.
- Worse battery on laptops.

Genuinely unnoticeable on a 4090 desktop. Can matter on a laptop.

### How to apply (Flatpak Chromium)

Don't edit the system .desktop file. Flatpak rewrites it on every update. Copy it to `~/.local/share/applications/` instead and edit the local copy. KDE picks up the local override automatically:

```bash
cp /var/lib/flatpak/exports/share/applications/org.chromium.Chromium.desktop \
   ~/.local/share/applications/
```

Add the chosen flag to all three `Exec=` lines (main, "New Window" action, "New Incognito Window" action). For Option 1 the main line becomes:

```ini
Exec=/usr/bin/flatpak run --branch=stable --arch=x86_64 --command=/app/bin/chromium --file-forwarding org.chromium.Chromium --ozone-platform=x11 @@u %U @@
```

Quit Chromium fully and relaunch. Verify in `chrome://gpu`: "Ozone platform" should now read `x11`, or under Option 2 "Compositing" should read software-only.

For Vivaldi the equivalent lives in `~/.config/vivaldi-stable.conf`. Same flags, see [[vivaldi-persistent-flags]] for the conf-file syntax.

## Things I tried that didn't work

For future-me, so I don't bisect them again. None of these fix it:

- `--disable-features=WebRtcVideoCaptureGpuMemoryBuffer`. Disables zero-copy GPU buffers for camera capture. The camera path is fine, the bug doesn't live here.
- `--disable-features=WebRtcVideoCaptureGpuMemoryBuffer,VaapiVideoDecoder`. Adds VA-API decode disable on top. Same outcome.
- `--use-angle=gl`. Forces ANGLE off Vulkan onto plain GL. The ANGLE backend isn't the broken thing.
- `--disable-features=WebRTCPipeWireCamera`. Falls back to V4L2 capture. Doesn't matter; camera path is fine.
- `--disable-features=Vulkan`. Kills Vulkan everywhere. The shared-image bug doesn't go through Vulkan in this configuration.
- `--disable-accelerated-2d-canvas`. Forces 2D canvas to CPU. MediaPipe outputs from a WebGL canvas, so this is the wrong canvas.

Only the two flags above cross the broken path entirely (Ozone on X11 sidesteps the NVIDIA Wayland EGL path; `--disable-gpu-compositing` forces a CPU readback that doesn't need shared images at all).

## The real fix lives upstream

This is a Chromium ↔ NVIDIA Wayland EGL interop bug. Almost certainly already tracked in [issues.chromium.org](https://issues.chromium.org/). Worth searching with terms like *chromium wayland nvidia canvas captureStream white* or *MediaPipe NVIDIA Wayland virtual background* before filing anything new.

Could also end up being a Flathub packaging fix. The Chromium flatpak already pre-applies `--enable-features=WebRTCPipeWireCapturer` for exactly this kind of platform-specific compatibility patch, so a future build could ship one of these flags by default for NVIDIA users.

## Related

- [[vivaldi-persistent-flags]]. Same flags, applied via Vivaldi's conf-file mechanism.
- [[bazzite-overview]]. Why the browser is a Flatpak in the first place.
- [[flatpak-1password-browser-bridge]]. Another Flatpak browser quirk worth knowing about.

## References

- [Chromium command-line switches](https://peter.sh/experiments/chromium-command-line-switches/).
- [Chromium SharedImage design overview](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/gpu/shared_image.md).
- [Ozone platform abstraction](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/ozone_overview.md).
- [MediaPipe selfie segmentation](https://ai.google.dev/edge/mediapipe/solutions/vision/image_segmenter).
