Aumiqx
AUM

experiment 02

@aumiqx/scroll

A programmable scroll physics engine. Per-section friction, magnetic snap points, configurable mass. Pure math — no DOM, no framework, ~4KB, zero dependencies.

Every website on Earth uses identical scroll physics. This library lets you change all of it.

01The Problem

Every website scrolls exactly the same.

Same mass. Same friction. Same inertia. Your hero section and your footer have identical scroll physics. Try both boxes below — scroll in each one and feel the difference.

Normal Scroll

same friction everywhere

hero
features
pricing
social
cta
vel
0.0

scroll here

Physics Scroll

per-section friction + magnets

hero
features
pricing
social
cta
vel
0.0

scroll here

left: uniform friction 0.95 everywhere — right: hero resists, features glide, pricing snaps

02How It Works

Four steps, every frame.

The engine runs a physics loop at 60fps. No DOM manipulation — just pure math. You get a position number and decide what to do with it.

F

Force

Wheel or touch event applies force, divided by mass

f

Friction

Per-zone friction decays velocity each frame

M

Magnets

Nearby magnets pull position toward target

P

Position

Final position updates. You render it however you want.

velocity += force / mass | velocity *= zoneFriction | position += velocity

03Try It

Feel the difference yourself.

Five sections, each with different physics. The velocity bar and zone indicator update in real time. Drag the mass slider to change how the whole thing feels.

1Scroll down slowly — notice the hero section resists
2Keep scrolling — the features zone lets you glide
3Approach pricing — feel the magnet grab you
4Try changing mass to 4 and flick hard
hero

Scroll feels HEAVY here. Every pixel matters. This is your first impression — the reader should sink into it, not fly past it.

friction: 0.82 — cinematic, heavy

features

Now scroll feels LIGHT. Momentum carries you. This is a gallery zone — users browse visually, skimming like flipping cards. One flick and you glide.

friction: 0.975 — featherlight, momentum

pricing

This section has a MAGNET. Try to scroll past it — the engine pulls you back to center. Pricing should never be skipped.

snap: true + magnet — can't skip this

social proof

Comfortable reading pace. Not too fast, not too slow. The friction is calibrated for trust-building content — testimonials, case studies.

friction: 0.93 — reading pace

cta

Maximum resistance. Every scroll tick is HEAVY. Combined with a magnet, this section anchors the user. You can't casually scroll past it.

friction: 0.80 + magnet — anchored

velocity
0.0 px/f
zonehero
friction0.82

scroll to feel different physics per section

physics

mass1.2

active zone

hero

friction: 0.82 — cinematic, heavy

try this

1. Scroll fast — notice hero resists but features glides

2. Slow down near pricing — it grabs you

3. Set mass to 4 and flick — cinematic

4. Try reaching the cta — it fights you

size~4KB
deps0
domnot needed
fpspure math/tick

each section has different physics — the velocity bar shows real-time momentum

04The Code

~20 lines to get started.

Create an engine, feed it wheel events, call tick() on every frame. The engine gives you a position — you decide what moves.

tsxthe essential setup
import { ScrollEngine } from "@aumiqx/scroll"

const engine = new ScrollEngine({
  mass: 1.2,
  friction: 0.93,
  zones: [
    { start: 0, end: 500, friction: 0.85 },
    { start: 500, end: 1000, friction: 0.97 },
    { start: 1000, end: 1500, snap: true },
  ],
  magnets: [
    { position: 750, strength: 0.4, range: 120 },
  ],
})

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

function tick() {
  const { position } = engine.tick()
  el.style.transform = `translateY(${-position}px)`
  requestAnimationFrame(tick)
}
tick()
applyForce(delta)

Feed input from any event

tick()

Advance physics by one frame

configure(opts)

Change settings at runtime

05What You Could Build

It is not just for websites.

The engine outputs a number. Anything that responds to position can be driven by scroll physics.

Storytelling Landing Pages

Hero is slow and dramatic. Feature gallery is fast and fluid. CTA magnetically grabs and holds.

Cinematic WebGL Experiences

Drive a 3D camera with scroll. Mass creates Steadicam movements. Zones change camera speed at key moments.

Reading Experience Optimizer

Long-form articles auto-increase friction. Readers absorb more content without consciously stopping.

E-commerce Product Scroller

Product listings with physical momentum. Sale items slow you down. Magnetic snap on checkout CTA.

Interactive Data Visualization

Scroll drives chart progression. Zones map to datasets. Magnetic points on major data events.

Scroll-Driven Games

Platformers, runners, puzzles — controlled entirely by scroll physics. The scroll engine becomes the game engine.

06Under The Hood

For the technically curious.

Every configuration option and method, at a glance.

~4KB

bundle

0

dependencies

no

dom required

none

framework

configuration

optiontypedefaultdescription
massnumber1Scroll inertia. Higher = more momentum, slower response.
frictionnumber0.95Base velocity decay per frame. 0.80 = stops fast. 0.99 = glides.
min / maxnumber0 / InfScroll bounds. Hitting bounds causes a bounce.
zonesScrollZone[][]Per-section friction overrides and snap behavior.
magnetsScrollMagnet[][]Points that pull scroll toward them when in range.
wallsScrollWall[][]Custom bounce points with configurable elasticity.

api

applyForce(delta)

Apply external force (wheel/touch). Divided by mass.

tick()

Advance physics one frame. Returns position, velocity, zone, magnet, bounce state.

setPosition(pos)

Teleport to position. Resets velocity.

configure(opts)

Update mass, friction, zones, magnets at runtime.

.position

Current scroll position (getter).

.velocity

Current velocity in px/frame (getter).

.state

Full state snapshot (getter).

ScrollZone

{
  start: number,
  end: number,
  friction?: number,  // 0.80-0.99
  snap?: boolean      // pull to center
}

ScrollMagnet

{
  position: number,   // target px
  strength: number,   // 0-1 pull force
  range: number       // activation radius
}

built by aumiqx labs