How OpenMontage’s Character-Animation Pipeline Utilizes SVG Rigs and GSAP Timelines
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, 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
<!-- 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 module parses these catalogs—typically named pose-library.json—which map rig part IDs to specific transform states including rotation, translation, and scale values.
JSON Pose Specification
{
"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.
Building Deterministic Timelines
In 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
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. 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 illustrates this rendering block at line 211.
CLI Execution
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: Loads SVG rigs and initializes the GSAP contextink-theater/ink-puppet.js: Parses JSON pose libraries and applies transforms to SVG groupsskills/pipelines/character-animation/proposal-director.md: Outlines the end-to-end pipeline architectureskills/core/hyperframes.md: Documents the HTML/GSAP rendering enginelib/verify_scene_pacing.py: Validates GSAP timeline timing against expected beatsdocs/ARCHITECTURE.md: Contains the character-animation block diagramdocs/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 viaink-theater.js - JSON pose libraries define transform states parsed by
ink-puppet.jsand applied to rig parts - GSAP timelines are constructed with
paused: truefor 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 and 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.
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 files to adjust rotation, translation, and scale values for any rig part ID, while the timeline logic in 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, 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →