Description
When using the mouse wheel to zoom out, the canvas scale decreases too much, sometimes jumping to very small. This happens because the zoom-out calculation depends on the current scale, making the decrease too aggressive.
For example, at 100% scale, a single scroll can drop the scale from 1 to 0, making the canvas disappear. However, zooming in (scroll up) works correctly, increasing the scale smoothly.
The issue:

Expected result
Zooming out with the mouse wheel should decrease the canvas scale incrementally and symmetrically to zooming in, but using an exponential scale approach.
For example, starting at a scale of 100%:
Scroll up: Increase exponentially by a fixed factor (e.g., 10% → 20% → 40% → 80%).
Scroll down: Decrease exponentially by the same fixed factor (e.g., 80 → 40% → 20% → 10%)
Actual result
Zooming out with the mouse wheel reduces the scale drastically in one step, proportional to the current scale, often resulting in complete zoom-out:
At 100%: Zooms out to 10%
At 50%: Zooms out to 10%
Zooming in works as expected, with exponentially increases.
Which browsers are you seeing the problem on?
Microsoft Edge
Additional details
As a reverse engineer, I decided to investigate this issue using DevTools since I really like this project—but the zooming behavior was frustrating.
Fix Implemented:
I updated wheelChange to use an exponential zooming approach, doubling the scale when zooming in and halving it when zooming out. Instead of applying a scale factor based on the current offset, the new implementation calculates the target scale and adjusts _delta accordingly.
Additionally, I added a safeguard to ensure the scale never drops below 0.1, preventing invalid values or sudden jumps to near-zero. The updated logic ensures smooth and predictable zooming behavior:
https://stately.ai/registry/_next/static/chunks/1339-d0e529723d5c15c1.js
wheelChange(e) {
let t = "uv" in e;
!t && e.cancelable && e.preventDefault();
let n = this.state;
- n._delta = [-eU(e)[1] / 100 * n.offset[0], 0],
+ const direction = -eU(e)[1] > 0 ? 1 : -1; // 1 for zoom in, -1 for zoom out
+ const newScale = direction > 0 ? n.offset[0] * 2 : n.offset[0] / 2; // Double or halve
+ n._delta = [newScale - n.offset[0], 0]; // Difference to reach target scale
+ n._delta[0] = Math.max(n._delta[0], -n.offset[0] + 0.1); // Prevent scale ≤ 0, min 0.1
eE.addTo(n._movement, n._delta),
e4(n),
this.state.origin = [e.clientX, e.clientY],
this.compute(e),
this.emit()
}
OR a oneline solution by Grok;
wheelChange(e) {
let t = "uv" in e;
!t && e.cancelable && e.preventDefault();
let n = this.state;
- n._delta = [-eU(e)[1] / 100 * n.offset[0], 0],
+ n._delta = [Math.max((-eU(e)[1] > 0 ? n.offset[0] * 2 : n.offset[0] / 2) - n.offset[0], -n.offset[0] + 0.1), 0]
eE.addTo(n._movement, n._delta);
e4(n);
this.state.origin = [e.clientX, e.clientY];
this.compute(e);
this.emit();
}
An example of the result:

Description
When using the mouse wheel to zoom out, the canvas scale decreases too much, sometimes jumping to very small. This happens because the zoom-out calculation depends on the current scale, making the decrease too aggressive.
For example, at 100% scale, a single scroll can drop the scale from 1 to 0, making the canvas disappear. However, zooming in (scroll up) works correctly, increasing the scale smoothly.
The issue:

Expected result
Zooming out with the mouse wheel should decrease the canvas scale incrementally and symmetrically to zooming in, but using an exponential scale approach.
For example, starting at a scale of 100%:
Scroll up: Increase exponentially by a fixed factor (e.g., 10% → 20% → 40% → 80%).
Scroll down: Decrease exponentially by the same fixed factor (e.g., 80 → 40% → 20% → 10%)
Actual result
Zooming out with the mouse wheel reduces the scale drastically in one step, proportional to the current scale, often resulting in complete zoom-out:
At 100%: Zooms out to 10%
At 50%: Zooms out to 10%
Zooming in works as expected, with exponentially increases.
Which browsers are you seeing the problem on?
Microsoft Edge
Additional details
As a reverse engineer, I decided to investigate this issue using DevTools since I really like this project—but the zooming behavior was frustrating.
Fix Implemented:
I updated
wheelChangeto use an exponential zooming approach, doubling the scale when zooming in and halving it when zooming out. Instead of applying a scale factor based on the current offset, the new implementation calculates the target scale and adjusts _delta accordingly.Additionally, I added a safeguard to ensure the scale never drops below 0.1, preventing invalid values or sudden jumps to near-zero. The updated logic ensures smooth and predictable zooming behavior:
https://stately.ai/registry/_next/static/chunks/1339-d0e529723d5c15c1.js
wheelChange(e) { let t = "uv" in e; !t && e.cancelable && e.preventDefault(); let n = this.state; - n._delta = [-eU(e)[1] / 100 * n.offset[0], 0], + const direction = -eU(e)[1] > 0 ? 1 : -1; // 1 for zoom in, -1 for zoom out + const newScale = direction > 0 ? n.offset[0] * 2 : n.offset[0] / 2; // Double or halve + n._delta = [newScale - n.offset[0], 0]; // Difference to reach target scale + n._delta[0] = Math.max(n._delta[0], -n.offset[0] + 0.1); // Prevent scale ≤ 0, min 0.1 eE.addTo(n._movement, n._delta), e4(n), this.state.origin = [e.clientX, e.clientY], this.compute(e), this.emit() }OR a oneline solution by Grok;
wheelChange(e) { let t = "uv" in e; !t && e.cancelable && e.preventDefault(); let n = this.state; - n._delta = [-eU(e)[1] / 100 * n.offset[0], 0], + n._delta = [Math.max((-eU(e)[1] > 0 ? n.offset[0] * 2 : n.offset[0] / 2) - n.offset[0], -n.offset[0] + 0.1), 0] eE.addTo(n._movement, n._delta); e4(n); this.state.origin = [e.clientX, e.clientY]; this.compute(e); this.emit(); }An example of the result:
