Combining adapters

Use native animations, GSAP, Anime.js and Lenis together, and read their relationships.

On this page
  1. No duplicate records
  2. Scroll and animation relationships
  3. Filtering by engine
  4. Tested combinations

A creative site rarely uses one engine. MotionPR records every engine in the same checkpoint, on one clock, and keeps their meaning separate:

RecordWhat it isProduced by
AnimationRecordOne element animated over timeCSS (driver: css), WAAPI (waapi), GSAP and Anime.js (javascript), Anime.js waapi.animate() (waapi, source: animejs)
TimelineRecordA container that sequences animations, with labelsGSAP and Anime.js timelines
ScrollMotionRecordHow a scroller movedLenis (source: lenis) and native scrolling (source: native)
FrameSampleComputed values of one animation at one instantDeterministic replay
TypeScript
// motionpr.config.ts
export default defineConfig({
  baseURL: "http://localhost:3000",
  adapters: [
    { name: "gsap", required: true },
    { name: "animejs", required: true },
    { name: "lenis", required: true },
  ],
  scenarios: [
    {
      id: "journal",
      steps: [
        { type: "scrollTo", target: "#journal" },
        { type: "waitForScrollSettled" },
        { type: "capture", label: "journal" },
      ],
    },
  ],
});

Connect each engine in the app as its guide describes (GSAP, Anime.js, Lenis). Order does not matter, and each one is a no-op outside a capture.

No duplicate records

  • An animation is recorded by exactly one source. Anime.js waapi.animate() creates real Web Animations; the Anime.js adapter claims them, so MotionPR's native collector records them once, labelled source: animejs, driver: waapi.
  • When Lenis drives the document scroller, the native document scroll is not recorded again.
  • If two engines animate the same property of the same element at the same time (usually a bug in the app), the checkpoint gets a POSSIBLE_DUPLICATE_RECORDS warning naming them.

Scroll and animation relationships

MotionPR distinguishes three things that often happen together:

  1. Scroll motion (Lenis or native): a ScrollMotionRecord.
  2. Scroll-linked animations, whose engine declares that their progress follows scroll: CSS scroll-driven animations (timeline: scroll | view) and GSAP ScrollTrigger animations. They cannot be replayed; MotionPR records their timing and, for GSAP, their progress observed live (progressTrace).
  3. Animations that merely ran during a scroll, e.g. an IntersectionObserver reveal.

Every checkpoint lists correlations: kind: "scroll-linked" for (2), and kind: "overlap" with the overlap in milliseconds for (3). An overlap is an observation in time, not a claim that the scroll caused the animation; the report says so. MotionPR does not assume a GSAP animation is controlled by Lenis just because both are present.

For scroll-linked GSAP animations the comparison relates progress to scroll position: if the same scroll position yields different progress (a moved ScrollTrigger start/end, a different scrub range), it reports “Scroll-linked progress at the same scroll position differs by up to N%”. The values a scroll-linked tween writes (e.g. yPercent) are not sampled, so a change of parallax distance alone is not detected.

Filtering by engine

The report's timeline and difference list can be filtered by engine: Native CSS, WAAPI, GSAP, Anime.js, Lenis (and Native scroll when present). Every difference carries its source, and JSON/Markdown output include it.

Tested combinations

CombinationCovered by
GSAP + Lenis (ScrollTrigger via gsap.ticker)demo app lenis-journal, CLI end-to-end test
Anime.js + native CSS transitionsAnime.js browser tests, demo app
Anime.js + WAAPI (waapi.animate, no duplicates)Anime.js browser tests
GSAP + native animationsGSAP browser tests, demo app
GSAP + Anime.js + Lenis in one pagedemo app (11 scenarios), motionpr run CLI end-to-end test