logo

useTime

useTime returns a Svelte readable store that updates once per animation frame with elapsed milliseconds since the store was created.

<script>
  import { useTime } from '@humanspeak/svelte-motion'
  import { derived } from 'svelte/store'

  const time = useTime()
  const rotate = derived(time, (t) => ((t % 4000) / 4000) * 360)
</script>

<div style="transform: rotate({$rotate}deg)">
  Rotating content
</div>
<script>
  import { useTime } from '@humanspeak/svelte-motion'
  import { derived } from 'svelte/store'

  const time = useTime()
  const rotate = derived(time, (t) => ((t % 4000) / 4000) * 360)
</script>

<div style="transform: rotate({$rotate}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>
0s

Shared timelines

Pass an id string to share the same timeline across multiple components. All calls with the same id will receive the same store instance, keeping animations perfectly synchronized.

Note: Within a single component, you can simply reuse the same store reference. The id parameter 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 synchronized stores:

<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>
A
B
Synchronized Timeline
time A: 0.0s
=
time B: 0.0s

Both elements animate in perfect synchronization even though they use different store references (time and time2). The magic happens because both useTime('synced-timeline') calls return the same underlying timeline. 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 leverages Svelte’s reactive store system:

  1. Creates a readable store that updates via requestAnimationFrame
  2. Returns elapsed milliseconds since the store was created
  3. With an id, returns a shared store instance for synchronization
  4. Automatically cleans up animation frames when all subscribers unsubscribe

Store lifecycle

<script>
  const time = useTime()

  // Subscribe explicitly
  const unsubscribe = time.subscribe(value => {
    console.log('Elapsed time:', value, 'ms')
  })

  // Or use $ prefix for automatic subscription
  $: console.log('Current time:', $time)
</script>
<script>
  const time = useTime()

  // Subscribe explicitly
  const unsubscribe = time.subscribe(value => {
    console.log('Elapsed time:', value, 'ms')
  })

  // Or use $ prefix for automatic subscription
  $: console.log('Current time:', $time)
</script>

The store automatically starts updating when you subscribe and stops when all subscriptions are removed.

Performance

useTime is optimized for smooth animations:

  • Frame-perfect: Updates at display refresh rate (typically 60 FPS)
  • Efficient: Single requestAnimationFrame loop per unique timeline
  • Shared resources: Multiple subscribers share the same animation frame
  • Auto-cleanup: Cancels frames when no subscribers remain

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

A Svelte Readable<number> store containing elapsed milliseconds since creation.

Methods

The returned store implements Svelte’s Readable interface:

interface Readable<T> {
  subscribe(run: (value: T) => void): () => void
}
interface Readable<T> {
  subscribe(run: (value: T) => void): () => void
}

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 $time syntax for clean, reactive code

For direct DOM manipulation or frame-by-frame control, consider useAnimationFrame instead.

Comparison with useAnimationFrame

FeatureuseTimeuseAnimationFrame
ReturnsReactive storeCleanup function
UsageDeclarative with $Imperative callback
IntegrationSvelte storesDirect DOM
Derived valuesEasy with derived()Manual calculation
SynchronizationBuilt-in with idManual coordination

See also


Based on Motion’s useTime API.