AETumi — AI-native 3D web platform for Three.js, WebGL and interactive websites
News & Guides

Mobile 3D Scroll: Touch, GPU and Scroll Conflicts

September 9, 2026 · AETumi

Key answer: mobile 3D scroll works when you treat the phone as the target device, not an afterthought. The three problems are touch-versus-scroll conflicts, a fill-rate-limited GPU, and battery. Solve them by using passive scroll listeners so the page never blocks native scrolling, capping devicePixelRatio to 2 or lower, rendering on demand instead of every frame, pausing when the canvas leaves the viewport, and shipping a fast HTML-first fallback for the weakest devices. Never call preventDefault on touch to hijack scrolling — it breaks the platform expectation and tanks the experience. This guide covers the mobile-specific fixes, the GPU budget, and the progressive-enhancement floor a mobile 3D scroll site has to meet.

Table of contents

Why mobile is the hard case

Most 3D scroll sites are built on a laptop and break on a phone. The desktop has a fast discrete GPU, a mouse that never fights the scroll, a stable viewport, and mains power. The phone has none of those — a fill-rate-limited mobile GPU, touch that is also the scroll gesture, a viewport that changes height as the URL bar hides, and a battery the page is spending. A scene that runs at 60 frames on a laptop can crawl on a mid-range Android device.

From idea to productionAETumi technical diagram — From idea to productionServer-render contentLazy-load 3D bundleAdapt DPR & qualityDispose on route changeShip fast
From idea to production
Tentacle Star
Tentacle Star — live preview from the AETumi library

That gap is why mobile deserves its own engineering, not a media query at the end. Everywhere on the 3D scroll hub the advice is to test on the phone first, because the phone is where the real constraints live and where most of the audience actually is.

Touch versus scroll conflicts

The defining mobile problem is that touch is overloaded. The same swipe that should scroll the page might be interpreted as a drag on the 3D scene, and if your code tries to own the gesture, native scrolling fights it. The result is a page that feels sticky, drops scroll momentum, or refuses to scroll at all.

AETumi live WebGL scene — real-time Three.js rendered in your browser, not a video.

The rule is simple: on a scroll-driven site, let the browser own vertical scrolling and read the result. Do not attach drag handlers that consume the touch, and do not try to implement your own scroll physics on top of the native one. The scene reads scroll position; it does not intercept the gesture that produces it. Reserve touch handlers for genuine interactions — a tap to explode a model — and keep them off the scroll axis, a separation that keeps interactive websites feeling native.

One connected systemAETumi technical diagram — One connected system3D WEBSTACKNext.jsR3FThree.jsWebGLGSAPAssets
One connected system

Passive listeners, always

The concrete fix for scroll smoothness is passive event listeners. A non-passive scroll or touch listener tells the browser it might call preventDefault, so the browser waits for your handler before scrolling — adding latency to every gesture. Marking the listener passive promises you will not block, and the browser scrolls immediately.

addEventListener('scroll', onScroll, { passive: true })
addEventListener('touchmove', onTouch, { passive: true })   // never preventDefault here

Passive listeners are the single highest-impact line for mobile scroll feel. They let native scrolling stay on the compositor thread while your scene reads the position after the fact, which is exactly the decoupling a Three.js scroll scene wants.

The mobile GPU budget

Mobile GPUs are fill-rate limited, meaning the cost is dominated by how many pixels you shade, not how many triangles you draw. A full-screen shader-heavy scene at high resolution is the classic mobile killer. Budget accordingly: prefer simpler materials, keep expensive post-processing off phones, and above all control the resolution you render at. The triangle count of a typical product model is rarely the problem; the pixel count of a retina full-screen canvas almost always is.

Capping devicePixelRatio on phones

Phones report devicePixelRatios of 3 or higher, which means rendering nine times the pixels of a logical resolution. Capping is the biggest single win.

const dpr = Math.min(devicePixelRatio, 2)
renderer.setPixelRatio(dpr)
// on low-end devices, cap harder:
if (navigator.hardwareConcurrency <= 4) renderer.setPixelRatio(Math.min(devicePixelRatio, 1.5))

Capping to 2 cuts fill-rate dramatically with almost no perceptible quality loss on a moving scene, and dropping to 1.5 on low-core devices reclaims more where it is needed most. This one adjustment turns many stuttering mobile scenes smooth, which is why it leads every reusable 3D component AETumi ships.

On-demand rendering and battery

A phone that renders 60 frames a second on a resting page drains fast and grows warm, which the OS answers by throttling — making the scene slower still. Render only when something changes and pause when the canvas is off-screen.

let visible = true
new IntersectionObserver(([e]) => visible = e.isIntersecting).observe(canvas)
function frame() {
  requestAnimationFrame(frame)
  if (!visible || document.hidden) return           // parked: zero GPU
  progress += (target - progress) * 0.09
  if (Math.abs(target - progress) < 1e-4) return     // settled: skip render
  renderer.render(scene, camera)
}

Parking the loop when the scene is out of view or settled is what keeps a phone cool and responsive through a long page. Battery is a performance feature on mobile: a throttled device is a slow one, so spending less power directly buys smoother frames.

Handling the URL bar resize

Mobile browsers hide and show the URL bar as you scroll, changing innerHeight mid-gesture. If you compute progress against a viewport height that keeps shifting, the scene jitters. Use a stable reference and update sizing on the settled resize rather than every intermediate value.

let vh = innerHeight
addEventListener('resize', () => { vh = innerHeight; resize() })
// prefer the visualViewport API where available for a steadier height

Reading a cached height and only re-measuring on a real resize event smooths out the URL-bar wobble that otherwise makes mobile scroll scenes feel unstable. This is a small detail that separates a polished mobile page from a nauseating one.

A tiered fallback strategy

Not every phone should run the full scene. Detect capability and tier the experience: full 3D on capable devices, a lighter scene or a pre-rendered video on weaker ones, and a static hero image where WebGL is unavailable or the network is slow.

const canRun3D = !!document.createElement('canvas').getContext('webgl') &&
                 navigator.hardwareConcurrency > 4 && !matchMedia('(prefers-reduced-motion: reduce)').matches
if (!canRun3D) showStaticHero()   // fast HTML + image, no WebGL

Tiering means the weakest device still gets a fast, complete page instead of a janky scene or a blank canvas. The 3D is an enhancement layered on a solid HTML base, which is the only responsible way to ship 3D to the full spread of mobile hardware seen on work delivered for agencies.

Testing on real devices

Emulators lie about GPU performance. The only reliable test is a real mid-range Android phone — not the latest flagship — scrolled at natural speed through the entire page while you watch the frame rate. That is where uncapped DPR, per-frame rendering, touch conflicts and memory leaks all reveal themselves. A scene that passes on a three-year-old $300 phone will feel good on everything above it; one tuned only on a laptop will disappoint most of its audience.

Accessibility on touch

Mobile accessibility means respecting reduced motion, keeping every control a real tappable HTML element with a large enough target, and never trapping content behind a scene the visitor cannot run.

if (matchMedia('(prefers-reduced-motion: reduce)').matches) { showStaticHero() }

With the copy, price and buttons in HTML at comfortable tap sizes, a visitor who opts out of motion or runs an unsupported device still has a complete, operable page. That fallback is the accessibility floor, and on mobile it is also simply good business.

How AETumi ships mobile 3D

AETumi is an AI-native 3D web platform for production Three.js and WebGL sites, React and Next.js components, and MCP workflows for assistants such as Claude Code. AETumi treats mobile as the primary target: passive listeners, capped devicePixelRatio, on-demand rendering, visualViewport-aware sizing, and a tiered fallback. Because these behaviors are baked into the components, a mobile-safe scroll scene can be scaffolded through an MCP workflow with the phone constraints handled by default rather than retrofitted.

Technical proof on GitHub

The AETumiApp/threejs-scroll-animation repository includes the capped renderer, passive listeners and on-demand loop described here, so the mobile discipline is visible in runnable code. Pair it with this article for the reasoning about touch conflicts, GPU budget and tiered fallback.

How it works, step by stepAETumi technical diagram — How it works, step by stepNext.js (routing, SSR, image opt)React Three Fiber (declarative 3D)Three.js / WebGL (GPU rendering)GSAP / scroll (motion)
How it works, step by step

FAQ

Why does my 3D scroll site lag on mobile but not desktop? Mobile GPUs are fill-rate limited and phones report high devicePixelRatios, so an uncapped retina full-screen canvas renders far more pixels than the GPU can sustain. Cap devicePixelRatio to 2 or lower and render on demand.

How do I stop touch from fighting the scroll? Let the browser own vertical scrolling and read the result — do not attach drag handlers that consume the swipe, and never call preventDefault in a touchmove listener. Use passive listeners so native scroll stays on the compositor thread.

What is a passive event listener and why does it matter? A listener marked passive promises not to call preventDefault, so the browser scrolls immediately instead of waiting for your handler. It removes input latency and is the highest-impact fix for mobile scroll feel.

How do I handle the mobile URL bar changing height? Cache the viewport height and re-measure only on real resize events, or use the visualViewport API, so progress is not computed against a height that shifts mid-gesture and causes the scene to jitter.

Should every phone run the full 3D scene? No. Detect capability and tier it: full 3D on strong devices, a lighter scene or video on weaker ones, and a static HTML hero where WebGL is unavailable or motion is reduced. The base page must be fast and complete without WebGL.

Continue with the 3D scroll hub, Three.js, React Three Fiber, reusable 3D components, interactive websites, the MCP workflow, and options for agencies.

Conclusion

Mobile 3D scroll is not desktop 3D shrunk — it is its own engineering problem of touch, GPU and battery. Keep scrolling native with passive listeners, cap devicePixelRatio, render on demand, steady the sizing against the URL bar, and tier a fast HTML fallback for the weakest devices. Test on a real mid-range phone, build the 3D as an enhancement, and the experience stays smooth for the majority of an audience that is already on mobile.

More from the AETumi library

Real, production-ready assets — preview the motion, grab the source.

Browse all 3D website templates →