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 Type | Friction | What It Feels Like |
|---|---|---|
| Reading | 0.82 | Heavy. Every pixel matters. Content demands attention. |
| Gallery | 0.975 | Featherlight. Momentum carries you through. Browsing mode. |
| Snap | magnetic | Pulls to center when you slow down. Can't accidentally skip. |
| CTA | 0.80 | Maximum 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
| Feature | Native Scroll | Lenis | GSAP ScrollTrigger | @aumiqx/scroll |
|---|---|---|---|---|
| Custom friction | No | Partial | No | Per-section |
| Magnetic snap | CSS only | No | No | Configurable |
| Custom mass | No | No | No | Yes |
| Bounce walls | Platform-specific | No | No | Elastic |
| Pure computation | N/A | DOM-dependent | DOM-dependent | No DOM |
| Size | Built-in | 5KB | 25KB+ | ~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
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.
Top comments (0)