Skip to content

Zooming velocity in deep zoom #770

Description

@ShaMan123

Describe the bug 📝

Zooming is expected to be exponential to the wheel event
Deep zooming becomes linear.

Screen.Recording.2026-07-21.at.10.16.06.mov

Investigating via claude

infinityDolly produces two different zoom regimes, and the boundary between them is minDistance:

radius vs minDistance per-notch step feel
radius > minDistance radius · k (multiplicative) proportional / exponential — natural
radius ≤ minDistance minDistance · k (constant, additive) linear

Once you zoom in far enough that the orbit radius collapses to minDistance, camera-controls stops shrinking the radius and instead pushes the target/camera forward by a fixed world-space step every notch (_dollyInternal_dollyToNoClamp, and the isMin branch in update() at line 2185-2190@node_modules/camera-controls/dist/camera-controls.module.js see code snippet below). My sweep confirms that step is exactly minDistance · k:

  • minDistance = 0.01 → step 0.0015 — this was your original "decay": a frozen crawl.
  • minDistance = 6 → step 0.942 — the current "linear at high zoom": a fixed step that no longer scales with how close you are.

So it's the same bug seen from two sides. My earlier fix moved you from the frozen end of the scale to the coarse end. Both are the constant-step regime; the constant is minDistance · k.

Why it reads as a "slowdown": in the proportional regime the thing under the cursor grows by a constant percentage per notch. In the constant regime the camera moves a fixed distance per notch, so when the target is far the on-screen growth per notch keeps shrinking — the zoom feels like it's grinding to a linear crawl. It bites specifically "at high zoom" because that's exactly when radius has pinned to minDistance.

The root problem: infinityDolly's step is governed by the fixed minDistance, which is decoupled from the actual distance to the geometry under the cursor. No single fixed minDistance can be right — small = frozen, large = coarse/linear.

// node_modules/camera-controls/dist/camera-controls.module.js
                const isMin = this._lastDollyDirection === DOLLY_DIRECTION.IN && this._spherical.radius <= this.minDistance;
                const isMax = this._lastDollyDirection === DOLLY_DIRECTION.OUT && this.maxDistance <= this._spherical.radius;
                if (this.infinityDolly && (isMin || isMax)) {
                    this._sphericalEnd.radius -= dollyControlAmount;
                    this._spherical.radius -= dollyControlAmount;
                    const dollyAmount = _v3B.copy(cameraDirection).multiplyScalar(-dollyControlAmount);
                    newTargetEnd.add(dollyAmount);
                }

Reproduction ▶️

No response

Steps to reproduce 🔢

No response

System Info 💻

n/a

Used Package Manager 📦

npm

Error Trace/Logs 📃

No response

Validations ✅

  • Read the docs.
  • Check that there isn't already an issue that reports the same bug to avoid creating a duplicate.
  • Make sure this is a repository issue and not a framework-specific issue. For example, if it's a THREE.js related bug, it should likely be reported to mrdoob/threejs instead.
  • Check that this is a concrete bug. For Q&A join our Community.
  • The provided reproduction is a minimal reproducible example of the bug.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions