# anime.js v4 — Administration & Architecture Guide

## Executive Summary

anime.js v4.3.6 is a tree-shakeable JavaScript animation engine authored by Julian Garnier. It targets browsers and Node.js, providing CSS/SVG/DOM/object animation, timeline orchestration, scroll-driven animation, draggable interactions, FLIP-style layout transitions, text splitting, and a Web Animations API (WAAPI) bridge. The library ships as ES modules, CommonJS, UMD, and IIFE bundles with full TypeScript declarations. It has **zero runtime dependencies**; all dev tooling (Rollup, Mocha/Chai, BrowserSync, TypeScript) is devDependencies-only.

| Attribute | Value |
|---|---|
| Version | 4.3.6 |
| License | MIT |
| Source commit | `3b369a4` (2026-02-13) |
| Package name | `animejs` |
| Entry point | `src/index.js` |
| Bundle sizes | UMD (`anime.umd.min.js`), IIFE (`anime.iife.min.js`), ES modules, CJS |
| Node.js compat | Yes (`setImmediate`/`clearImmediate` replaces rAF) |
| TypeScript | Full `.d.ts` declarations generated via `tsc` |
| Tree-shaking | `"sideEffects": false` in package.json |

---

## Architecture

### High-Level Design

anime.js is structured as a **layered class hierarchy** built on a shared timing core with an intrusive linked-list data structure for managing children (tweens, animations, timelines).

```
┌─────────────────────────────────────────────────────┐
│                    Engine (singleton)                │
│         requestAnimationFrame / setImmediate         │
│              ┌──────────┴──────────┐                │
│              │   Linked-list of    │                │
│              │    active Timers    │                │
│              └──────────┬──────────┘                │
├─────────────────────────┼───────────────────────────┤
│  Timer ← Clock          │         Timeline ← Timer  │
│  JSAnimation ← Timer    │         WAAPIAnimation     │
├─────────────────────────┼───────────────────────────┤
│            Composition Layer (replace/none/blend)    │
├─────────────────────────────────────────────────────┤
│  Animatable  │  Draggable  │  ScrollObserver        │
│  AutoLayout  │  TextSplitter│  Scope                │
└─────────────────────────────────────────────────────┘
```

### Class Hierarchy

```
Clock                         Base timing: deltaTime, fps, speed, requestTick()
├── Engine (singleton)        RAF loop, document visibility, timeUnit, precision
└── Timer                     Playback: play/pause/resume/reverse/seek/cancel/revert
    ├── JSAnimation           Property tweening, keyframes, composition, targets
    └── Timeline              Sequencing: add(), sync(), set(), label(), stretch()

Scope                         Framework integration, scoped cleanup, media queries
ScrollContainer               Scroll position tracking, resize observer
ScrollObserver                Scroll-linked playback (sync modes)
Animatable                    Reactive property animation (getter/setter pattern)
Draggable                     Pointer-driven drag with snap, bounds, inertia
AutoLayout                    FLIP-based layout animation
LayoutSnapshot                Captures DOM measurements for FLIP
TextSplitter                  Text segmentation (lines, words, chars)
WAAPIAnimation                Web Animations API bridge
DOMProxy                      Non-DOM target wrapper for Draggable
Transforms                    Matrix/transform resolution for Draggable
Spring                        Physics-based easing with mass/stiffness/damping
```

### Module Map

The source is organized into 15 sub-packages under `src/`, each with its own `index.js` re-export:

| Module | Path | Files | Chars | Purpose |
|---|---|---|---|---|
| core | `src/core/` | 11 | 52,640 | Clock, constants, helpers, colors, globals, render, styles, targets, transforms, units, values |
| animation | `src/animation/` | 4 | 44,755 | JSAnimation class, `animate()` factory, additive blending, composition |
| timer | `src/timer/` | 2 | 15,566 | Timer class (playback control base) |
| timeline | `src/timeline/` | 3 | 12,812 | Timeline class, position parser |
| engine | `src/engine/` | 2 | 4,404 | Engine singleton, RAF/setImmediate loop |
| animatable | `src/animatable/` | 2 | 4,710 | Animatable class, `createAnimatable()` factory |
| draggable | `src/draggable/` | 2 | 44,503 | Draggable, DOMProxy, Transforms |
| events | `src/events/` | 2 | 32,887 | ScrollContainer, ScrollObserver |
| layout | `src/layout/` | 2 | 55,727 | AutoLayout, LayoutSnapshot |
| scope | `src/scope/` | 2 | 7,236 | Scope class |
| text | `src/text/` | 2 | 19,919 | TextSplitter |
| svg | `src/svg/` | 5 | 9,147 | createDrawable, createMotionPath, morphTo, getPath |
| waapi | `src/waapi/` | 3 | 20,720 | WAAPIAnimation, easing conversion, composition |
| easings | `src/easings/` | 7 sub-dirs | ~18,839 | eases, spring, cubic-bezier, linear, steps, irregular |
| utils | `src/utils/` | 7 | 20,329 | Chainable math, stagger, random, target get/set, time sync |

> [!NOTE]
> The `src/types/` directory contains JSDoc type definitions only — no runtime code. TypeScript `.d.ts` files are generated from these via `tsc -p tsconfig.types.json`.

### Package Exports Map

The library exposes **20 sub-path exports** for fine-grained tree-shaking:

| Import path | Module |
|---|---|
| `animejs` | Full library |
| `animejs/animation` | JSAnimation + animate() |
| `animejs/timer` | Timer |
| `animejs/timeline` | Timeline + createTimeline() |
| `animejs/animatable` | Animatable + createAnimatable() |
| `animejs/draggable` | Draggable + createDraggable() |
| `animejs/scope` | Scope + createScope() |
| `animejs/engine` | Engine singleton |
| `animejs/events` | ScrollObserver + onScroll() |
| `animejs/layout` | AutoLayout + createLayout() |
| `animejs/text` | TextSplitter + splitText() |
| `animejs/svg` | createDrawable, createMotionPath, morphTo |
| `animejs/waapi` | WAAPIAnimation + waapi() |
| `animejs/utils` | Math utilities, stagger, get/set |
| `animejs/easings` | All easing exports |
| `animejs/easings/eases` | Named easings (in/out/inOut/outIn) |
| `animejs/easings/spring` | Spring physics easing |
| `animejs/easings/cubic-bezier` | Cubic bezier curves |
| `animejs/easings/linear` | Linear easing with stops |
| `animejs/easings/steps` | Step easing functions |
| `animejs/easings/irregular` | Irregular easing curves |

Each export provides `types`, `require` (CJS), `import` (ESM), and `default` (CJS fallback) conditions.

---

## Internal Data Structures

### Intrusive Linked Lists

The core data structure throughout anime.js is an **intrusive doubly-linked list**. Every parent-child relationship (Engine→Timer, Timer→Tween, Timeline→child, WAAPI composition lookups) uses the same pattern:

```javascript
// Parent node
{ _head: firstChild, _tail: lastChild, _hasChildren: true }

// Child node
{ _prev: previousSibling, _next: nextSibling }
```

Operations are defined in `src/core/helpers.js`:

| Function | Complexity | Purpose |
|---|---|---|
| `addChild(parent, child, sortMethod)` | O(n) worst, O(1) append | Insert with optional sort |
| `removeChild(parent, child)` | O(1) | Unlink from list |
| `forEachChildren(parent, callback, reverse)` | O(n) | Iterate forward or backward |

> [!IMPORTANT]
> The linked list operations use configurable property names (`_prev`/`_next` by default, `_prevRep`/`_nextRep` for composition siblings, `_prevAdd`/`_nextAdd` for additive blending chains). This allows a single tween node to participate in multiple linked lists simultaneously without extra wrapper objects.

### Tween Types and Value Types

Tweens are classified by target type and value format:

**Tween types** (`src/core/consts.js`):

| Enum | Value | Description |
|---|---|---|
| `OBJECT` | 0 | Plain JavaScript object property |
| `ATTRIBUTE` | 1 | SVG/HTML attribute |
| `CSS` | 2 | CSS property via `style` |
| `TRANSFORM` | 3 | CSS transform (batched per element) |
| `CSS_VAR` | 4 | CSS custom property |

**Value types**:

| Enum | Value | Description |
|---|---|---|
| `NUMBER` | 0 | Raw number |
| `UNIT` | 1 | Number + CSS unit (px, rem, %, etc.) |
| `COLOR` | 2 | RGBA color (hex, rgb, hsl → internal [r,g,b,a]) |
| `COMPLEX` | 3 | String with embedded numbers (e.g., `"0px 10px 20px rgba(0,0,0,0.5)"`) |

### Composition System

Three composition modes control how overlapping animations interact (`src/animation/composition.js`):

| Mode | Enum | Behavior |
|---|---|---|
| `replace` | 0 | Default. New animation replaces previous ones on same target+property. Previous tween is unlinked via `_prevRep`/`_nextRep` chain. |
| `none` | 1 | No composition tracking. Multiple animations stack independently. |
| `blend` | 2 | Additive blending. Values from all active animations on the same property are summed. Uses `_prevAdd`/`_nextAdd` chain and a separate additive animation object. |

> [!CAUTION]
> Known issue (#1138): `addChild` can create self-referential `_prevRep`/`_nextRep` links when re-adding an already-linked tween. This can cause infinite loops in composition resolution. Avoid rapidly starting the same animation instance while a previous run is still composing.

---

## Rendering Pipeline

### Engine Tick Loop

The `Engine` singleton (`src/engine/engine.js`) drives all animation:

1. **`engine.wake()`** — starts the RAF loop (or `setImmediate` in Node.js)
2. **`tickEngine()`** — recursive RAF callback:
   - If `engine._head` exists, request next frame and call `engine.update()`
   - Otherwise, set `reqId = 0` (auto-sleep when no active animations)
3. **`engine.update()`**:
   - `requestTick(time)` — framerate gating via `Clock._scheduledTime`
   - `computeDeltaTime(time)` — calculate time delta
   - Walk linked list of active tickables (`_head` → `_next`)
   - For each non-paused tickable: call `tick()` with computed time
   - Paused tickables: remove from engine, set `_running = false`, auto-cancel if completed
   - Call `additive.update()` for blend-mode composition

### Per-Frame Render (`src/core/render.js`)

The `render()` function handles a single tickable per frame:

1. **Time clamping** — clamp absolute time to `[-delay, duration]`
2. **Iteration calculation** — compute current loop iteration (bitwise `~~` for performance)
3. **Direction resolution** — XOR of `_reversed` and `(_alternate && isOdd)` determines effective direction
4. **Playback easing** — optional `_ease` applied to iteration time
5. **Callbacks** — `onBegin`, `onLoop`, `onBeforeUpdate` fired at appropriate transitions
6. **Tween rendering** — walk child tweens via linked list:
   - Compute eased progress: `tween._ease(time / duration)`
   - Interpolate: `lerp(fromNumber, toNumber, easedProgress)`
   - For COLOR/COMPLEX types: interpolate each number component
   - Apply `_modifier` function
   - Set value on target based on tween type
7. **Transform batching** — CSS transforms are concatenated per-element into a single `transform` string (stored via `transformsSymbol` on the target), then applied once
8. **Composition** — for `replace` mode, resolved tweens overwrite; for `blend` mode, additive values are summed across the `_prevAdd`/`_nextAdd` chain

> [!NOTE]
> `Date.now` is used instead of `performance.now` throughout. The source comments note it is "precise enough for timings calculations, performs slightly faster, and works in Node.js environment."

### Document Visibility Handling

The engine automatically pauses on `document.hidden` and resumes on visibility restore (calling `resetTime()` on all children to prevent time jumps). This can be disabled via `engine.pauseOnDocumentHidden = false`.

---

## Requirements & Compatibility

### Runtime Requirements

| Requirement | Detail |
|---|---|
| Browsers | Any browser supporting ES2015+ (Map, Symbol, Proxy, WeakMap) |
| Node.js | Requires `setImmediate` (available natively; no polyfill needed) |
| Optional | `Intl.Segmenter` for accurate Unicode text splitting (TextSplitter) |
| Optional | `ResizeObserver` for ScrollContainer dimension tracking |
| Optional | `element.animate()` for WAAPI bridge |

### Build Requirements

| Tool | Version | Purpose |
|---|---|---|
| Node.js | Any recent LTS | Build and test runner |
| Rollup | ^4.9.1 | Bundle ESM, CJS, UMD, IIFE |
| TypeScript | ^5.9.2 | Type declaration generation |
| @rollup/plugin-terser | ^0.4.4 | Minification |
| Mocha | ^10.2.0 | Test framework |
| Chai | ^4.3.10 | Assertion library |
| BrowserSync | ^3.0.4 | Browser test server |
| nodemon | ^3.1.10 | Dev watch mode |
| three | ^0.160.0 | Three.js playground tests only |

---

## Configuration Reference

### Engine Configuration

The `engine` singleton exposes these configuration surfaces:

| Property | Type | Default | Description |
|---|---|---|---|
| `engine.timeUnit` | `'ms'` \| `'s'` | `'ms'` | Switches global time scale between milliseconds and seconds. Setting to `'s'` multiplies `globals.timeScale` by 0.001 and adjusts default duration. |
| `engine.speed` | `Number` | `1` | Global playback speed multiplier. Affects all active tickables. |
| `engine.precision` | `Number` | `4` | Decimal places for rounding interpolated values. |
| `engine.fps` | `Number` | `240` | Maximum frames per second. Lower values skip frames. |
| `engine.useDefaultMainLoop` | `Boolean` | `true` | When `false`, engine does not auto-start RAF. Call `engine.update()` manually. |
| `engine.pauseOnDocumentHidden` | `Boolean` | `true` | Auto-pause when tab is hidden. |

### Global Defaults

`engine.defaults` (alias for `globals.defaults` in `src/core/globals.js`):

| Default | Type | Value | Description |
|---|---|---|---|
| `duration` | `Number` | `1000` (ms) | Default animation duration |
| `delay` | `Number` | `0` | Default delay before start |
| `ease` | `String` | `'out(2)'` | Default easing function |
| `loop` | `Number\|Boolean` | `0` | Loop count (0 = no loop, `true` = infinite) |
| `reversed` | `Boolean` | `false` | Play in reverse |
| `alternate` | `Boolean` | `false` | Alternate direction each loop |
| `autoplay` | `Boolean` | `true` | Start immediately on creation |
| `persist` | `Boolean` | `false` | Keep WAAPI animations after completion |
| `composition` | `Number` | `0` (replace) | Default composition mode |
| `modifier` | `Function` | `v => v` | Value modifier applied after interpolation |
| `loopDelay` | `Number` | `0` | Delay between loop iterations |
| `playbackRate` | `Number` | `1` | Per-animation speed multiplier |
| `frameRate` | `Number` | `240` | Per-animation max FPS |
| `playbackEase` | `Function\|null` | `null` | Easing applied to overall playback progress |

> [!TIP]
> Override defaults globally for your entire application:
> ```javascript
> import { engine } from 'animejs';
> engine.defaults.duration = 500;
> engine.defaults.ease = 'inOut(3)';
> engine.timeUnit = 's'; // Switch to seconds globally
> ```

### Build Configuration

The only environment variable is `build` (checked in `rollup.config.js`). When `build=true`, Rollup produces minified production bundles. The `npm run build` script sets this automatically.

---

## Build & Development

### npm Scripts

| Script | Command | Purpose |
|---|---|---|
| `npm run dev` | `nodemon → rollup -c && tsc` | Watch mode: recompile on source changes |
| `npm run build` | `rm -rf dist && build=true rollup -c && tsc` | Production build (clean + minify) |
| `npm run test:browser` | `browser-sync start --startPath tests/index.html` | Browser tests via BrowserSync |
| `npm run test:node` | `mocha -u tdd --timeout 20000 ./tests/suites/node.test.js` | Node.js test suite |
| `npm run dev:test` | `npm run dev & npm run test:browser` | Dev + browser tests in parallel |

### Build Output (`dist/`)

```
dist/
├── modules/           ES modules + CJS (per sub-package)
│   ├── index.js       Main ESM entry
│   ├── index.cjs      Main CJS entry
│   ├── index.d.ts     TypeScript declarations
│   ├── animation/
│   ├── timer/
│   ├── timeline/
│   ├── ...            (one directory per sub-path export)
│   └── waapi/
└── bundles/           Pre-built bundles
    ├── anime.umd.js
    ├── anime.umd.min.js
    ├── anime.iife.js
    └── anime.iife.min.js
```

> [!NOTE]
> The `"files"` field in package.json restricts npm publish to the `dist/` directory only. Source code under `src/` is not included in the published package.

### TypeScript Configuration

Two tsconfig files:

| File | Purpose |
|---|---|
| `tsconfig.json` | Base config (ES module resolution, strict checks) |
| `tsconfig.types.json` | Declaration generation (`-p` flag in build script) |
| `tests/tsconfig.json` | Extends base; maps `animejs` → `dist/modules/index.js` for test imports |

---

## Testing

### Test Architecture

Tests use **Mocha** (TDD interface) with **Chai** assertions. Two test environments:

| Environment | Entry point | Runner |
|---|---|---|
| Browser | `tests/index.html` | BrowserSync serves files; Mocha runs in-page |
| Node.js | `tests/suites/node.test.js` | `mocha` CLI with `--allow-natives-syntax` |

Test suites live in `tests/suites/` (31 files, 335,861 chars — the largest directory by character count).

### Playground Tests

The `tests/playground/` directory contains 20+ interactive test pages covering:

- Playback controls, keyframes, lerp, sandbox
- Layout (including clone variants)
- Timeline (nested, test sets, seek)
- ScrollObserver (onscroll with assets)
- Draggables (callbacks)
- WAAPI (composition, eases, playback, values)
- SVG motion path, scope, timekeeper
- Three.js instanced mesh, benchmark

These are manual/visual tests, not part of the automated suite.

### Build Validation

`tests/suites/build.test.js` validates that all module entry points can be loaded without errors. It dynamically imports each sub-path export and catches module loading failures:

```
Error loading module ${path}: ${error.message}
```

---

## Security Analysis

### Attack Surface

anime.js is a **client-side library with no network I/O, no file system access, and no eval/Function constructor usage**. The attack surface is minimal:

| Vector | Risk | Mitigation |
|---|---|---|
| DOM XSS via animation values | Low | Values are set via `style` property or `setAttribute`, not `innerHTML`. Color and unit parsing use regex, not eval. |
| Prototype pollution | Low | `mergeObjects()` uses spread + `for...in` with `hasOwnProperty` guard via `isKey()`. No deep merge. |
| ReDoS (regex denial of service) | Very Low | Regex patterns are anchored and non-backtracking for the most part. Complex value parsing uses multiple simple patterns rather than nested quantifiers. |
| Supply chain | None | Zero runtime dependencies. |

### Symbol-Based Caching

Target metadata is stored using `Symbol` keys (`isRegisteredTargetSymbol`, `isDomSymbol`, `isSvgSymbol`, `transformsSymbol`, `morphPointsSymbol`, `proxyTargetSymbol`) to avoid property name collisions with user objects.

### CSS Variable Handling

CSS custom properties are parsed via `cssVariableMatchRgx` (`/var\(\s*(--[\w-]+)(?:\s*,\s*([^)]+))?\s*\)/`), which correctly extracts variable names and fallback values without executing them.

---

## Operations & Performance

### Memory Management

- **No garbage collection pressure from tween objects**: Tween decomposition reuses pre-allocated objects (`fromTargetObject`, `toTargetObject`) rather than creating new ones per frame.
- **Linked list vs. array**: Using intrusive linked lists for children means O(1) insert/remove without array shifting or splicing.
- **Transform batching**: CSS transforms are concatenated into a single string per element per frame, avoiding multiple style recalculations.
- **Engine auto-sleep**: When all animations complete and the linked list is empty, the RAF loop stops (`reqId = 0`), consuming zero CPU.

### Performance Constants

| Constant | Value | Purpose |
|---|---|---|
| `minValue` | `1e-11` | Minimum non-zero value (prevents division by zero) |
| `maxValue` | `1e12` | Infinity replacement for duration clamping |
| `K` | `1000` | Milliseconds constant |
| `maxFps` | `240` | Maximum framerate cap |
| `tickThreshold` | `200` (ms) | Large delta detection threshold (tab switching) |

### Framerate Control

The `Clock.requestTick(time)` method implements frame gating:

```javascript
requestTick(time) {
  if (time < this._scheduledTime) return tickModes.NONE; // Skip frame
  // Advance scheduled time by at least one frameDuration
  this._scheduledTime += frameDelta < frameDuration ? frameDuration : frameDelta;
  return tickModes.AUTO;
}
```

This allows per-animation `fps` caps. An animation with `fps: 30` inside an engine running at 60fps will only update every other frame.

### DevTools Integration

If `window.AnimeJSDevTools` is present at initialization, the engine registers with it:

- Each Timer/Animation registers via `devTools.register(this, parameters, isInfinite)`
- Infinite loops are capped to `devTools.maxInfiniteLoops` iterations in dev mode
- Multiple anime.js instances are tracked via `window.AnimeJS` array (each pushes a `{ version, engine }` entry)

---

## Module Deep-Dive

### Core Module (`src/core/`)

The 11-file core module is the foundation everything else builds on:

| File | Purpose |
|---|---|
| `clock.js` | `Clock` base class: deltaTime, fps, speed, frame gating |
| `consts.js` | All enums (tweenTypes, valueTypes, tickModes, compositionTypes), regex patterns, transform lists, Symbol keys |
| `globals.js` | Singleton state: `defaults`, `scope`, `globals`, `devTools`, version tracking |
| `helpers.js` | Type checkers, math utilities (clamp, round, snap, lerp), linked list operations (addChild, removeChild, forEachChildren) |
| `colors.js` | Color conversion: hex→rgba, rgb→rgba, hsl→rgba. All paths produce `[r, g, b, a]` arrays. |
| `render.js` | `render()` — the per-frame interpolation + value application function. `tick()` — top-level tick dispatcher. |
| `styles.js` | CSS property name sanitization, inline style management |
| `targets.js` | Target resolution: CSS selectors, NodeLists, arrays, objects → normalized `TargetsArray`. Registration via Symbol caching. |
| `transforms.js` | CSS transform string building and decomposition |
| `units.js` | CSS unit conversion (px↔rem↔em↔%) via temporary DOM measurement |
| `values.js` | Value decomposition, function evaluation, relative value resolution (`+=`, `-=`, `*=`), tween value setup |

### Easing System (`src/easings/`)

The easing subsystem is modular with 7 sub-packages:

| Sub-package | Key exports | Description |
|---|---|---|
| `eases/` | `eases` map, `parseEase()`, named easings | Power-based easings: `in(p)`, `out(p)`, `inOut(p)`, `outIn(p)`. String parser handles `'out(2)'`, `'inOut(3)'`, etc. |
| `spring/` | `Spring` class, `spring()` factory | Physics solver: mass, stiffness, damping, velocity. Auto-computes duration from settling time. |
| `cubic-bezier/` | `cubicBezier()` | Standard CSS cubic-bezier implementation |
| `linear/` | `linear()` | CSS `linear()` equivalent with arbitrary stops |
| `steps/` | `steps()` | Step easing with jump-start/jump-end/jump-both/jump-none |
| `irregular/` | `irregular()` | Custom curve from array of points |
| (root) | `none` | Identity easing (`v => v`) |

**Spring physics**: The `Spring` class uses a damped harmonic oscillator solver. It iterates until the spring settles within `globals.precision` tolerance, automatically computing the duration. The solver runs at construction time (not per-frame), producing a lookup-based easing function.

### Scroll System (`src/events/`)

**ScrollContainer**: Wraps a scrollable element (or `document.body` for window scroll). Tracks `scrollX`, `scrollY`, dimensions, and scale. Uses `ResizeObserver` for dimension updates. Maintains its own linked list of `ScrollObserver` children.

**ScrollObserver**: Links a Timer/Animation/Timeline to scroll position. Sync modes:

| Mode | Behavior |
|---|---|
| `playPause` | Play when entering threshold, pause when leaving |
| `linear` | Map scroll position linearly to animation progress |
| `smooth` | `lerp`-smoothed scroll-to-progress mapping |
| `easing` | Apply easing function to scroll-to-progress mapping |

Threshold parsing supports absolute pixels, viewport-relative percentages, and callback functions.

### Layout System (`src/layout/`)

**LayoutSnapshot**: Captures DOM element positions (bounding rect, computed transforms, margins, padding) for FLIP calculation. Handles both regular elements and SVG.

**AutoLayout**: Implements FLIP (First-Last-Invert-Play) animation:

1. Capture "first" state via `LayoutSnapshot`
2. Apply DOM mutation
3. Capture "last" state
4. Compute delta (translate + scale)
5. Apply inverted delta as starting transform
6. Animate to identity

Supports `enterFrom`, `leaveTo`, and `swapAt` states for add/remove/reorder transitions. Internally creates a Timeline (or WAAPI animation) to drive the FLIP.

### Draggable System (`src/draggable/`)

**DOMProxy**: Lightweight wrapper that gives plain objects a DOM-like interface (`getBoundingClientRect`, `classList`, `x`, `y`, `width`, `height`). Used for non-DOM draggable targets.

**Transforms**: Helper that resolves the current DOMMatrix for an element's parent chain, computes the inverse matrix, and converts screen-space pointer deltas to local-space deltas. This allows correct drag behavior on transformed/rotated/scaled elements.

**Draggable**: Full drag implementation with:
- Touch and mouse pointer handling
- Configurable axes (`x`, `y`, or both)
- Container bounds with overflow modes
- Snap points (grid-based or array-based)
- Momentum/inertia with spring-based settling
- Cursor management (grab/grabbing states)
- Event callbacks: `onGrab`, `onDrag`, `onRelease`, `onSettle`, `onSnap`, `onUpdate`

Uses `Animatable` internally for smooth animated property updates.

### Text Splitting (`src/text/`)

**TextSplitter**: Segments text content into lines, words, and characters using `Intl.Segmenter` (with fallback to regex splitting). Features:

- Wraps segments in `<span>` elements with `data-line`, `data-word`, `data-char` attributes
- Clone mode: creates duplicate elements offset by `top`/`bottom`/`left`/`right` for reveal effects
- Wrap mode: adds overflow-hidden containers around each segment
- Template customization via `class` parameter
- Line detection: measures element positions post-split to group characters into actual rendered lines
- Preserves `aria-hidden` on decorative duplicates
- `revert()` restores original DOM

### WAAPI Bridge (`src/waapi/`)

**WAAPIAnimation**: Translates anime.js animation parameters into native `Element.animate()` calls:

- Converts anime.js easing functions to CSS `linear()` strings via sampling
- Maps `compositionTypes` to WAAPI `composite` modes
- Manages a global linked list of active WAAPI animations for composition tracking
- Handles `commitStyles()` before cancellation to preserve final values
- Supports scroll-linked WAAPI via timeline sync

**Easing conversion** (`parseWAAPIEasing`): Caches converted easings in `WAAPIEasesLookups`. Native CSS timing functions (`linear`, `ease`, `cubic-bezier()`, `steps()`) pass through directly. Spring and custom easings are sampled to 100 points and converted to `linear()` strings.

---

## Error Messages

The library produces very few runtime errors, all from specific modules:

| Error | Source | Condition |
|---|---|---|
| `"Can't morph an SVG of type..."` | `src/svg/morphto.js:28` | `morphTo()` called on non-path SVG element |
| `"Can't morph between different SVG types"` | `src/svg/morphto.js:32` | Source and target SVG elements have different tag names |
| `"Can't morph path..."` | `src/svg/morphto.js:36` | Path data cannot be parsed for morphing |

> [!WARNING]
> `getPath()` in `src/svg/helpers.js` uses `console.warn` (not throw) when a selector doesn't resolve to a valid `SVGGeometryElement`. This means invalid SVG selectors fail silently with an `undefined` return rather than throwing.

---

## Code Patterns & Conventions

### Coding Style

| Pattern | Usage |
|---|---|
| ES modules | `import`/`export` throughout; no CommonJS in source |
| JSDoc types | All parameters and return types annotated; `@import` syntax for cross-module types |
| No classes for utilities | Factory functions (`animate()`, `createTimeline()`, etc.) wrap class constructors |
| Enum via objects | `tweenTypes`, `valueTypes`, `tickModes`, `compositionTypes` — plain objects with number values |
| Bitwise operators | `~~` for `Math.floor`, `^` for XOR in direction calculation |
| IIFE singletons | `engine` and `shortTransforms` use `/*#__PURE__*/(() => ...)()` for tree-shaking hints |
| Proxy pattern | SVG drawable, chainable utilities, Animatable property access |
| Symbol for metadata | Target registration and transform caching use Symbol keys |

### Naming Conventions

| Convention | Examples |
|---|---|
| Prefixed private properties | `_currentTime`, `_head`, `_tail`, `_ease`, `_speed` |
| `$` prefix for DOM elements | `$el`, `$scalled`, `$measure` |
| Factory functions | `createTimeline()`, `createAnimatable()`, `createDraggable()`, `createScope()` |
| Short aliases | `K` = 1000, `isUnd` = isUndefined, `isFnc` = isFunction, `isStr` = isString |

### Tree-Shaking Design

- `"sideEffects": false` in package.json
- `/*#__PURE__*/` annotations on singleton IIFEs
- Each module re-exports only from its own `index.js`
- Sub-path exports allow importing only needed functionality
- No circular dependencies between sibling modules (all share `core` as common dependency)

---

## Known Issues & Community Reports

### Open Bugs (from GitHub Issues as of source commit)

| Issue | Summary | Impact |
|---|---|---|
| #1138 | `addChild` self-referential `_prevRep`/`_nextRep` on re-add | Can cause infinite loops in composition |
| #1157 | Draggables restricted to viewport boundaries with container | Bounds calculation incorrect when container is scrollable |
| #1154 | `utils.set` parses `backgroundImage` incorrectly | Incorrect value decomposition for complex CSS values |
| #1118 | Spring easing: perceived complete callback/duration mismatch | Spring may visually settle before `onComplete` fires |
| #1114 | SVG fill ignored for color names | Named colors (e.g., `red`) not recognized in SVG fill animation |
| #1121 | WAAPI synced to timeline: looping bugs | Loop count mismatch between WAAPI and timeline |
| #1120 | WAAPI animation repeats on scroll reset | `onScroll` WAAPI doesn't properly handle reverse scroll |
| #1119 | Inaccurate viewport height in Safari 12.2.1 | `100lvh` unit unsupported in older Safari |
| #1137 | Text scramble animation issue | TextSplitter behavior with dynamic content |
| #1124 | Comment syntax error causing crash | Edge case in source parsing |

### Notable Feature Requests

| Issue | Request |
|---|---|
| #1161 | Controlling speed of timeline synced to ScrollObserver |
| #1147 | Custom durations/easings per element with Layout |
| #1017 | React Native support |
| #1007 | ScrollObserver: play immediately if target already in viewport |
| #1141 | Option to suppress logs in module builds |
| #1084 | Improving text char option |

---

## Troubleshooting

### Animations Not Starting

1. **Check autoplay**: Default is `true`, but if overridden globally via `engine.defaults.autoplay = false`, you must call `.play()` manually.
2. **Scope cleanup**: If using `Scope`, ensure the scope hasn't been reverted. Reverted animations are cancelled.
3. **Engine paused**: Check `engine.paused`. Tab visibility changes auto-pause the engine.
4. **Zero duration**: A duration of `0` or `minValue` creates a "setter" (instant jump), not an animation.

### WAAPI Animations Disappearing

WAAPI animations call `commitStyles()` then `cancel()` when they finish (unless `persist: true`). If styles aren't sticking:

- Set `persist: true` to keep the WAAPI animation alive
- Check that `commitStyles()` isn't failing silently (wrapped in try/catch in source)

### Transform Conflicts

anime.js batches all CSS transforms for an element into a single `transform` string per frame. If external code modifies `element.style.transform` between frames, those changes will be overwritten. Use anime.js for all transform manipulation on animated elements.

### Spring Duration Calculation

Springs auto-calculate duration based on settling time. If animations appear to end too early or `onComplete` fires before visual settling:

- Increase `globals.precision` (default 4) for tighter convergence
- Adjust spring parameters (reduce `stiffness`, increase `damping`)
- Known issue #1118: perceived duration may not match calculated duration

### Node.js Usage

anime.js detects non-browser environments via `typeof window !== 'undefined'`. In Node.js:

- `requestAnimationFrame` → `setImmediate`
- `cancelAnimationFrame` → `clearImmediate`
- DOM-specific features (CSS, SVG, scroll, text splitting, WAAPI) are unavailable
- Object property animation and timer/timeline features work normally

---

## Quick Reference

### Factory Functions

| Function | Module | Returns |
|---|---|---|
| `animate(targets, params)` | `animejs/animation` | `JSAnimation` |
| `createTimeline(params)` | `animejs/timeline` | `Timeline` |
| `createAnimatable(targets, params)` | `animejs/animatable` | `Animatable` |
| `createDraggable(targets, params)` | `animejs/draggable` | `Draggable[]` |
| `createScope(params)` | `animejs/scope` | `Scope` |
| `onScroll(params)` | `animejs/events` | `ScrollObserver` |
| `waapi(targets, params)` | `animejs/waapi` | `WAAPIAnimation` |
| `createLayout(targets, params)` | `animejs/layout` | `AutoLayout` |
| `splitText(targets, params)` | `animejs/text` | `TextSplitter` |
| `createDrawable(selector, start, end)` | `animejs/svg` | `DrawableSVGGeometry[]` |
| `createMotionPath(pathEl)` | `animejs/svg` | Motion path helper |
| `morphTo(fromPath, toPath)` | `animejs/svg` | Morph easing function |
| `stagger(value, params)` | `animejs/utils` | Stagger function |
| `spring(params)` | `animejs/easings/spring` | `Spring` easing |
| `cubicBezier(x1, y1, x2, y2)` | `animejs/easings/cubic-bezier` | Easing function |
| `steps(count, position)` | `animejs/easings/steps` | Step easing function |
| `linear(stops)` | `animejs/easings/linear` | Linear easing function |

### Timer/Animation Methods

| Method | Available on | Description |
|---|---|---|
| `.play()` | Timer, JSAnimation, Timeline | Start or restart from beginning |
| `.pause()` | Timer, JSAnimation, Timeline | Pause at current position |
| `.resume()` | Timer, JSAnimation, Timeline | Resume from paused position |
| `.reverse()` | Timer, JSAnimation, Timeline | Reverse playback direction |
| `.seek(time)` | Timer, JSAnimation, Timeline | Jump to specific time (ms) |
| `.restart()` | Timer, JSAnimation, Timeline | Reset and play from start |
| `.reset()` | Timer, JSAnimation, Timeline | Reset to initial state (paused) |
| `.cancel()` | Timer, JSAnimation, Timeline | Cancel and remove from engine |
| `.revert()` | Timer, JSAnimation, Timeline | Cancel, restore original values, remove composition |
| `.stretch(newDuration)` | Timer, JSAnimation, Timeline | Scale duration proportionally |
| `.then` | Timer, JSAnimation, Timeline | Promise interface (resolves on complete) |
| `.add(targets, params, position)` | Timeline | Add animation at position |
| `.sync(animation, position)` | Timeline | Sync existing animation to timeline |
| `.set(targets, params, position)` | Timeline | Instant property set at position |
| `.call(callback, position)` | Timeline | Execute callback at position |
| `.label(name, position)` | Timeline | Named position marker |
| `.remove(targets)` | Timeline | Remove targets from timeline |

### Utility Functions

| Function | Module | Description |
|---|---|---|
| `get(target, property, unit)` | `animejs/utils` | Get current animated value |
| `set(targets, properties)` | `animejs/utils` | Set values immediately |
| `stagger(value, params)` | `animejs/utils` | Generate staggered values |
| `clamp(v, min, max)` | `animejs/utils` | Clamp value to range |
| `round(v, decimals)` | `animejs/utils` | Round to decimal places |
| `snap(v, increment)` | `animejs/utils` | Snap to nearest increment |
| `lerp(start, end, factor)` | `animejs/utils` | Linear interpolation |
| `damp(start, end, factor, dt)` | `animejs/utils` | Frame-rate-independent lerp |
| `wrap(v, min, max)` | `animejs/utils` | Wrap value within range |
| `mapRange(inLow, inHigh, outLow, outHigh, v)` | `animejs/utils` | Map value between ranges |
| `degToRad(v)` | `animejs/utils` | Degrees to radians |
| `radToDeg(v)` | `animejs/utils` | Radians to degrees |
| `roundPad(v, decimals)` | `animejs/utils` | Round with zero-padding |
| `padStart(v, length, char)` | `animejs/utils` | String padding (start) |
| `padEnd(v, length, char)` | `animejs/utils` | String padding (end) |
| `random(min, max, decimals)` | `animejs/utils` | Random number in range |
| `randomPick(array)` | `animejs/utils` | Random array element |
| `shuffle(array)` | `animejs/utils` | Fisher-Yates shuffle |

All chainable utilities support a fluent API via Proxy-based chaining:

```javascript
import { clamp, round, mapRange } from 'animejs/utils';
const transform = mapRange(0, 100, -1, 1).clamp(-0.5, 0.5).round(2);
transform(75); // 0.5
```

---

## Contributing

### Repository Structure

```
src/                    Source modules (ES module)
├── core/               Foundation (11 files)
├── animation/          JSAnimation + composition
├── timer/              Timer base class
├── timeline/           Timeline sequencing
├── engine/             RAF loop singleton
├── animatable/         Reactive animation
├── draggable/          Drag interaction
├── events/             Scroll system
├── layout/             FLIP animations
├── scope/              Framework integration
├── text/               Text splitting
├── svg/                SVG utilities
├── waapi/              WAAPI bridge
├── easings/            Easing functions (7 sub-packages)
├── utils/              Math + DOM utilities
├── types/              JSDoc type definitions
└── index.js            Main barrel export
tests/
├── suites/             Automated tests (31 files)
├── playground/         Interactive visual tests
└── index.html          Browser test entry
```

### Development Workflow

1. `npm install` — install dev dependencies
2. `npm run dev` — watch mode with auto-recompile
3. Make changes in `src/`
4. `npm run test:browser` — verify in browser
5. `npm run test:node` — verify in Node.js
6. `npm run build` — production build

### Adding a New Module

1. Create directory under `src/` with `index.js` re-export
2. Add sub-path export to `package.json` `"exports"` map (types, require, import, default)
3. Export from `src/index.js` if it should be in the main bundle
4. Add corresponding test file in `tests/suites/`
5. Rebuild to verify tree-shaking works correctly

---

_Generated May 2026 from juliangarnier/anime at 3b369a4. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
