Repository navigation
[Bug]: preview_snapshot renders backdrop-filter as mirrored tiles at viewports wider than 640px #20899
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/server
Steps to reproduce
- Serve the fixture below locally (
python3 -m http.server). It is a page with a fixed overlay usingbackdrop-filter: blur(4px)and a dialog on top. - Have an agent call
preview_openon it with the preview panel hidden, so the tab runs headless, and keep the default 1280×800 viewport. - Call
preview_snapshotwithsave: trueand open the PNG. preview_resizeto freeform 640×400 and snapshot again.
Fixture
<!doctype html>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { margin: 0; font: 16px Arial; color: #171717; background: white; }
aside { position: fixed; inset: 0 auto 0 0; width: 240px; padding: 24px; background: #f4f4f5; }
main { margin-left: 288px; padding: 24px; }
.row { display: flex; gap: 12px; margin-bottom: 12px; }
.cell { flex: 1; padding: 12px; border: 1px solid #d4d4d8; }
.green { color: #16a34a; } .red { background: #fecaca; }
.overlay { position: fixed; inset: 0; background: rgb(0 0 0 / 0.1);
backdrop-filter: blur(4px); display: grid; place-items: center; }
.dialog { width: 480px; padding: 24px; background: white; border-radius: 12px;
box-shadow: 0 10px 30px rgb(0 0 0 / 0.2); }
</style>
<aside><p>Sidebar item one</p><p>Sidebar item two</p><p>Sidebar item three</p><p>Sidebar item four</p></aside>
<main>
<h1>Page behind the dialog</h1>
<div class="row"><div class="cell">Alpha</div><div class="cell green">Bravo</div><div class="cell red">Charlie</div></div>
<div class="row"><div class="cell">Delta</div><div class="cell green">Echo</div><div class="cell red">Foxtrot</div></div>
<div class="row"><div class="cell">Golf</div><div class="cell green">Hotel</div><div class="cell red">India</div></div>
</main>
<div class="overlay"><div class="dialog"><h2>Dialog</h2><p>The page behind should be one blurred copy.</p></div></div>Expected behavior
The area behind the dialog is one blurred copy of the page, as Playwright's own page.screenshot of the same page at 1280×800 shows it: three columns, the red one on the right.
Actual behavior
At the default 1280×800 viewport, the backdrop holds only the page's top-left quarter (640×400 CSS px), mirrored horizontally and vertically to fill the screen. The heading reads "Page behind the dialaib eht bnided egaP" and the red column is missing. The dialog itself and everything outside the overlay render correctly.
At 640×400 the same page snapshots correctly. A preview_recording of the 1280×800 tab also renders the backdrop correctly.
The snapshot path appears to be the cause. snapshot() computes scale = min(1, MAX_SCREENSHOT_WIDTH / (viewport.width * renderScale)) (ServerBrowserPage.ts:415), so any viewport wider than 640 CSS px gets scale < 1. captureViewport then sends Page.captureScreenshot with a scaled clip (L380-L396), under --disable-gpu (ServerBrowserContexts.ts:90). The backdrop reads back only the scaled-size region, and the blur's mirrored edge mode fills the rest. That is inferred from the output, not traced in Chromium. Every path that captures at scale 1 is correct: a viewport of 640 px or narrower, recordings (scale: 1, no clip), and Playwright's page.screenshot.
One fix that avoids the clip scale: capture at scale 1, then downscale the PNG to MAX_SCREENSHOT_WIDTH afterwards.
This is separate from #16690 (a desktop-drawn tab's PNG is sized by the display DPR) and #9872 (detail follows the panel's on-screen size). Here the tab is headless, the PNG is the expected 1280×800, and the pixels inside a backdrop-filter are wrong. It affects any page with a modal over a blurred backdrop, which is common in shadcn/Radix-style apps. An agent checking such a dialog sees broken rendering that isn't in the app.
Impact
Major degradation or frequent failure
Version or commit
0.1.1-nightly.20261016.3218 (e58a4857130a); the same code is on main at 4d679a63244e.
Environment
Ubuntu 24.04.5, bundled local server, headless tab (panel hidden), chrome-headless-shell 154.0.8037.92. Captures were taken by an agent through the T3 MCP preview tools.
Logs or stack traces
No response
Screenshots, recordings, or supporting files
preview_snapshot, default 1280×800 viewport: backdrop tiled and mirrored.
preview_snapshot, 640×400 viewport: correct.
- Playwright
page.screenshot, 1280×800, same page: expected.
Workaround
Snapshot at a viewport of 640 CSS px wide or narrower, or take a frame from a preview_recording. Neither gives a snapshot of a desktop-width layout.
Written by Claude Opus 5.5 on behalf of leomontigatti.
Activity
juliusmarminge commentedon Oct 17, 2026
Note
Grok responding on behalf of Julius.
Confirmed on main, with one correction:
MAX_SCREENSHOT_WIDTHis 1280 output pixels, not 640. Server tabs render atRENDER_SCALE = 2(apps/server/src/preview/ServerBrowser.ts), so the threshold works out to 640 CSS px.capture()inapps/server/src/preview/ServerBrowserPage.tscomputesscale = min(1, 1280 / (viewport.width * 2)). A 1280×800 tab gets 0.5.captureViewport()passes that toPage.captureScreenshotasclip.scalewhenever it's below 1. At 640 px or narrower, no clip is sent.--disable-gpu(ServerBrowserContexts.ts), which means software compositing. Software compositing drawingbackdrop-filterwrong in a scaled capture fits your result.preview_snapshotonly takes a tab target,includeImageandsave, so there's no full-resolution option to use instead. This isn't a duplicate of #16690, #18593 or #9872, and #19913 only changes desktop-drawn tabs.Workarounds until it's fixed:
clip.scale.preview_recordingframe. As you found, it captures correctly.docs: track the preview capture faults upstream, with their cause (#7)
watashiwaaniket commentedon Oct 17, 2026
can I get issue assigned to me? I am currently working on the fix. Thank you