# How OpenMontage’s Character-Animation Pipeline Utilizes SVG Rigs and GSAP Timelines

> Explore OpenMontage's character animation pipeline. Learn how SVG rigs and GSAP timelines create MP4 videos frame-by-frame with HyperFrames.

- Repository: [Calesthio/OpenMontage](https://github.com/calesthio/OpenMontage)
- Tags: deep-dive
- Published: 2026-08-30

---

**OpenMontage’s character-animation pipeline binds hierarchical SVG rigs to JSON pose libraries and drives them with deterministic, paused GSAP timelines that the HyperFrames renderer executes frame-by-frame to produce MP4 video.**

The `calesthio/OpenMontage` repository implements a complete character-animation system that transforms static vector graphics into timed animations. This pipeline leverages **SVG rigs** for articulated character parts and **GSAP timelines** for deterministic animation control, ultimately rendering output through a headless browser environment managed by HyperFrames.

## SVG Rig Architecture and Component Structure

The foundation of the pipeline rests on specially structured SVG files where each animatable body part exists as a distinct group element. In [`ink-theater/ink-theater.js`](https://github.com/calesthio/OpenMontage/blob/main/ink-theater/ink-theater.js), the engine loads these SVGs and registers each `<g id="…">` element for programmatic access, creating a root object that exposes both the DOM structure and the GSAP animation context to the runtime.

Artists define rigs by organizing components hierarchically—such as `head`, `torso`, `leftArm`, and `rightArm`—within `<g>` tags. This structure allows the pipeline to target specific elements via stable IDs when applying transforms during playback.

### Example SVG Rig Structure

```svg
<!-- assets/character.svg -->
<svg width="200" height="400" viewBox="0 0 200 400" xmlns="http://www.w3.org/2000/svg">
  <g id="body">
    <rect x="70" y="150" width="60" height="150" fill="#fdd"/>
  </g>
  <g id="head">
    <circle cx="100" cy="100" r="50" fill="#ffd"/>
  </g>
  <g id="leftArm">
    <line x1="70" y1="180" x2="30" y2="250" stroke="#555" stroke-width="10"/>
  </g>
  <g id="rightArm">
    <line x1="130" y1="180" x2="170" y2="250" stroke="#555" stroke-width="10"/>
  </g>
</svg>

```

## Pose Libraries and Animation Data

The pipeline decouples artwork from animation timing through JSON pose libraries. The [`ink-theater/ink-puppet.js`](https://github.com/calesthio/OpenMontage/blob/main/ink-theater/ink-puppet.js) module parses these catalogs—typically named [`pose-library.json`](https://github.com/calesthio/OpenMontage/blob/main/pose-library.json)—which map rig part IDs to specific transform states including rotation, translation, and scale values.

### JSON Pose Specification

```json
{
  "idle": {
    "head":  {"rotate": 0,   "x": 0, "y": 0},
    "leftArm":  {"rotate": -10, "x": 0, "y": 0},
    "rightArm": {"rotate": 10,  "x": 0, "y": 0}
  },
  "wave": {
    "rightArm": {"rotate": 45, "x": 0, "y": -10}
  }
}

```

## Orchestrating Animation with GSAP Timelines

Rather than manipulating DOM elements directly, the pipeline constructs a `gsap.timeline()` instance configured with `paused: true` to create a deterministic, seek-safe animation sequence. This approach allows the **HyperFrames** renderer to jump to any timestamp instantly during frame recording, as documented in [`docs/PROVIDERS.md`](https://github.com/calesthio/OpenMontage/blob/main/docs/PROVIDERS.md).

### Building Deterministic Timelines

In [`ink-theater/examples/mocap-figure/ink-theater.js`](https://github.com/calesthio/OpenMontage/blob/main/ink-theater/examples/mocap-figure/ink-theater.js), the system demonstrates how motion-capture pose libraries stitch into GSAP timelines. Each keyframe becomes a `gsap.to()` call targeting specific SVG groups, with durations and easing functions precisely controlling interpolation between poses. The timeline remains paused until HyperFrames explicitly seeks through it.

### Timeline Implementation Example

```javascript
import {gsap} from "gsap";
import {loadSVG, applyPose} from "./ink-puppet.js";

async function createCharacterAnimation(svgUrl, poseLib) {
  const root = await loadSVG(svgUrl);           // injects SVG into DOM
  const tl   = gsap.timeline({paused: true});   // deterministic timeline

  // Idle pose (0–2 seconds)
  tl.to(root.getElementById("head"),      {duration: 2, rotate: 0}, 0);
  tl.to(root.getElementById("leftArm"),  {duration: 2, rotate: -10}, 0);
  tl.to(root.getElementById("rightArm"), {duration: 2, rotate: 10}, 0);

  // Wave pose (2–4 seconds)
  tl.to(root.getElementById("rightArm"), {duration: 0.5, rotate: 45}, 2);
  tl.to(root.getElementById("rightArm"), {duration: 0.5, rotate: -45}, 2.5);
  tl.to(root.getElementById("rightArm"), {duration: 0.5, rotate: 45}, 3);
  tl.to(root.getElementById("rightArm"), {duration: 0.5, rotate: 0}, 3.5);

  return tl;   // HyperFrames consumes this timeline
}

```

## Rendering with HyperFrames

The final stage executes within the **HyperFrames** runtime environment, detailed in [`skills/core/hyperframes.md`](https://github.com/calesthio/OpenMontage/blob/main/skills/core/hyperframes.md). This system spawns a headless Chromium instance, injects both the SVG rig and the paused GSAP timeline, then captures canvas frames at the specified framerate using FFmpeg. The architecture diagram in [`docs/ARCHITECTURE.md`](https://github.com/calesthio/OpenMontage/blob/main/docs/ARCHITECTURE.md) illustrates this rendering block at line 211.

### CLI Execution

```bash
npx hyperframes render \
  --svg assets/character.svg \
  --timeline timeline.json \
  --output renders/final.mp4 \
  --duration 4 \
  --fps 30

```

## Key Source Files

- [`ink-theater/ink-theater.js`](https://github.com/calesthio/OpenMontage/blob/main/ink-theater/ink-theater.js): Loads SVG rigs and initializes the GSAP context
- [`ink-theater/ink-puppet.js`](https://github.com/calesthio/OpenMontage/blob/main/ink-theater/ink-puppet.js): Parses JSON pose libraries and applies transforms to SVG groups
- [`skills/pipelines/character-animation/proposal-director.md`](https://github.com/calesthio/OpenMontage/blob/main/skills/pipelines/character-animation/proposal-director.md): Outlines the end-to-end pipeline architecture
- [`skills/core/hyperframes.md`](https://github.com/calesthio/OpenMontage/blob/main/skills/core/hyperframes.md): Documents the HTML/GSAP rendering engine
- [`lib/verify_scene_pacing.py`](https://github.com/calesthio/OpenMontage/blob/main/lib/verify_scene_pacing.py): Validates GSAP timeline timing against expected beats
- [`docs/ARCHITECTURE.md`](https://github.com/calesthio/OpenMontage/blob/main/docs/ARCHITECTURE.md): Contains the character-animation block diagram
- [`docs/PROVIDERS.md`](https://github.com/calesthio/OpenMontage/blob/main/docs/PROVIDERS.md): Describes the binding of SVG rigs, pose libraries, and GSAP timelines

## Summary

- OpenMontage uses **SVG group elements** (`<g>`) as rig components, loaded and registered via [`ink-theater.js`](https://github.com/calesthio/OpenMontage/blob/main/ink-theater.js)
- **JSON pose libraries** define transform states parsed by [`ink-puppet.js`](https://github.com/calesthio/OpenMontage/blob/main/ink-puppet.js) and applied to rig parts
- **GSAP timelines** are constructed with `paused: true` for deterministic, seek-safe animation control
- **HyperFrames** renders the final output by executing the timeline in a headless Chromium browser and encoding with FFmpeg
- The pipeline outputs MP4 video files generated entirely from vector-based character rigs and declarative animation data

## Frequently Asked Questions

### How does the pipeline ensure frame-accurate rendering?

The `gsap.timeline()` is instantiated with the `paused: true` configuration, creating a deterministic animation that does not progress automatically. This allows the HyperFrames renderer to seek to specific timestamps using GSAP’s internal mechanisms, ensuring each frame is captured at the exact correct moment before FFmpeg encoding begins.

### What file formats does the character-animation pipeline produce?

According to the repository's [`README.md`](https://github.com/calesthio/OpenMontage/blob/main/README.md) and [`docs/PROVIDERS.md`](https://github.com/calesthio/OpenMontage/blob/main/docs/PROVIDERS.md), the primary output is an MP4 video file stored in the `renders/` directory. The system also generates preview JSON files containing pose-to-timestamp mappings for quality assurance and validation by [`lib/verify_scene_pacing.py`](https://github.com/calesthio/OpenMontage/blob/main/lib/verify_scene_pacing.py).

### Can artists modify rig poses without changing the timeline code?

Yes. The architecture specifically decouples rig definitions from animation timing through JSON pose libraries. Artists can edit [`pose-library.json`](https://github.com/calesthio/OpenMontage/blob/main/pose-library.json) files to adjust rotation, translation, and scale values for any rig part ID, while the timeline logic in [`ink-theater/ink-puppet.js`](https://github.com/calesthio/OpenMontage/blob/main/ink-theater/ink-puppet.js) remains unchanged, simply reading the updated values during execution.

### Which runtime environment executes the GSAP timelines?

The **HyperFrames** system, documented in [`skills/core/hyperframes.md`](https://github.com/calesthio/OpenMontage/blob/main/skills/core/hyperframes.md), provides the execution environment. It loads the SVG rig and GSAP timeline into a headless Chromium instance, renders each frame to a canvas, and pipes the output to FFmpeg for final video encoding.