motion-one-waapi

Installation
SKILL.md

Motion (motion.dev) for vanilla JS / WAAPI

When to use this

  • You want GSAP-like ergonomics (animate, timelines, stagger, scroll linking) but need a much smaller bundle and are fine relying on the native Web Animations API for the actual playback engine.
  • You're building a framework-agnostic site (not React) and want hardware-accelerated animation without hand-rolling element.animate() calls yourself.
  • You need scroll-linked or viewport-triggered animation with minimal setup (scroll(), inView()) and do not need GSAP ScrollTrigger's full pinning/snapping feature set.
  • You want spring-based motion outside of a React component tree (Motion's standalone spring and animate support spring easing directly).
  • Do NOT use this when you're already in a React codebase and want gesture/layout features tied to component lifecycle; use framer-motion-layout or framer-motion-gestures-drag (Motion's React package, motion/react) instead. Do NOT use this for complex pinned scrollytelling with snapping; gsap-scrolltrigger has a deeper feature set for that specific job.

Mental model

Motion (the standalone/vanilla package, formerly called Motion One) is a thin, opinionated layer over the browser's native Web Animations API (Element.animate()). Where GSAP maintains its own tick loop and writes styles imperatively every frame, Motion's animate() call, for animations expressible as keyframes, hands the actual interpolation off to the browser's compositor thread via a real Animation object, the same one you would get from calling element.animate() yourself. This is why Motion's bundle can be so small and its performance so good even on lower-powered devices: for the common case, Motion is a syntax and feature layer (spring physics, stagger helpers, scroll linking, imperative controls) sitting on top of work the browser engine already does natively and efficiently, rather than reimplementing interpolation in JS.

Motion supports two easing paradigms that resolve differently under the hood: standard cubic-bezier/keyframe easing compiles directly to WAAPI keyframes and runs entirely off the main thread. Spring easing (spring({...}) or type: 'spring') is physically simulated; Motion pre-computes the spring's resolved keyframe curve (since WAAPI keyframes need to be knowable ahead of time, not computed live), converts it into a dense keyframe list that approximates the spring's motion, and hands THAT to WAAPI. This means even spring physics in Motion generally still runs on the compositor, unlike a from-scratch spring implementation that would need per-frame JS computation.

scroll() and inView() are Motion's two scroll-related primitives, and they solve different problems. inView(element, callback) is a thin, ergonomic wrapper around IntersectionObserver: it calls your callback when the element enters/leaves the viewport, and you decide what happens (typically calling animate() inside it), a discrete trigger. scroll(onScroll, options) is a continuous progress driver: it hooks into scroll position (of the whole page or a specific scrollable container/element range you specify) and calls your function with a 0-1 progress value on every scroll frame, and critically, it can hand that progress value directly as the "time" input to an existing Animation object's .currentTime, letting you scrub any Motion or native WAAPI animation with scroll position at compositor-friendly cost.

Installs
2
First Seen
Aug 30, 2026
motion-one-waapi — avnehsbhatia/ultraui