> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/BintzGavin/helios/llms.txt
> Use this file to discover all available pages before exploring further.

# Animation system

> Using CSS animations, WAAPI, and animation libraries with Helios

Helios drives the browser's native animation engine rather than reimplementing animation in JavaScript. This means your existing CSS animations, WAAPI timelines, and animation libraries work out of the box.

## CSS animations

Standard CSS `@keyframes` animations work natively with Helios when `autoSyncAnimations` is enabled.

### Basic CSS animation

```css theme={null}
@keyframes fadeIn {
  from {
    opacity: 0;
    transform: scale(0.9);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

.title {
  animation: fadeIn 2s ease-out forwards;
}
```

```typescript theme={null}
import { Helios } from '@helios-project/core';

const helios = new Helios({
  duration: 5,
  fps: 30,
  autoSyncAnimations: true  // Enable CSS animation sync
});

window.helios = helios;
helios.bindToDocumentTimeline();
```

With `autoSyncAnimations: true`, Helios automatically:

1. Discovers all CSS animations via `document.getAnimations()`
2. Sets `animation.currentTime` to match the timeline
3. Pauses animations to prevent drift

See `packages/core/src/drivers/DomDriver.ts:372` for the WAAPI sync implementation.

### Animation timing

CSS animation duration maps directly to composition time:

```css theme={null}
/* 3-second animation */
.element {
  animation: slideIn 3s ease-out forwards;
}
```

At 30fps:

* Frame 0: Animation at 0% (start state)
* Frame 45: Animation at 50% (1.5 seconds)
* Frame 90: Animation at 100% (end state)

### Animation delay

```css theme={null}
.element {
  animation: fadeIn 2s ease-out 1s forwards;
  /*                            ↑ 1 second delay */
}
```

Animation won't start until 1 second into the composition (frame 30 at 30fps).

### Multiple animations

```css theme={null}
.element {
  animation: 
    fadeIn 1s ease-out forwards,
    slideUp 1s ease-out forwards,
    rotate 2s linear infinite;
}
```

All animations are synchronized to the same timeline.

## Web Animations API (WAAPI)

The Web Animations API provides programmatic animation control while still using the browser's rendering engine.

### Creating WAAPI animations

```typescript theme={null}
const element = document.querySelector('.box');

const animation = element.animate(
  [
    { transform: 'translateX(0px)', opacity: 0 },
    { transform: 'translateX(500px)', opacity: 1 }
  ],
  {
    duration: 2000,  // 2 seconds
    easing: 'ease-out',
    fill: 'forwards'
  }
);

// Helios automatically syncs this animation when autoSyncAnimations is enabled
```

### Manual WAAPI control

Without `autoSyncAnimations`, you can manually control WAAPI animations:

```typescript theme={null}
const helios = new Helios({
  duration: 5,
  fps: 30,
  autoSyncAnimations: false
});

const animation = element.animate([...], { duration: 2000 });

helios.currentTime.subscribe(time => {
  animation.currentTime = time * 1000;  // Convert to milliseconds
  animation.pause();
});
```

### Shadow DOM support

Helios automatically discovers animations in Shadow DOM when using DomDriver:

```typescript theme={null}
class MyComponent extends HTMLElement {
  constructor() {
    super();
    const shadow = this.attachShadow({ mode: 'open' });
    shadow.innerHTML = `
      <style>
        @keyframes pulse {
          0%, 100% { opacity: 1; }
          50% { opacity: 0.5; }
        }
        .inner {
          animation: pulse 2s infinite;
        }
      </style>
      <div class="inner">Pulsing element</div>
    `;
  }
}

customElements.define('my-component', MyComponent);
```

The DomDriver scans shadow roots automatically (see `DomDriver.ts:120`).

## Animation libraries

Helios works with popular animation libraries that use WAAPI under the hood or provide time-based APIs.

### GSAP

<Tabs>
  <Tab title="Time-based (recommended)">
    ```typescript theme={null}
    import gsap from 'gsap';
    import { Helios } from '@helios-project/core';

    const helios = new Helios({ duration: 5, fps: 30 });

    // Create timeline
    const tl = gsap.timeline({ paused: true });
    tl.to('.box', { x: 500, duration: 2 });
    tl.to('.box', { rotation: 360, duration: 1 });

    // Sync with Helios time
    helios.currentTime.subscribe(time => {
      tl.time(time);
    });
    ```
  </Tab>

  <Tab title="With autoSyncAnimations">
    ```typescript theme={null}
    import gsap from 'gsap';
    import { Helios } from '@helios-project/core';

    const helios = new Helios({
      duration: 5,
      fps: 30,
      autoSyncAnimations: true  // GSAP uses WAAPI internally
    });

    gsap.to('.box', {
      x: 500,
      duration: 2,
      ease: 'power2.out'
    });

    // Helios will automatically sync this
    ```
  </Tab>
</Tabs>

See `examples/gsap-animation/` for a complete example.

### Framer Motion

```typescript theme={null}
import { motion } from 'framer-motion';
import { useVideoFrame } from '@helios-project/react';

function AnimatedBox() {
  const { currentTime } = useVideoFrame();

  return (
    <motion.div
      animate={{ x: 500, opacity: 1 }}
      transition={{ duration: 2 }}
      style={{ x: 0, opacity: 0 }}
    />
  );
}
```

With Helios's React adapter:

```typescript theme={null}
const helios = new Helios({
  duration: 5,
  fps: 30,
  autoSyncAnimations: true
});

<HeliosProvider helios={helios}>
  <AnimatedBox />
</HeliosProvider>
```

See `examples/framer-motion-animation/` for details.

### Motion One

```typescript theme={null}
import { animate } from 'motion';
import { Helios } from '@helios-project/core';

const helios = new Helios({
  duration: 5,
  fps: 30,
  autoSyncAnimations: true
});

animate('.box', { x: 500 }, { duration: 2 });
// Automatically synced via WAAPI
```

See `examples/motion-one-animation/`.

### Lottie

```typescript theme={null}
import lottie from 'lottie-web';
import { Helios } from '@helios-project/core';

const helios = new Helios({ duration: 5, fps: 30 });

const animation = lottie.loadAnimation({
  container: document.getElementById('lottie'),
  renderer: 'svg',
  loop: false,
  autoplay: false,
  path: 'animation.json'
});

helios.currentTime.subscribe(time => {
  const totalFrames = animation.totalFrames;
  const duration = animation.getDuration();
  const frame = (time / duration) * totalFrames;
  animation.goToAndStop(frame, true);
});
```

See `examples/lottie-animation/`.

## Animation helpers

Helios provides utility functions for common animation patterns.

### Interpolate

Map input values to output values with easing:

```typescript theme={null}
import { interpolate } from '@helios-project/core';

helios.subscribe(({ currentFrame }) => {
  // Fade in from frames 0-30
  const opacity = interpolate(
    currentFrame,
    [0, 30],
    [0, 1],
    { easing: t => t * t }  // Ease-in quadratic
  );
  
  element.style.opacity = opacity;
});
```

Full interpolate API at `packages/core/src/animation.ts:20`:

```typescript theme={null}
function interpolate(
  input: number,
  inputRange: number[],
  outputRange: number[],
  options?: {
    extrapolateLeft?: 'extend' | 'clamp' | 'identity',
    extrapolateRight?: 'extend' | 'clamp' | 'identity',
    easing?: (t: number) => number
  }
): number
```

<Tabs>
  <Tab title="Basic">
    ```typescript theme={null}
    // Linear interpolation
    const x = interpolate(frame, [0, 100], [0, 500]);
    // frame 0 → x = 0
    // frame 50 → x = 250
    // frame 100 → x = 500
    ```
  </Tab>

  <Tab title="Multi-segment">
    ```typescript theme={null}
    // Different speeds in different ranges
    const y = interpolate(
      frame,
      [0, 30, 60, 90],
      [0, 100, 150, 500]
    );
    // Slow: frames 0-30 → 0-100 (fast)
    // Slow: frames 30-60 → 100-150 (slow)
    // Fast: frames 60-90 → 150-500 (fast)
    ```
  </Tab>

  <Tab title="Extrapolation">
    ```typescript theme={null}
    const scale = interpolate(
      frame,
      [30, 60],
      [1, 2],
      {
        extrapolateLeft: 'clamp',   // Stay at 1 before frame 30
        extrapolateRight: 'extend'  // Continue growing after 60
      }
    );
    ```
  </Tab>
</Tabs>

### Spring

Physics-based spring animations:

```typescript theme={null}
import { spring } from '@helios-project/core';

helios.subscribe(({ currentFrame, fps }) => {
  const y = spring({
    frame: currentFrame,
    fps,
    from: 0,
    to: 500,
    config: {
      stiffness: 100,
      damping: 10,
      mass: 1
    }
  });
  
  element.style.transform = `translateY(${y}px)`;
});
```

Spring configurations:

```typescript theme={null}
// Bouncy
{ stiffness: 200, damping: 10 }

// Gentle
{ stiffness: 50, damping: 20 }

// Quick snap
{ stiffness: 300, damping: 30 }
```

See `animation.ts:136` for physics implementation.

### Calculate spring duration

```typescript theme={null}
import { calculateSpringDuration } from '@helios-project/core';

const frames = calculateSpringDuration(
  {
    fps: 30,
    from: 0,
    to: 500,
    config: { stiffness: 100, damping: 10 }
  },
  0.001  // Threshold for "settled"
);

console.log(`Spring settles in ${frames} frames`);
```

## Sequencing

Coordinate animations across time.

### Sequence helper

```typescript theme={null}
import { sequence } from '@helios-project/core';

helios.subscribe(({ currentFrame }) => {
  // Title animation: frames 0-60
  const title = sequence({
    frame: currentFrame,
    from: 0,
    durationInFrames: 60
  });
  
  if (title.isActive) {
    const opacity = interpolate(
      title.localFrame,
      [0, 30],
      [0, 1]
    );
    titleElement.style.opacity = opacity;
  }
  
  // Subtitle animation: frames 30-90 (overlaps title)
  const subtitle = sequence({
    frame: currentFrame,
    from: 30,
    durationInFrames: 60
  });
  
  if (subtitle.isActive) {
    const y = interpolate(
      subtitle.localFrame,
      [0, 60],
      [50, 0]
    );
    subtitleElement.style.transform = `translateY(${y}px)`;
  }
});
```

Sequence returns:

```typescript theme={null}
interface SequenceResult {
  localFrame: number;      // Frame relative to start (0, 1, 2, ...)
  relativeFrame: number;   // Alias for localFrame
  progress: number;        // 0 to 1 progress through duration
  isActive: boolean;       // true if within duration
}
```

See `packages/core/src/sequencing.ts:20` for implementation.

### Series helper

Place items sequentially:

```typescript theme={null}
import { series } from '@helios-project/core';

const items = [
  { id: 'intro', durationInFrames: 60 },
  { id: 'main', durationInFrames: 120 },
  { id: 'outro', durationInFrames: 60 }
];

const sequenced = series(items);
// [
//   { id: 'intro', durationInFrames: 60, from: 0 },
//   { id: 'main', durationInFrames: 120, from: 60 },
//   { id: 'outro', durationInFrames: 60, from: 180 }
// ]

helios.subscribe(({ currentFrame }) => {
  sequenced.forEach(item => {
    const seq = sequence({
      frame: currentFrame,
      from: item.from,
      durationInFrames: item.durationInFrames
    });
    
    if (seq.isActive) {
      console.log(`Showing ${item.id}`);
    }
  });
});
```

### Stagger helper

Stagger animations by a fixed interval:

```typescript theme={null}
import { stagger } from '@helios-project/core';

const boxes = document.querySelectorAll('.box');
const staggered = stagger(
  Array.from(boxes),
  10,  // 10 frame delay between each
  30   // Start at frame 30
);

helios.subscribe(({ currentFrame }) => {
  staggered.forEach((box, i) => {
    const seq = sequence({
      frame: currentFrame,
      from: box.from,
      durationInFrames: 30
    });
    
    if (seq.isActive) {
      const y = interpolate(seq.localFrame, [0, 30], [50, 0]);
      box.style.transform = `translateY(${y}px)`;
    }
  });
});
```

## Canvas and procedural animation

For canvas-based compositions, you control the render loop:

```typescript theme={null}
import { Helios } from '@helios-project/core';

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');

const helios = new Helios({
  duration: 5,
  fps: 30,
  width: 1920,
  height: 1080
});

helios.subscribe(({ currentTime, currentFrame, width, height }) => {
  // Clear canvas
  ctx.clearRect(0, 0, width, height);
  
  // Draw based on time
  const x = (currentTime / 5) * width;
  const radius = 50 + Math.sin(currentTime * Math.PI) * 20;
  
  ctx.fillStyle = '#ff6b6b';
  ctx.beginPath();
  ctx.arc(x, height / 2, radius, 0, Math.PI * 2);
  ctx.fill();
});
```

See `examples/p5-canvas-animation/` and `examples/pixi-canvas-animation/` for library integrations.

## Easing functions

Helios provides common easing functions:

```typescript theme={null}
import { Easing } from '@helios-project/core';

// Available easings
Easing.linear
Easing.easeIn.quad
Easing.easeIn.cubic
Easing.easeOut.quad
Easing.easeOut.cubic
Easing.easeInOut.quad
Easing.easeInOut.cubic
// ... and more

// Use with interpolate
const x = interpolate(
  currentFrame,
  [0, 60],
  [0, 500],
  { easing: Easing.easeOut.cubic }
);
```

See `packages/core/src/easing.ts` for the full list.

## Best practices

### Prefer CSS for simple animations

```css theme={null}
/* Good: Declarative, browser-optimized */
@keyframes slideIn {
  from { transform: translateX(-100%); }
  to { transform: translateX(0); }
}

.element {
  animation: slideIn 1s ease-out forwards;
}
```

```typescript theme={null}
// Avoid: Imperative, per-frame calculation
helios.subscribe(({ currentFrame }) => {
  const progress = currentFrame / 30;
  element.style.transform = `translateX(${progress * -100}%)`;
});
```

### Use `will-change` for better performance

```css theme={null}
.animated {
  will-change: transform, opacity;
}
```

This hints to the browser to optimize for animation.

### Avoid layout thrashing

```typescript theme={null}
// Bad: Reading and writing in a loop
elements.forEach(el => {
  const width = el.offsetWidth;  // Read (forces layout)
  el.style.width = width + 10 + 'px';  // Write
});

// Good: Batch reads, then writes
const widths = elements.map(el => el.offsetWidth);
widths.forEach((width, i) => {
  elements[i].style.width = width + 10 + 'px';
});
```

### Use hardware-accelerated properties

Prefer `transform` and `opacity` over layout-affecting properties:

```css theme={null}
/* Fast: GPU-accelerated */
@keyframes slideIn {
  from { transform: translateX(-100%); }
  to { transform: translateX(0); }
}

/* Slow: Triggers layout */
@keyframes slideIn {
  from { left: -100%; }
  to { left: 0; }
}
```

## Next steps

* Explore [drivers](/concepts/drivers) to understand time control
* Learn about [timeline control](/concepts/timeline-control) for playback
* See [compositions](/concepts/compositions) for project structure
