Skip to content

Fix Chrome window.innerWidth priority for correct canvas resizing#8122

Closed
ayushman1210 wants to merge 9 commits into
processing:mainfrom
ayushman1210:fix/chrome-window-innerwidth
Closed

Fix Chrome window.innerWidth priority for correct canvas resizing#8122
ayushman1210 wants to merge 9 commits into
processing:mainfrom
ayushman1210:fix/chrome-window-innerwidth

Conversation

@ayushman1210

Copy link
Copy Markdown
Contributor

Resolves #8121

Changes:

  • Modified the getWindowWidth() and getWindowHeight() functions in environment.js to prioritize document.documentElement.clientWidth and document.documentElement.clientHeight over window.innerWidth and window.innerHeight.
  • This resolves an issue in Chrome on Android where windowWidth and windowHeight values do not update correctly when the device is rotated between portrait and landscape orientation.
  • Updated logic ensures consistent behavior across mobile browsers while maintaining backward compatibility with desktop browsers.

PR Checklist

  • npm run lint passes
  • Inline reference is included / updated
  • Unit tests are included / updated

@welcome

welcome Bot commented Oct 3, 2025

Copy link
Copy Markdown

🎉 Thanks for opening this pull request! For guidance on contributing, check out our contributor guidelines and other resources for contributors!
🤔 Please ensure that your PR links to an issue, which has been approved for work by a maintainer; otherwise, there might already be someone working on it, or still ongoing discussion about implementation. You are welcome to join the discussion in an Issue if you're not sure!
🌸 Once your PR is merged, be sure to add yourself to the list of contributors on the readme page !

Thank You!

@eyaler

eyaler commented Oct 3, 2025

Copy link
Copy Markdown

@ayushman1210 Thanks! I think innerWidth should be before body.clientWidth as the body dimensions reflect the content and not the viewport. Also, the suggested change may have issue when scrollbars are present (in Chrome they are part of the innerWidth but not of the documentElement.clientWidth) - I did not look into this yet.

@ayushman1210

Copy link
Copy Markdown
Contributor Author

Hi @eyaler ,

Thanks for the feedback! I’ve updated the code so that window.innerWidth and window.innerHeight are checked first, before document.body.clientWidth and document.documentElement.clientWidth/Height. This ensures the viewport dimensions are captured correctly and also handles scrollbars properly in Chrome.

Thanks
Ayushman

@perminder-17 perminder-17 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain what's the reason behind removing all the docs? I think you need to fix the PR with the minimal changes whatever needed.

@eyaler

eyaler commented Oct 4, 2025

Copy link
Copy Markdown

@ayushman1210 i don't get it. this was the state of things before with the issue that innerWidth may be wrong, no? my suggestion was that document.documentElement.clientWidth will be used first (but i think we need more feedback before making this change)

@ayushman1210

Copy link
Copy Markdown
Contributor Author

@eyaler
good catch and thanks for the feedback.
Quick recap of what I changed so far

  • I fixed the timing issue by deferring the resize handler to the next animation frame so layout has settled after rotation (so windowResized() sees the post-rotation viewport).

  • I also updated getWindowWidth() / getWindowHeight() to prefer visualViewport (when available) and then fall back to other viewport measures

About your suggestion (use [document.documentElement.clientWidth] first )

  • I agree that preferring [document.documentElement.clientWidth] avoids surprising inclusion of scrollbar width in many desktop Chrome cases — that was exactly your point.

The tradeoffs:

  • [documentElement.clientWidth] tends to reflect the CSS/layout viewport and avoids scrollbars, which is often what sketches expect.

  • [window.innerWidth] includes scrollbars on some browsers; that can make sketch sizing look different when scrollbars appear.

  • [window.visualViewport] (when available) is still the best for mobile cases (on-screen keyboard, pinch-zoom) so I kept it as the top preference.

Because timing of which value updates when can vary across browsers, I left the resize handler deferred to rAF so whichever source we pick will be read after layout has updated.

Proposed conservative change (what I can implement now)

Use the following order in [getWindowWidth()] / [getWindowHeight()] :
[window.visualViewport (when available)
[document.documentElement.clientWidth]/ [clientHeight]
[window.innerWidth] / [innerHeight]
[document.body.clientWidth]/ [clientHeight]
This matches your suggestion while preserving [visualViewport] for mobile edge cases.

Questions / next steps

Is that ordering acceptable to you (visualViewport -> documentElement -> innerWidth -> body)?
If yes I’ll update the PR to use this ordering and push the change.
If you prefer a different rule (e.g., a heuristic that switches based on scrollbar presence, or exposing a small opt-in API to choose the preference), tell me and I’ll implement that instead.

Thanks
Ayushman

@eyaler

eyaler commented Oct 4, 2025

Copy link
Copy Markdown

thanks @ayushman1210

* I fixed the timing issue by deferring the resize handler to the next animation frame so layout has settled after rotation (so windowResized() sees the post-rotation viewport).

I coundn't find the added requestAnimationFrame in your PR, and in my own testing requestAnimationFrame did not solve the issue. Can you point me to the commit/line?

* I also updated getWindowWidth() / getWindowHeight() to prefer visualViewport (when available) and then fall back to other viewport measures

In my testing visualViewport does not include scrollbars just as documentElement.clientWidth, and also is wrong when rotating back just as innerWidth. I did find viewport.segments[0].width that seems to both include scrollbars and be correct after orientation change but that is still experimental and has partial support (and i don't have a good enough understanding of it)

* I agree that preferring [document.documentElement.clientWidth] avoids surprising inclusion of scrollbar width in many desktop Chrome cases — that was exactly your point.

On the contrary, I actully think scrollbar should be included if possible

…able

Prefer experimental visualViewport.segments[0] and visualViewport before window.inner* so measurements include scrollbars and handle orientation changes when available. Guard experimental access and fall back to documentElement.client* for compatibility.
@ayushman1210

Copy link
Copy Markdown
Contributor Author

@eyaler Hi maintainer !!
I pushed a change on branch fix/chrome-window-innerwidth (commit 4b5ee8b) that adjusts how p5 computes viewport size in [environment.js]: it now prefers [visualViewport.segments[0]] (when available), then [window.visualViewport] then [window.inner*] (so scrollbars are included when possible), and finally falls back to [document.documentElement.client*]and [document.body.client*]). Experimental accesses are guarded with try/catch.
I ran the full test suite locally (all tests passed). Please let me know if you'd prefer excluding the experimental [segments] usage or a different fallback order — I can update quickly.

Thanks
ayushman

@eyaler

eyaler commented Oct 5, 2025

Copy link
Copy Markdown

@ayushman1210 sorry but i am not a maintainer... btw, i still couldn't find where you used requestAnimationFrame

@ayushman1210

Copy link
Copy Markdown
Contributor Author

@eyaler
You're absolutely right about the scrollbar issue. I've removed visualViewport.width from the fallback chain to avoid the inconsistency.

Updated implementation:

  • Priority 1: visualViewport.segments[0] (experimental foldable/dual-screen API)
  • Priority 2: window.innerWidth (includes scrollbars, matches historical p5.js behavior)
  • Priority 3: document.documentElement.clientWidth (fallback when innerWidth unavailable)
  • Priority 4: document.body.clientWidth (last resort)

The segments API is only used when explicitly available (for foldable devices), then we fall back directly to innerWidth. This avoids the Chrome scrollbar issue you mentioned while still supporting dual-screen devices.

Regarding requestAnimationFrame: I apologize for any confusion - I did NOT add or modify any RAF code. My changes are limited to the getWindowWidth() and getWindowHeight() helper functions, which are only called:

  1. On initialization in _updateWindowSize()
  2. In the _onresize event handler

These functions are NOT called in the animation loop, so there's no per-frame performance impact.

Thanks
Ayushman

@ayushman1210

Copy link
Copy Markdown
Contributor Author

Hi @perminder-17
can you please Review this PR #8122 for the issue #8121

@ayushman1210

Copy link
Copy Markdown
Contributor Author

Hi @ksen0,
When you have a moment, could you please review PR #8122 ?
Thank you to all the maintainers for your time and for keeping this project running smoothly! 🙏

@perminder-17 perminder-17 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay in review, I'll be testing the changes once.

@perminder-17 perminder-17 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @ayushman1210 @eyaler , really sorry for the delay in reviewing this one.

Btw, are you still able to reproduce this issue with the latest chrome. Looks like I am unable to reproduce this issue on my android chrome.

If the issue is still reproduceable, @eyaler gave a very nice finding;

the issue seems to be with chrome window.innerWidth, so maybe in https://github.com/processing/p5.js/blob/main/src/core/environment.js#L787 the order should be changes so that innerWidth gets lower priority?

The main thing the issue needs is just changing the order in which we check the window size, checking document.documentElement.clientWidth first, before window.innerWidth, since innerWidth is the one that gives the wrong value on Chrome Android after rotating.

Comment thread src/core/environment.js
Comment on lines +785 to 819
// Prefer the visual viewport when available (better for mobile: pinch/keyboard/zoom),
// then the layout viewport, then the window inner size, then the body content size.
// Order chosen intentionally:
// 1) window.visualViewport.* -> visual viewport (mobile-friendly; adapts to on-screen keyboard)
// 2) document.documentElement.client* -> layout viewport (avoids including scrollbars on some platforms)
// 3) window.inner* -> includes scrollbars on some platforms (Chrome)
// 4) document.body.client* -> content-box fallback if the above are unavailable
// Caveat: different browsers expose subtly different viewport measurements (scrollbars, zoom,
// and visual viewport). This ordering prefers mobile-friendly measurements while retaining
// sensible fallbacks for desktop browsers.
function getWindowWidth() {
// Prefer including scrollbars when possible per contributor guidance.
// Order: experimental visualViewport.segments (may include scrollbars/handle rotation),
// visualViewport.width, window.innerWidth (includes scrollbars),
// documentElement.clientWidth (excludes scrollbars), document.body.clientWidth.
try {
if (window.visualViewport) {
const segs = window.visualViewport.segments;
if (Array.isArray(segs) && segs.length > 0) {
const w = segs[0] && typeof segs[0].width === 'number' ? segs[0].width : null;
if (w && w > 0) {
return w;
}
}

// if (typeof window.visualViewport.width === 'number' && window.visualViewport.width > 0) {
// return window.visualViewport.width;
// }
}
} catch (e) {
// experimental access may throw; ignore and continue with fallbacks
}

return (
window.innerWidth ||

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The extra visualViewport.segments part and the try/catch around it aren't really needed for this, which isn't what this bug is about. There are also a few commented-out lines left in getWindowWidth/getWindowHeight that we can remove to keep things tidy.

So it could come down to just this one line:

(document.documentElement && document.documentElement.clientWidth) || window.innerWidth || (document.body && document.body.clientWidth) || 0

@perminder-17 perminder-17 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now, I am closing this PR since I am unable to reproduce the issue. Will be reopening if we again face the same issue, but looks like the latest chrome doesn't have that issue.

Sign up for free to 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.

Rotating device to landscape and back gives wrong windowWidth after resizeCanvas

3 participants