Skip to content

🤖 fix: avoid upscaling capped browser preview frames - #3396

Merged
coadler merged 1 commit into
mainfrom
fix-browser-preview-frame-scaling
May 27, 2026
Merged

🤖 fix: avoid upscaling capped browser preview frames#3396
coadler merged 1 commit into
mainfrom
fix-browser-preview-frame-scaling

Conversation

@coadler

Copy link
Copy Markdown
Collaborator

Summary

Avoid making capped agent-browser screencast frames blurrier by rendering Browser Preview screenshots at their intrinsic captured bitmap size instead of always stretching them to fill the preview pane.

Background

Issue #3115 reports that Browser Preview becomes blurry at high viewport resolutions. Mux receives already-encoded frames from agent-browser, so it cannot restore detail lost by upstream capture caps. This change keeps Mux from further degrading capped streams by upscaling the received bitmap.

Implementation

  • Track the loaded screenshot's natural dimensions per browser session.
  • Center the screenshot with max bounds rather than forcing full width/height object-contain scaling.
  • Map pointer coordinates through the actual rendered frame rectangle so clicks, wheels, and drag moves still land correctly when the frame is letterboxed.

Validation

  • bun test src/browser/features/RightSidebar/BrowserTab/BrowserViewport.test.tsx
  • nix shell --extra-experimental-features 'nix-command flakes' nixpkgs#shfmt nixpkgs#shellcheck nixpkgs#hadolint -c make static-check

Risks

Low-to-moderate Browser Preview risk: rendering and input mapping changed for sessions where captured frame dimensions differ from the preview panel. Tests cover capped-frame gutters and existing object-contain coordinate behavior.


Generated with mux • Model: openai:gpt-5.5 • Thinking: xhigh • Cost: 88851{MUX_COSTS_USD:-0.00}

Render browser preview frames at their intrinsic captured bitmap size when agent-browser provides lower-resolution screencast frames, and keep pointer mapping aligned with the rendered frame bounds.
---
_Generated with `mux` • Model: `openai:gpt-5.5` • Thinking: `xhigh` • Cost: `87963{MUX_COSTS_USD:-0.00}`_
<!-- mux-attribution: model=openai:gpt-5.5 thinking=xhigh costs=0.00 -->
@coadler

Copy link
Copy Markdown
CollaboratorAuthor

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@coadler
coadler added this pull request to the merge queueMay 27, 2026
Merged via the queue into main with commit f1d0763May 27, 2026
24 checks passed
@coadler
coadler deleted the fix-browser-preview-frame-scaling branch May 27, 2026 03:56
yermakoffivan pushed a commit to yermakoffivan/mux that referenced this pull request Aug 22, 2026
## Summary
Fix the Browser Preview panel rendering pixelated/blurry on HiDPI
(Retina) displays by capping the rendered frame at `naturalSize /
devicePixelRatio` CSS pixels, so one stream bitmap pixel is never
stretched past one physical display pixel.
Fixescoder#3115
## Background
Browser Preview frames come from agent-browser's stream server, which
captures via CDP `Page.startScreencast`. That capture is bounded by the
remote session's CSS viewport size (in DIPs): `deviceScaleFactor` never
raises the bitmap above CSS resolution, and older agent-browser versions
additionally capped frames at 1280x720. Xum is a passive session viewer
and cannot increase capture resolution.
PR coder#3396 stopped upscaling frames beyond their intrinsic size in CSS
pixels. On a HiDPI display that is still up to `devicePixelRatio`x
physical upscaling: a 1280x720 bitmap rendered at 1280 CSS px covers
2560 physical px on a 2x Mac, which is exactly the "renders at 1x DPI
and gets scaled up" blur reported in the issue.
## Implementation
- New `useDevicePixelRatio` hook (`useSyncExternalStore` + a
self-re-arming `matchMedia` resolution listener) so the cap tracks
monitor moves and zoom changes.
- `BrowserViewport` caps the frame `<img>` with inline
`max-width`/`max-height: min(100%, naturalSize/dpr px)`. A
smaller-but-crisp centered preview is preferred over fill-and-blur; at
1x displays behavior is unchanged.
- Pointer-input mapping (`mapDomPointToViewport`/`getRenderedFrameRect`)
mirrors the cap (`min(1, 1/dpr)` scale bound) so clicks, drags, and
wheel events land correctly; the metadata-only fallback branch is
untouched.
## Validation
- `bun test src/browser/features/RightSidebar/BrowserTab/` (58 pass,
including new DPR mapping + component cap tests)
- `make static-check`
- Remote dogfood UAT on this exact head (PASS): at emulated dpr=2 the
img caps at `min(100%, 1080px)` for a 2160px frame and letterboxes
crisply when the cap binds; at dpr=1 behavior is unchanged; click
mapping accurate to ~2px, gutter clicks ignored, wheel + typing
forwarded.
## Risks
Low. Display sizing and input mapping change only when `devicePixelRatio
> 1`; capture side and bridge protocol untouched.
---
_Generated with `xum` • Model: `anthropic:claude-fable-5` • Thinking:
`xhigh`_
<!-- xum-attribution: model=anthropic:claude-fable-5 thinking=xhigh -->
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@coadler