Why I removed Lenis from zaidxshaikh
Smooth-scroll libraries can fight native scroll on platforms they were supposed to enhance. Here's the diagnosis.
After shipping the v2 rebuild of zaidxshaikh I got two messages from people on Windows trackpads: "scroll is stuck". Reproducible on my own laptop within ten seconds of opening the production URL. Page refused to scroll past the 3D hero. Even after that, it felt sluggish.
Three things were fighting each other.
What was happening
scroll-behavior: smoothon the<html>element — native CSS smooth scroll.- Lenis 1.3 — a smooth-scroll JS library hooking into wheel + touch events.
OrbitControlsfrom drei — listening for wheel events on the 3D canvas.
Each one believed it owned the scroll. The native CSS rule wanted to animate. Lenis wanted to capture wheel events and animate them through requestAnimationFrame. OrbitControls called event.preventDefault() on wheel events over the canvas because zoom was technically enabled, even though I never used it.
Net effect: the wheel event landed on the canvas, OrbitControls killed it via preventDefault, Lenis never got to run, and the CSS rule had no scroll position change to animate. Scroll silently dropped on the floor.
How I diagnosed it
The hint was that some scroll worked — keyboard arrow keys, the scrollbar drag. Those don't fire wheel events. So the bug had to be in the wheel/touch pipeline.
I added a tiny debug listener:
window.addEventListener(
"wheel",
(e) => console.log("wheel", e.target, e.defaultPrevented),
{ capture: true, passive: true },
);Every event over the hero logged defaultPrevented: true. That pointed straight at the canvas. The R3F devtools showed OrbitControls' internal _wheelListener was the culprit.
The fix
A three-line diff would have been enough — disable wheel on OrbitControls — but the broader principle was clearer: don't fight native scroll.
So:
- Removed OrbitControls entirely. Replaced with a manual
useFramecamera auto-orbit I actually control. - Removed Lenis. The site doesn't need JS smooth scroll for the kind of content it has.
- Removed
scroll-behavior: smoothfrom CSS. - Added
touchAction: "pan-y"to the<Canvas>element so mobile vertical scroll passes through cleanly even if a future feature adds pointer interactions. - Dropped the glyph field count from 1200 → 700 so the canvas isn't dropping frames during scroll.
That's it. Native scroll handles itself perfectly on every platform.
The takeaway
Smooth-scroll libraries exist to fix a problem most pages don't have. The default platform behavior is good enough on modern Chrome / Safari / Edge / Firefox. Reach for Lenis or Locomotive only when you have a specific motion choreography that requires control over scroll position over time — pinned sections, parallax-tied-to-scroll, GSAP ScrollTrigger orchestration.
For a portfolio with a single big hero and a regular content scroll? Native wins. Less code, fewer dependencies, fewer edge cases, smaller bundle.
The bug is fixed in production. PR #40 if you want to read the diff.