useVelocity

useVelocity returns an augmented MotionValue<number> tracking the velocity of a source’s value in units per second. Pass a motion value (from useMotionValue, useSpring, useScroll, …) or any Svelte readable; the result composes with useTransform, useSpring, and the rest of the Tier 2 surface.

<script>
  import { useSpring, useVelocity } from '@humanspeak/svelte-motion'

  const x = useSpring(0)
  const xVelocity = useVelocity(x)
</script>

<div>Velocity: {xVelocity.current} px/s</div>
<script>
  import { useSpring, useVelocity } from '@humanspeak/svelte-motion'

  const x = useSpring(0)
  const xVelocity = useVelocity(x)
</script>

<div>Velocity: {xVelocity.current} px/s</div>

Usage

The returned value emits the rate of change in units per second and settles to 0 when the source stops moving.

<script lang="ts">
  import { useSpring, useVelocity, useTransform } from '@humanspeak/svelte-motion'

  const x = useSpring(0)
  const xVelocity = useVelocity(x)

  // Map velocity to visual effects
  const skew = useTransform(xVelocity, [-1000, 0, 1000], [-20, 0, 20])
  const opacity = useTransform(xVelocity, [-500, 0, 500], [0.5, 1, 0.5])
</script>

<div
  style="transform: skewX({skew.current}deg); opacity: {opacity.current}"
  onpointermove={(e) => x.set(e.clientX)}
>
  Move your pointer
</div>
<script lang="ts">
  import { useSpring, useVelocity, useTransform } from '@humanspeak/svelte-motion'

  const x = useSpring(0)
  const xVelocity = useVelocity(x)

  // Map velocity to visual effects
  const skew = useTransform(xVelocity, [-1000, 0, 1000], [-20, 0, 20])
  const opacity = useTransform(xVelocity, [-500, 0, 500], [0.5, 1, 0.5])
</script>

<div
  style="transform: skewX({skew.current}deg); opacity: {opacity.current}"
  onpointermove={(e) => x.set(e.clientX)}
>
  Move your pointer
</div>

With unit strings

useVelocity parses numeric values from unit strings like "120px" or "45deg":

<script>
  import { useSpring, useVelocity } from '@humanspeak/svelte-motion'

  const rotation = useSpring('0deg')
  const rotationVelocity = useVelocity(rotation)
</script>
<script>
  import { useSpring, useVelocity } from '@humanspeak/svelte-motion'

  const rotation = useSpring('0deg')
  const rotationVelocity = useVelocity(rotation)
</script>

How it works

  1. Creates an internal motionValue from motion-dom for timestamp-based velocity tracking
  2. Subscribes to the source and forwards each emit to the tracker via .set()
  3. Polls motionValue.getVelocity() on every requestAnimationFrame and writes the result to the returned motion value
  4. Snaps the result to 0 and stops polling when velocity falls below 0.001 units/second; the next source emit restarts the loop
  5. The source subscription and both internal motion values are torn down when the surrounding $effect scope unmounts

Performance

  • On-demand polling: The RAF loop only runs while velocity is non-zero
  • Auto-settle: Stops polling when movement stops, avoiding idle CPU usage
  • Cleanup: Cancels the frame loop and unsubscribes the source at component unmount
  • SSR-safe: Returns a static motionValue(0) with no source subscription on the server

Common patterns

Momentum-based skew

<script>
  import { useSpring, useVelocity, useTransform } from '@humanspeak/svelte-motion'

  const x = useSpring(0)
  const xVelocity = useVelocity(x)
  const skew = useTransform(xVelocity, [-1000, 0, 1000], [-25, 0, 25])
</script>

<div
  style="transform: skewX({skew.current}deg)"
  onpointermove={(e) => x.set(e.clientX)}
>
  Skews with momentum
</div>
<script>
  import { useSpring, useVelocity, useTransform } from '@humanspeak/svelte-motion'

  const x = useSpring(0)
  const xVelocity = useVelocity(x)
  const skew = useTransform(xVelocity, [-1000, 0, 1000], [-25, 0, 25])
</script>

<div
  style="transform: skewX({skew.current}deg)"
  onpointermove={(e) => x.set(e.clientX)}
>
  Skews with momentum
</div>

Velocity-driven blur

<script>
  import { useSpring, useVelocity, useTransform, useMotionTemplate } from '@humanspeak/svelte-motion'

  const y = useSpring(0)
  const yVelocity = useVelocity(y)
  const blur = useTransform(yVelocity, [-2000, 0, 2000], [10, 0, 10])
  const filter = useMotionTemplate`blur(${blur}px)`
</script>

<div style="filter: {filter.current}">
  Blurs when moving fast
</div>
<script>
  import { useSpring, useVelocity, useTransform, useMotionTemplate } from '@humanspeak/svelte-motion'

  const y = useSpring(0)
  const yVelocity = useVelocity(y)
  const blur = useTransform(yVelocity, [-2000, 0, 2000], [10, 0, 10])
  const filter = useMotionTemplate`blur(${blur}px)`
</script>

<div style="filter: {filter.current}">
  Blurs when moving fast
</div>

Chaining velocities

You can derive the velocity of a velocity to get acceleration:

<script>
  import { useSpring, useVelocity } from '@humanspeak/svelte-motion'

  const x = useSpring(0)
  const xVelocity = useVelocity(x)
  const xAcceleration = useVelocity(xVelocity)
</script>
<script>
  import { useSpring, useVelocity } from '@humanspeak/svelte-motion'

  const x = useSpring(0)
  const xVelocity = useVelocity(x)
  const xAcceleration = useVelocity(xVelocity)
</script>

API Reference

Parameters

  • source MotionValue<number | string> | Readable<number | string> — a motion value or Svelte readable whose value changes over time.

Returns

An AugmentedMotionValue<number> — a real motion-dom MotionValue containing the current velocity in units per second, plus:

  • .current — Svelte 5 reactive getter (templates, $derived, $effect).
  • .subscribe(run) — Svelte readable store contract (powers $velocity template syntax).
  • All other MotionValue methods from motion-dom (get, getVelocity, on, etc.).

When to use

  • Momentum effects: Skew, stretch, or blur elements based on how fast a value is changing
  • Physics-based UI: React to acceleration or deceleration of animations
  • Gesture feedback: Show speed-dependent visual feedback during pointer or scroll interactions
  • Chained dynamics: Derive acceleration by passing a velocity store back into useVelocity

See also


Based on Motion’s useVelocity API.