Motion

What it does

@rat-stack/motion moves values over time in a Foldkit27 app. Its core is four TypeScript modules in packages/motion/src/*.pure.ts. They import only each other, so another project can copy them at a pinned commit.ΒΆ

The spring takes fixed steps of 1/120 s. It carries the leftover time to the next frame. When it comes to rest, it snaps exactly onto the target.ΒΆ

packages/motion/src/spring.pure.ts Β· 5fa5df15 Β· Each substep checks for rest, so the snap happens at the same instant at any frame rate
  const span = Math.min(MAX_STEP_MS, Math.max(0, elapsed));
  const ticks = into.remainder + span / SUBSTEP_MS;
  const substeps = Math.floor(ticks + SUBSTEP_GRID_TOLERANCE);

  for (let substep = 0; substep < substeps; substep += 1) {
    substepInto(into);

    if (atRest(into.config, into.position, into.velocity, into.target)) {
      return snapInto(into);
    }
  }

  into.remainder = Math.max(0, ticks - substeps);

  return into;

The Foldkit adapter30, @rat-stack/motion/foldkit, keeps a clock and the named tweens and springs in the Model. A frame is a TickedFrame Message. Motion.advance steps every spring and moves the clock. The view samples each tween at the clock time.ΒΆ

packages/motion/src/foldkit.ts Β· 5fa5df15 Β· Frames run only while something moves, and the reduced-motion preference arrives as a Message
export const motionSubscriptions = <Model, Message>(
  options: MotionSubscriptionOptions<Model, Message>
) =>
  Subscription.make<Model, Message>()(() => ({
    motionFrame: Subscription.animationFrameEntry<Model, Message>({
      isActive: options.isAnimating,
      toMessage: (elapsed) =>
        options.toMessage(Message.TickedFrame({ elapsed })),
    }),
    reducedMotion: Subscription.persistentEntry(
      Dom.streamFromMediaQuery({
        mapMatches: (isReducedMotion) =>
          options.toMessage(Message.ChangedReducedMotion({ isReducedMotion })),
        query: REDUCED_MOTION_QUERY,
      })
    ),
  }));

Pick a target in the demo at the end of this page, then pick another before the dots arrive. The tween restarts its curve. The spring keeps its speed and turns.ΒΆ

The curve gallery plots every easing.ΒΆ

The standard

Each rule is an it.prop property over generated inputs. Each property also runs against a planted bug, and the suite fails if the property passes it.ΒΆ

packages/motion/test/spring.test.ts Β· 5fa5df15 Β· The frame-rate property must reject an integrator that takes one step per frame
      "the frame-rate property catches an integrator that takes whole frames",
      () =>
        falsifies(
          Arbitrary.all([
            movingSpring,
            integerBetween(1, 6),
            integerBetween(1, 6),
          ]),
          ([from, first, second]) =>
            splitAgrees(wholeFrameEuler, from, first, second)
        )
    );

How to check

Sources

  1. Terms of Use: Easing Functions (Equations). Robert Penner. The MIT and 3-Clause BSD terms for the easing equations; used for the curve set. Accessed 2026-11-02.
  2. CSS Easing Functions Level 1. W3C. The cubic-bezier() and steps() definitions and the named presets. Accessed 2026-11-02.
  3. A perceptual color space for image processing. BjΓΆrn Ottosson. The OKLab matrices used for colour interpolation. Accessed 2026-11-02.

Last changed 2026-11-02 in 67d126f. Source on GitHub. Change log.

Linked from

  1. Agent guide. An Effect stack so pure (aspirational) Kit Langton will blush.

    Motion β€” @rat-stack/motion gives Foldkit apps Penner and CSS easings, tweens, fixed-step springs and interpolation, held in the Model and checked by properties.

  2. Build and run rat-stack (README.md). What is in the repo, how the example works, and how to run it.

    Role: Easing, tweens, springs and interpolation as dependency-free TypeScript, with an optional Foldkit adapter

  3. Change log. What changed in the files served here, newest first.

    Motion β€” new page: Initial commit from joelhooks/rat-stack template

  4. Dependency injection is the reason to choose Effect. Effect tracks service requirements in types and supplies replaceable implementations through Layers.

    Dillon Mulroy compares Effect with Spring and NestJS:ΒΆ

  5. Full agent guide. An Effect stack so pure (aspirational) Kit Langton will blush.
  6. Rat Stack systems | rat-stack. The systems rat-stack ships, the standard each keeps, and how to check it.

    Motion β€” @rat-stack/motion gives Foldkit apps Penner and CSS easings, tweens, fixed-step springs and interpolation, held in the Model and checked by properties.

  7. Source change history (log.md). The generated change log as Markdown.

    Motion β€” new page: Initial commit from joelhooks/rat-stack template

Spring and tween

Pick a target. Pick another before the dots arrive. The tween restarts its curve from where it is. The spring keeps its speed and bends toward the new target.

β—‹ Tween dotβ–  Spring dot

Move both dots to

Each plot shows one easing over progress from 0 to 1. The dot runs along the curve.

  • linear
  • quadIn
  • quadOut
  • quadInOut
  • cubicIn
  • cubicOut
  • cubicInOut
  • quartIn
  • quartOut
  • quartInOut
  • quintIn
  • quintOut
  • quintInOut
  • sineIn
  • sineOut
  • sineInOut
  • expoIn
  • expoOut
  • expoInOut
  • circIn
  • circOut
  • circInOut
  • backIn
  • backOut
  • backInOut
  • elasticIn
  • elasticOut
  • elasticInOut
  • bounceIn
  • bounceOut
  • bounceInOut
  • ease
  • ease-in
  • ease-out
  • ease-in-out
  • step-start
  • step-end