fix(mapbox,google-maps): use css-dpr pixel sizing in overlaid mode to prevent basemap misalignment - #10370
Conversation
… prevent basemap misalignment At fractional browser zoom levels, luma.gl's exact device-pixel-content-box sizing diverges from the Math.round(css * dpr) approach used by Mapbox GL, MapLibre, and Google Maps, causing visible drift between the deck.gl overlay canvas and the basemap. Pass pixelSizeSource: 'css-dpr' when creating the canvas context in overlaid (non-interleaved) mode so luma.gl derives pixel sizes using the same rounding as the underlying basemap. Depends on visgl/luma.gl#2588 (pixelSizeSource prop, merged to master, pending release). Closes #10173 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…release The pixelSizeSource prop is available in luma.gl >9.3.3 (visgl/luma.gl#2588) but not yet in the currently pinned @luma.gl/core@9.3.3 types. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
luma.gl 9.3.4 ships the pixelSizeSource prop (visgl/luma.gl#2588), so the ts-expect-error suppression is no longer needed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Update yarn.lock to pull in @luma.gl/core 9.3.5 which includes the css-dpr observer box fix (visgl/luma.gl#2676) - Add test/apps/fractional-zoom-alignment for manual verification of issue #10173 (basemap misalignment at fractional browser zoom) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…uation Resolved conflicts: took @loaders.gl ^4.4.3 (from master) and @luma.gl ^9.3.4 (from this branch). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…mode Add unit tests confirming that MapboxOverlay and GoogleMapsOverlay pass pixelSizeSource: 'css-dpr' through deviceProps.createCanvasContext when creating their own Deck instance, ensuring the luma.gl canvas context uses content-box sizing to stay aligned with the basemap. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…uation Resolved conflicts: took @luma.gl ^9.4.0-alpha.1 from master. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| // The basemap owns the shared canvas in interleaved mode; Deck only forwards the preferred DPR. | ||
| // In non-interleaved mode this still feeds the luma canvas context that Deck creates. | ||
| useDevicePixels: props.useDevicePixels ?? true, | ||
| ...(!props.gl && { |
There was a problem hiding this comment.
This is pretty crazy that we have to write nested code like this just to override or pass a prop.
A result of the unfortunate prop type overloading in luma CanvasContext.
I will see if we can propose improved props in luma.gl
There was a problem hiding this comment.
😬 yeah.. I held my breath a bit pushing this one up. Lmk if you figure something out
There was a problem hiding this comment.
Left comments on the RFC, thanks for following up!
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 9f47e2b. Configure here.
… prevent basemap misalignment (#10370) * fix(mapbox,google-maps): use css-dpr pixel sizing in overlaid mode to prevent basemap misalignment At fractional browser zoom levels, luma.gl's exact device-pixel-content-box sizing diverges from the Math.round(css * dpr) approach used by Mapbox GL, MapLibre, and Google Maps, causing visible drift between the deck.gl overlay canvas and the basemap. Pass pixelSizeSource: 'css-dpr' when creating the canvas context in overlaid (non-interleaved) mode so luma.gl derives pixel sizes using the same rounding as the underlying basemap. Depends on visgl/luma.gl#2588 (pixelSizeSource prop, merged to master, pending release). Closes #10173 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(mapbox): add ts-expect-error for pixelSizeSource pending luma.gl release The pixelSizeSource prop is available in luma.gl >9.3.3 (visgl/luma.gl#2588) but not yet in the currently pinned @luma.gl/core@9.3.3 types. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: bump @luma.gl to 9.3.4 and remove ts-expect-error workaround luma.gl 9.3.4 ships the pixelSizeSource prop (visgl/luma.gl#2588), so the ts-expect-error suppression is no longer needed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: bump @luma.gl to 9.3.5 and add fractional-zoom test app - Update yarn.lock to pull in @luma.gl/core 9.3.5 which includes the css-dpr observer box fix (visgl/luma.gl#2676) - Add test/apps/fractional-zoom-alignment for manual verification of issue #10173 (basemap misalignment at fractional browser zoom) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test(mapbox,google-maps): verify pixelSizeSource css-dpr in overlaid mode Add unit tests confirming that MapboxOverlay and GoogleMapsOverlay pass pixelSizeSource: 'css-dpr' through deviceProps.createCanvasContext when creating their own Deck instance, ensuring the luma.gl canvas context uses content-box sizing to stay aligned with the basemap. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

Summary
pixelSizeSource: 'css-dpr'to the luma.gl canvas context in overlaid (non-interleaved) mode for bothMapboxOverlayandGoogleMapsOverlayfractional-zoom-alignmentVite app for side-by-side manual verificationBefore
https://github.com/user-attachments/assets/2f68a865-fb81-40db-903b-5a1b86f0336b
After
https://github.com/user-attachments/assets/71145289-b5e2-4968-b2a7-feef35affb78
luma.gl dependency
The base branch already uses
@luma.gl/* ^9.4.0-alpha.4, which includes thecss-dprcanvas sizing support and the follow-up fix to observecontent-boxand useMath.floor(visgl/luma.gl#2676). This PR does not change luma.gl dependencies.Impact by environment
pixelSizeSourceremains'exact'width: null, height: null, map owns canvasglprop present,devicePropsnot injectedRoot cause
At fractional browser zoom,
window.devicePixelRatiobecomes non-integer. Mapbox/MapLibre compute canvas pixel size asMath.floor(cssWidth * dpr)(via implicit truncation), while exactdevice-pixel-content-boxsizing can report a different physical pixel count. This can give the deck.gl overlay and basemap different drawing buffer dimensions, causing visible misalignment.Using luma.gl's
css-dprmode observes the CSS content box and derives the pixel size with the same floor-based calculation as the basemap.Test plan
pixelSizeSource: 'css-dpr'for overlaid Mapbox and non-interleaved Google MapsCloses #10173
🤖 Generated with Claude Code
Note
Medium Risk
Changes canvas sizing for all overlaid Mapbox/MapLibre and non-interleaved Google Maps integrations; standalone Deck and interleaved mode are unchanged. The required luma.gl behavior is already present on the base branch.
Overview
Overlaid basemap overlays now pass
pixelSizeSource: 'css-dpr'into luma’screateCanvasContextso Deck’s canvas buffer is sized like Mapbox/MapLibre/Google (CSS size × DPR), instead of relying on exactdevice-pixel-content-boxsizing that can disagree at fractional browser zoom.MapboxOverlayapplies this whenever Deck owns its own canvas (default overlaid mode).GoogleMapsOverlaydoes the same only when no externalglis supplied (non-interleaved); interleaved WebGL sharing is unchanged. UserdeviceProps/createCanvasContextare merged so callers can still extend options.Adds a fractional-zoom-alignment Vite test app (Mapbox + diagnostic panel) and unit tests asserting
css-dpron overlaid Mapbox and non-interleaved Google Maps Deck creation.Reviewed by Cursor Bugbot for commit 9f47e2b. Bugbot is set up for automated code reviews on this repo. Configure here.