> For the complete documentation index, see [llms.txt](https://docs.flatredball.com/gum/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.flatredball.com/gum/code/files-and-fonts/animation-chains.md).

# Animation Chains

## Introduction

An `.achx` file is an FRB-style (FlatRedBall) XML animation chain — a list of named animations, where each animation is a sequence of frames that swap texture, source rectangle, and flip state over time. This is frame-by-frame "flipbook" animation, the kind you'd use for a walk cycle or an explosion sprite sheet. Gum's runtime can load an `.achx` and play it back on a `SpriteRuntime` or a `NineSliceRuntime`.

{% hint style="info" %}
Don't confuse animation chains with the [Animation](/gum/code/animationruntime.md) (`AnimationRuntime`) system. `AnimationRuntime` plays back keyframed *property* animations authored in the Gum tool (position, color, visibility, and so on) on any `GraphicalUiElement`. Animation chains are texture-swapping animations authored in an external FRB-format `.achx` file and only apply to `Sprite` and `NineSlice`. The two systems are unrelated and can be used together.
{% endhint %}

## From `.achx` to `AnimationChainList`

An `.achx` file deserializes into an `AnimationChainListSave` (namespace `Gum.Content.AnimationChain`) — an XML-serializable, disk-shaped representation. Calling `.ToAnimationChainList()` converts it into the runtime representation:

* `AnimationChainList` (namespace `Gum.Graphics.Animation`) — a `List<AnimationChain>`.
* `AnimationChain` — a `List<AnimationFrame>`, one per named animation (for example `"Walk"` or `"Explode"`).
* `AnimationFrame` — one frame: its `Texture`, a source rectangle (`LeftCoordinate`/`RightCoordinate`/`TopCoordinate`/`BottomCoordinate`, stored as UV), `FrameLength`, `FlipHorizontal`/`FlipVertical`, and optional `RelativeX`/`RelativeY` offsets.

You can index an `AnimationChainList` by chain name (`chains["Walk"]`) to get the `AnimationChain` directly.

### TextureCoordinateType: Pixel vs. UV

`AnimationFrame`'s source-rectangle fields are always stored as UV coordinates (0–1) at runtime, but the `.achx` file itself can author frames in either coordinate space via `AnimationChainListSave.CoordinateType`:

* **UV** (the default) — coordinates in the XML are already 0–1 and are copied verbatim.
* **Pixel** — coordinates in the XML are pixel values against the frame's texture. These are divided by the texture's width/height and converted to UV **at load time**, inside `AnimationFrameSave.ToAnimationFrame()`.

This conversion happens once, when the `.achx` loads — not every frame during rendering. If you ever see an animation frame showing the wrong region of a texture, check whether the `.achx`'s `CoordinateType` matches what the tool that authored it intended.

## Loading in Code

There are two ways to get an `.achx` onto a `SpriteRuntime` or `NineSliceRuntime`. Both work the same way on either type, and both work across MonoGame, KNI, FNA, Raylib, Skia, and Silk.NET.

### Auto-Load via SourceFileName

The simplest path is to assign `SourceFileName` to a path ending in `.achx`, the same way you'd assign a `.png`. Gum detects the extension, loads and converts the `.achx`, populates `AnimationChains`, and advances to the first frame automatically:

```csharp
// Initialize
var sprite = new SpriteRuntime();
sprite.SourceFileName = "MyAnimation.achx";
sprite.CurrentChainName = "Animation1";
sprite.Animate = true;
```

This is the same `SourceFileName` property documented on the [File Loading](/gum/code/files-and-fonts/file-loading.md) page — the `.achx` extension is just one more file type it recognizes.

### Manual Construction via AnimationChainListSave

If you want the `AnimationChainList` object itself — to inspect it, or to build/modify chains in code before handing them to a runtime object — load it directly instead of going through `SourceFileName`:

```csharp
// Initialize
using Gum.Content.AnimationChain;
using Gum.Graphics.Animation;

AnimationChainListSave save = AnimationChainListSave.FromFile("AnimatedFrame1.achx");
AnimationChainList chains = save.ToAnimationChainList();

// The AnimationChainList is a plain object you can inspect or modify before assigning it.
chains["Animation1"].FrameTime = 0.1f;

sprite.AnimationChains = chains;
sprite.CurrentChainName = "Animation1";
sprite.Animate = true;
```

### File Caching

Both loading paths go through `LoaderManager`, so an `.achx` loaded from the same path more than once is only read from disk once when `LoaderManager.Self.CacheTextures` is `true` (the default). See [File Loading](/gum/code/files-and-fonts/file-loading.md#file-caching) for the general caching rules, which apply here the same way they do to textures and fonts.

## Building an `AnimationChainList` in Code

No `.achx` file is required to play back an animation chain. `AnimationChainList`, `AnimationChain`, and `AnimationFrame` are plain classes with public constructors — you can build a chain entirely in code, with no XML file and no `AnimationChainListSave` involved. This is useful for procedurally generated animations, for splitting a single loaded texture into frames without authoring an `.achx`, or for generating chains from data at runtime.

The most common reason to build a chain by hand is splitting one sprite sheet texture into a grid of equal-sized frames. The following example builds a 4-frame walk cycle from a single row of a sprite sheet:

```csharp
// Initialize
using Gum.Graphics.Animation;

var texture = GumUI.ContentLoader.LoadContent<Texture2D>("WalkSpriteSheet.png");

const int frameCount = 4;
float frameWidth = texture.Width / (float)frameCount;

var chain = new AnimationChain();
chain.Name = "Walk";

for (int i = 0; i < frameCount; i++)
{
    var frame = new AnimationFrame();
    frame.Texture = texture;
    frame.LeftCoordinate = i * frameWidth / texture.Width;
    frame.RightCoordinate = (i + 1) * frameWidth / texture.Width;
    // TopCoordinate/BottomCoordinate default to 0/1, which is correct for a single-row sheet.

    chain.Add(frame);
}

// Setting FrameTime after all frames are added stamps FrameLength onto every
// frame currently in the chain. Frames added afterward won't get it.
chain.FrameTime = 0.1f;

var chains = new AnimationChainList();
chains.Add(chain);

sprite.AnimationChains = chains;
sprite.CurrentChainName = "Walk";
sprite.Animate = true;
```

* `AnimationFrame.LeftCoordinate`/`RightCoordinate`/`TopCoordinate`/`BottomCoordinate` are UV coordinates, the same representation described above under [TextureCoordinateType: Pixel vs. UV](#texturecoordinatetype-pixel-vs-uv). If you're working from a pixel-space rectangle instead — for example a grid cell from a sprite sheet — divide by the texture's width/height to convert, as the loop above does.
* `AnimationChainList` indexes by chain name (`chains["Walk"]`), which is why `AnimationChain.Name` must be set if you rely on that lookup.
* `AnimationFrame` also supports `FlipHorizontal`/`FlipVertical`, and the same optional `RelativeX`/`RelativeY` offsets described under [Per-Frame Position Offsets (Sprite Only)](#per-frame-position-offsets-sprite-only).

Texture loading works the same as anywhere else in Gum — see [File Loading](/gum/code/files-and-fonts/file-loading.md) for `LoaderManager`, caching, and the `RelativeDirectory` rules that apply here too.

## Playback

Once `AnimationChains` is populated, set `CurrentChainName` to the chain you want to play and set `Animate` to `true`. From there, the chain advances on its own — every call to `GumService.Default.Update` drives the animation for the entire visual tree, so you don't need to advance frames yourself.

{% hint style="warning" %}
Screenshot/GIF needed: an animated GIF showing a `SpriteRuntime` playing back an `.achx` chain frame-by-frame.
{% endhint %}

If you need lower-level control — for example, driving animation outside of `GumService.Default.Update` — see the [AnimateSelf](/gum/code/gum-code-reference/graphicaluielement/animateself.md) page.

## Per-Frame Position Offsets (Sprite Only)

Each `AnimationFrame` carries optional `RelativeX`/`RelativeY` values, and `Sprite` applies them as a position offset on every render while an animation with those values is playing. `NineSlice` does not apply `RelativeX`/`RelativeY` at all — a `NineSliceRuntime` ignores them even if the source `.achx` sets them.

These offsets are authored against FlatRedBall's center-anchored `Sprite`, while Gum positions from a configurable origin (top-left by default). For a `SpriteRuntime` with a fixed size this difference doesn't matter, but on a sprite whose size tracks its source rectangle (for example `HeightUnits = PercentageOfSourceFile`), the offset can visibly drift from what FRB's `AnimationEditor` previewed.

## Related Pages

* [File Loading](/gum/code/files-and-fonts/file-loading.md) — `RelativeDirectory`, file caching, and general file loading rules that also apply to `.achx` files.
* [AnimateSelf](/gum/code/gum-code-reference/graphicaluielement/animateself.md) — the method that advances animation chain playback each frame.
* [SpriteRuntime](/gum/code/standard-visuals/spriteruntime.md) and [NineSliceRuntime](/gum/code/standard-visuals/ninesliceruntime.md) — the two runtime types that can play animation chains.
* [Animation](/gum/code/animationruntime.md) — Gum's separate, unrelated keyframed property-animation system.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.flatredball.com/gum/code/files-and-fonts/animation-chains.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
