Skip to main content

Command Palette

Search for a command to run...

Every Website on Earth Uses the Same Scroll Physics. Why?

Updated
5 min read

Open any website. Scroll. Now open a different website. Scroll again.

Same feel. Same momentum. Same friction. Same inertia.

Every website on Earth uses identical scroll physics because the browser's scroll engine is a black box. You can listen to events, smooth them (Lenis), animate things on scroll (GSAP ScrollTrigger). But you cannot change how scroll feels.

Why should a long-form article scroll at the same speed as a photo gallery? Why should a CTA section fly past at the same momentum as your hero?

Introducing @aumiqx/scroll

A programmable scroll physics engine for the web. ~4KB. Zero dependencies. Pure computation — it calculates where scroll should be, you decide what moves.

import { ScrollEngine } from "@aumiqx/scroll"

const engine = new ScrollEngine({
  mass: 1.2,          // heavier = more momentum
  friction: 0.95,     // lower = stops faster
  zones: [
    { start: 0, end: 600, friction: 0.82 },     // hero: heavy, cinematic
    { start: 600, end: 1200, friction: 0.975 },  // gallery: featherlight
    { start: 1200, end: 1800, snap: true },       // pricing: magnetic snap
  ],
  magnets: [
    { position: 1500, strength: 0.4, range: 120 },
  ],
})

Now your hero section resists. Your gallery glides. Your pricing section magnetically grabs the user and holds them there.

The Physics

Every frame, four things happen:

velocity += force / mass        // input from wheel/touch
velocity *= zoneFriction        // per-section decay
velocity += magnetPull           // nearby magnets attract
position += velocity             // update scroll position

That's the entire engine. Four lines of math, 60 times per second.

The engine doesn't touch the DOM. It computes position. You apply it however you want — CSS transforms, Canvas, WebGL camera, or just window.scrollTo():

element.addEventListener("wheel", (e) => {
  e.preventDefault()
  engine.applyForce(e.deltaY * 0.3)
}, { passive: false })

function tick() {
  const state = engine.tick()
  element.style.transform = `translateY(${-state.position}px)`
  requestAnimationFrame(tick)
}
tick()

Zone Types

The power is in zones — regions of your page where scroll physics change:

Zone TypeFrictionWhat It Feels Like
Reading0.82Heavy. Every pixel matters. Content demands attention.
Gallery0.975Featherlight. Momentum carries you through. Browsing mode.
SnapmagneticPulls to center when you slow down. Can't accidentally skip.
CTA0.80Maximum resistance. The call-to-action anchors you.

Each zone is defined by start/end positions and a friction override:

zones: [
  { start: 0, end: 600, friction: 0.82 },       // hero
  { start: 600, end: 1200, friction: 0.975 },    // features
  { start: 1200, end: 1800, snap: true },         // pricing (snaps to center)
  { start: 1800, end: 2400, friction: 0.80 },    // CTA
]

Magnetic Snap Points

Important content gets magnets — invisible attraction points that pull scroll toward them when you're nearby:

magnets: [
  { position: 1500, strength: 0.4, range: 120 },  // pricing
  { position: 2100, strength: 0.5, range: 100 },   // CTA
]

The pull is proportional to proximity — stronger as you get closer, zero outside the range. Users can't accidentally fly past your pricing table.

Configurable Mass

One number changes the entire feel of your site:

  • mass: 0.5 — Snappy, responsive. Stops quickly. Good for utility apps.
  • mass: 1.2 — Balanced. Natural momentum.
  • mass: 4.0 — Heavy, cinematic. A single flick carries you through sections. Feels like pushing something physical.

Mass divides the input force: velocity += force / mass. Heavier scroll responds less to each input, but carries more momentum once moving.

How It Compares

FeatureNative ScrollLenisGSAP ScrollTrigger@aumiqx/scroll
Custom frictionNoPartialNoPer-section
Magnetic snapCSS onlyNoNoConfigurable
Custom massNoNoNoYes
Bounce wallsPlatform-specificNoNoElastic
Pure computationN/ADOM-dependentDOM-dependentNo DOM
SizeBuilt-in5KB25KB+~4KB

What You Can Build

Storytelling Landing Pages — Hero = slow and dramatic. Gallery = fast and fluid. CTA = magnetic snap. Each section of your page has different scroll physics, creating a journey, not just a page.

WebGL Camera Control — Use scroll physics to drive a 3D camera. Mass and inertia create Steadicam-like movement instead of mechanical stepping. Combine with zones to slow the camera for reveals and speed up for transitions.

Reading Experiences — Long-form articles automatically increase friction. Readers absorb more content without consciously stopping to read. Combine with magnets at key paragraphs to create natural "reading rest points."

E-commerce Product Scroller — Product listings glide with low friction for fast browsing. Category changes snap into place. The checkout CTA has maximum friction and a magnet — you can't scroll past it by accident.

Scroll-Driven Games — A game framework where scroll IS the primary input. Platformers, runners, and puzzle games controlled entirely by scroll physics. The engine becomes the game engine.

Runtime Reconfiguration

Change physics on the fly — respond to user preferences or viewport changes:

// User enables "reduced motion"
engine.configure({ mass: 0.5, friction: 0.85 })

// Viewport resized — recalculate zones
engine.configure({ max: document.body.scrollHeight })

Full State Introspection

Every frame, the engine returns complete state:

const state = engine.tick()

state.position      // current scroll position (px)
state.velocity      // current speed (px/frame)
state.activeZone    // which zone index (-1 if none)
state.nearestMagnet // magnet index in range (null if none)
state.isBouncing    // hitting a wall

Use velocity for parallax effects, active zone for section highlighting, magnet proximity for visual feedback.

Try It Live

We built an interactive demo with 5 zones you can scroll through. Each zone has different physics — you'll feel the difference immediately. There's a mass slider so you can experience heavy vs light scroll in real-time.

Install

npm install @aumiqx/scroll
# or
pnpm add @aumiqx/scroll

GitHub | npm | Live Demo

MIT licensed. TypeScript. Zero dependencies. ~4KB.


Built by Aumiqx — we build AI agents, workflow automations, and open-source tools that shouldn't work but do.