DOMElement with a non-zero origin drifts under camera zoom (scene-level only)
Nobody has claimed this yet.
- Dominant language
- JavaScript
- Stars
- 40.3k
- Forks
- 7.2k
- PR merge metrics
- No merged PRs in 30d
Description
Version: Phaser 4.2.0 / 4.2.1 (logic also present in 3.x); WebGL & Canvas; any browser.
Description
A DOMElement added directly to the Scene with a non-zero origin (default 0.5, 0.5) drifts from its world position when camera.zoom !== 1, by exactly (originX·width, originY·height)·(1 − camera.zoom). It is sliding bottom-right as you zoom out. The same DOMElement nested in a Container does not drift, so the two render paths are inconsistent.
Minimal example
https://phaser.io/sandbox/f0eb3f05
Root cause (found by Claude Code after some digging around)
In DOMElementCSSRenderer.js, camMatrix.translate(-dx, -dy) bakes the origin offset into the CSS matrix in both branches. The parentMatrix branch then keeps transform-origin: 0% 0% (offset applied once → correct). The else branch also sets transform-origin: (100·origin)% (offset applied twice). The two cancel only when the matrix's linear scale (camera zoom) is 1; under zoom the residual is originX·width·(1 − zoom). The transform-origin percentage lines date to 2019 (v3); the March-2025 v4 refactor preserved the behavior: it typically surfaces once an app drives a real camera zoom on scene-level DOM elements. I tested on v3.80 and the bug was already present.
I'll provide a PR to fix the issue.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in DOMElementCSSRenderer.js and reproduce the scene-level case from the linked sandbox with a non-zero origin and camera zoom. Compare the parentMatrix and else branches, then verify that scene-level DOMElements remain aligned with their world position under zoom while nested elements retain their current behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript
- Domain
- game-dev
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 55/100