useTime
useTime returns a motion-dom MotionValue<number> that updates once per animation frame with elapsed milliseconds since the value was created. It’s a real motion value augmented with a $state-backed .current getter and a Svelte readable .subscribe shim — so it composes with useTransform, useSpring, and the rest of the Tier 2 surface.
<script>
import { useTime, useTransform } from '@humanspeak/svelte-motion'
const time = useTime()
const rotate = useTransform(time, [0, 4000], [0, 360], { clamp: false })
</script>
<div style="transform: rotate({rotate.current}deg)">
Rotating content
</div><script>
import { useTime, useTransform } from '@humanspeak/svelte-motion'
const time = useTime()
const rotate = useTransform(time, [0, 4000], [0, 360], { clamp: false })
</script>
<div style="transform: rotate({rotate.current}deg)">
Rotating content
</div>Usage
The store value represents elapsed milliseconds, making it perfect for time-based animations that need to stay in sync with Svelte’s reactivity system.
<script lang="ts">
import { useTime } from '@humanspeak/svelte-motion'
import { derived } from 'svelte/store'
const time = useTime()
// Create derived values for smooth animations
const x = derived(time, (t) => Math.sin(t / 1000) * 100)
const y = derived(time, (t) => Math.cos(t / 1000) * 100)
const rotate = derived(time, (t) => (t / 10) % 360)
</script>
<div style="transform: translate({$x}px, {$y}px) rotate({$rotate}deg)">
Animated content
</div><script lang="ts">
import { useTime } from '@humanspeak/svelte-motion'
import { derived } from 'svelte/store'
const time = useTime()
// Create derived values for smooth animations
const x = derived(time, (t) => Math.sin(t / 1000) * 100)
const y = derived(time, (t) => Math.cos(t / 1000) * 100)
const rotate = derived(time, (t) => (t / 10) % 360)
</script>
<div style="transform: translate({$x}px, {$y}px) rotate({$rotate}deg)">
Animated content
</div>Shared timelines
Pass an id string to share the same RAF loop across multiple components. Each call returns its own motion value — destroying one consumer’s value doesn’t ripple to others — but they all observe the same underlying timeline, so the values stay perfectly in lockstep. The shared loop runs while at least one consumer is alive and stops the moment the last one unmounts; the next useTime(id) call restarts it.
Note: Within a single component, you can simply reuse the same motion-value reference. The
idparameter is specifically useful for synchronizing animations across different components that don’t share scope.
<script>
import { useTime } from '@humanspeak/svelte-motion'
import { derived } from 'svelte/store'
// This component will sync with any other component using 'global'
const time = useTime('global')
const rotation = derived(time, (t) => (t / 20) % 360)
</script>
<div style="transform: rotate({$rotation}deg)">
Synced animation
</div><script>
import { useTime } from '@humanspeak/svelte-motion'
import { derived } from 'svelte/store'
// This component will sync with any other component using 'global'
const time = useTime('global')
const rotation = derived(time, (t) => (t / 20) % 360)
</script>
<div style="transform: rotate({$rotation}deg)">
Synced animation
</div>Multiple components synchronized
<!-- ComponentA.svelte -->
<script>
import { useTime } from '@humanspeak/svelte-motion'
const time = useTime('shared-timeline')
</script>
<div>Time: {$time}ms</div>
<!-- ComponentB.svelte -->
<script>
import { useTime } from '@humanspeak/svelte-motion'
const time = useTime('shared-timeline')
</script>
<div>Same time: {$time}ms</div><!-- ComponentA.svelte -->
<script>
import { useTime } from '@humanspeak/svelte-motion'
const time = useTime('shared-timeline')
</script>
<div>Time: {$time}ms</div>
<!-- ComponentB.svelte -->
<script>
import { useTime } from '@humanspeak/svelte-motion'
const time = useTime('shared-timeline')
</script>
<div>Same time: {$time}ms</div>Even though these are separate components, they both receive updates from the same timeline by using the same id, ensuring perfect synchronization.
Synced timeline example
Here’s a complete example demonstrating the power of shared timelines. Notice how two separate useTime() calls with the same id return independent-but-synchronized motion values — different references, identical values every frame:
<script lang="ts">
import { useTime } from '@humanspeak/svelte-motion'
import { derived } from 'svelte/store'
// Two separate useTime calls with the same id
const time = useTime('synced-timeline')
const time2 = useTime('synced-timeline')
// Create animations from DIFFERENT store references
const rotate1 = derived(time, (t) => (t / 10) % 360)
const rotate2 = derived(time2, (t) => (t / 10) % 360)
const scale1 = derived(time, (t) => 1 + Math.sin(t / 800) * 0.2)
const scale2 = derived(time2, (t) => 1 + Math.sin(t / 800) * 0.2)
const hue = derived(time, (t) => (t / 30) % 360)
</script>
<div>
<!-- Element A uses 'time' -->
<div style="
transform: rotate({$rotate1}deg) scale({$scale1});
background: linear-gradient(135deg, hsl({$hue}, 70%, 60%), hsl({$hue + 30}, 70%, 50%));
">
A
</div>
<!-- Element B uses 'time2' -->
<div style="
transform: rotate({$rotate2}deg) scale({$scale2});
background: linear-gradient(135deg, hsl({$hue + 120}, 70%, 60%), hsl({$hue + 150}, 70%, 50%));
">
B
</div>
<!-- Proof they're synchronized -->
<div>
time: {$time}ms = time2: {$time2}ms
</div>
</div><script lang="ts">
import { useTime } from '@humanspeak/svelte-motion'
import { derived } from 'svelte/store'
// Two separate useTime calls with the same id
const time = useTime('synced-timeline')
const time2 = useTime('synced-timeline')
// Create animations from DIFFERENT store references
const rotate1 = derived(time, (t) => (t / 10) % 360)
const rotate2 = derived(time2, (t) => (t / 10) % 360)
const scale1 = derived(time, (t) => 1 + Math.sin(t / 800) * 0.2)
const scale2 = derived(time2, (t) => 1 + Math.sin(t / 800) * 0.2)
const hue = derived(time, (t) => (t / 30) % 360)
</script>
<div>
<!-- Element A uses 'time' -->
<div style="
transform: rotate({$rotate1}deg) scale({$scale1});
background: linear-gradient(135deg, hsl({$hue}, 70%, 60%), hsl({$hue + 30}, 70%, 50%));
">
A
</div>
<!-- Element B uses 'time2' -->
<div style="
transform: rotate({$rotate2}deg) scale({$scale2});
background: linear-gradient(135deg, hsl({$hue + 120}, 70%, 60%), hsl({$hue + 150}, 70%, 50%));
">
B
</div>
<!-- Proof they're synchronized -->
<div>
time: {$time}ms = time2: {$time2}ms
</div>
</div>Both elements animate in perfect synchronization even though they use different motion-value references (time and time2). The magic happens because both useTime('synced-timeline') calls observe the same underlying timeline — independent values, shared frame loop. Watch the values display - they’re always identical! This is especially powerful when these elements live in separate components - the id parameter ensures they all stay in sync without prop drilling or context.
How it works
useTime wraps motion-dom’s MotionValue with a per-frame RAF loop:
- Creates a
MotionValue<number>that writes elapsed milliseconds on everyrequestAnimationFrame - With an
id, multiple consumers observe the same RAF loop — each call still returns an independent motion value (so destroying one doesn’t affect others) but they stay in lockstep - The RAF loop attaches at component mount via
$effectand detaches at unmount — when the last consumer of a sharedidunmounts, the loop stops; the nextuseTime(id)call restarts it
Read patterns
<script>
const time = useTime()
// Preferred — reactive in Svelte 5 templates / $derived / $effect
$effect(() => {
console.log('Elapsed time:', time.current, 'ms')
})
// Imperative (event handlers, callbacks)
function logNow() {
console.log('Now:', time.get(), 'ms')
}
// Legacy $ prefix still works via the .subscribe shim
// {$time}
</script><script>
const time = useTime()
// Preferred — reactive in Svelte 5 templates / $derived / $effect
$effect(() => {
console.log('Elapsed time:', time.current, 'ms')
})
// Imperative (event handlers, callbacks)
function logNow() {
console.log('Now:', time.get(), 'ms')
}
// Legacy $ prefix still works via the .subscribe shim
// {$time}
</script>The RAF loop starts at component mount and stops at unmount — no subscriber tracking required.
Performance
useTime is optimized for smooth animations:
- Frame-perfect: Updates at display refresh rate (typically 60 FPS)
- Efficient: Single
requestAnimationFrameloop per unique timeline - Shared resources: Multiple consumers of the same
idshare one loop - Auto-cleanup: Cancels the frame loop on component unmount
Common patterns
Smooth rotation
<script>
import { useTime } from '@humanspeak/svelte-motion'
import { derived } from 'svelte/store'
const time = useTime()
const degrees = derived(time, (t) => (t / 10) % 360)
</script>
<div style="transform: rotate({$degrees}deg)">
↻
</div><script>
import { useTime } from '@humanspeak/svelte-motion'
import { derived } from 'svelte/store'
const time = useTime()
const degrees = derived(time, (t) => (t / 10) % 360)
</script>
<div style="transform: rotate({$degrees}deg)">
↻
</div>Oscillating scale
<script>
import { useTime } from '@humanspeak/svelte-motion'
import { derived } from 'svelte/store'
const time = useTime()
const scale = derived(time, (t) => 1 + Math.sin(t / 500) * 0.2)
</script>
<div style="transform: scale({$scale})">
Pulsing
</div><script>
import { useTime } from '@humanspeak/svelte-motion'
import { derived } from 'svelte/store'
const time = useTime()
const scale = derived(time, (t) => 1 + Math.sin(t / 500) * 0.2)
</script>
<div style="transform: scale({$scale})">
Pulsing
</div>Color cycling
<script>
import { useTime } from '@humanspeak/svelte-motion'
import { derived } from 'svelte/store'
const time = useTime()
const hue = derived(time, (t) => (t / 20) % 360)
</script>
<div style="background: hsl({$hue}, 70%, 50%)">
Rainbow
</div><script>
import { useTime } from '@humanspeak/svelte-motion'
import { derived } from 'svelte/store'
const time = useTime()
const hue = derived(time, (t) => (t / 20) % 360)
</script>
<div style="background: hsl({$hue}, 70%, 50%)">
Rainbow
</div>Multiple synchronized animations
<script>
import { useTime } from '@humanspeak/svelte-motion'
import { derived } from 'svelte/store'
const time = useTime('sync')
const x = derived(time, (t) => Math.sin(t / 1000) * 50)
const y = derived(time, (t) => Math.cos(t / 800) * 30)
const rotate = derived(time, (t) => (t / 15) % 360)
const scale = derived(time, (t) => 1 + Math.sin(t / 600) * 0.1)
</script>
<div style="transform: translate({$x}px, {$y}px) rotate({$rotate}deg) scale({$scale})">
Complex animation
</div><script>
import { useTime } from '@humanspeak/svelte-motion'
import { derived } from 'svelte/store'
const time = useTime('sync')
const x = derived(time, (t) => Math.sin(t / 1000) * 50)
const y = derived(time, (t) => Math.cos(t / 800) * 30)
const rotate = derived(time, (t) => (t / 15) % 360)
const scale = derived(time, (t) => 1 + Math.sin(t / 600) * 0.1)
</script>
<div style="transform: translate({$x}px, {$y}px) rotate({$rotate}deg) scale({$scale})">
Complex animation
</div>API Reference
Parameters
- id
string(optional) - Timeline identifier for sharing across components
Returns
An AugmentedMotionValue<number> — a real motion-dom MotionValue containing elapsed milliseconds since creation, plus:
.current— Svelte 5 reactive getter (templates,$derived,$effect)..subscribe(run)— Svelte readable store contract (powers$timetemplate syntax andderived(time, …))..get()/.on('change', cb)/ all other motion-value methods from motion-dom.
The motion value’s lifecycle is bound to the surrounding $effect: the RAF loop starts at mount and stops at unmount. For shared id timelines the loop stops only when the last consumer unmounts.
When to use
Use useTime when you need:
- Reactive time values - Integrate time into Svelte’s reactive system
- Store-based animations - Derive multiple animated values from one timeline
- Cross-component synchronization - Keep separate components in sync with shared timeline
id - Declarative time - Use
$timesyntax for clean, reactive code
For direct DOM manipulation or frame-by-frame control, consider useAnimationFrame instead.
Comparison with useAnimationFrame
| Feature | useTime | useAnimationFrame |
|---|---|---|
| Returns | Reactive store | Cleanup function |
| Usage | Declarative with $ | Imperative callback |
| Integration | Svelte stores | Direct DOM |
| Derived values | Easy with derived() | Manual calculation |
| Synchronization | Built-in with id | Manual coordination |
See also
- useAnimationFrame - For direct frame control
- useTransform - For mapping and transforming values
- styleString - For building CSS style strings with automatic unit handling
- Examples - See useTime in action
Based on Motion’s useTime API.