Table of Contents

Working with Animations

ImageSharp treats animation as a multi-frame Image<TPixel>. The authoring model is the same whether you save as GIF, animated PNG (APNG), or animated WebP: build the frame collection, set image-level animation metadata, set per-frame metadata, then save with the encoder for the format you want.

You still work with full-size frames in memory, but ImageSharp's animated encoders optimize the output by de-duplicating unchanged pixels between frames and writing only the differing region for later frames where the format supports it.

For format-specific background, palette, and compression tradeoffs, see GIF, PNG, and WebP. This page focuses on the shared multi-frame workflow.

Build a Multi-Frame Animation

The root frame is the first animation frame. Additional frames are appended through ImageFrameCollection.AddFrame(), which clones the source frame. In ImageSharp, animation frames must always match the image dimensions.

using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;

Color[] colors =
{
    Color.Orange,
    Color.DeepSkyBlue,
    Color.MediumSeaGreen
};

using Image<Rgba32> animation = new(120, 120, colors[0].ToPixel<Rgba32>());

for (int i = 1; i < colors.Length; i++)
{
    using Image<Rgba32> frameImage = new(120, 120, colors[i].ToPixel<Rgba32>());
    animation.Frames.AddFrame(frameImage.Frames.RootFrame);
}

When you start from Color values, convert them to the target pixel type with ToPixel<TPixel>() before passing them to generic image constructors.

ImageSharp Optimizes Later Frames

When encoding GIF, APNG, or animated WebP, ImageSharp compares later frames with the previous composited result and trims unchanged pixels from the encoded output. In practice, that means you usually author full-canvas frames, but the encoder writes only the changed bounds for later frames when that produces an equivalent animation.

This is especially helpful for sprite, UI, and cursor-style animations where only a small region changes from one frame to the next.

Configure GIF Metadata

Use GifMetadata and GifFrameMetadata when you are saving palette-based animation:

  • RepeatCount controls looping. 0 means loop indefinitely.
  • FrameDelay is measured in hundredths of a second.
  • DisposalMode controls how the previous composited frame is treated before the next frame is shown.

Starting from an existing Image<Rgba32> animation:

using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Gif;
using SixLabors.ImageSharp.PixelFormats;

GifMetadata gifMetadata = animation.Metadata.GetGifMetadata();
gifMetadata.RepeatCount = 0;

foreach (ImageFrame<Rgba32> frame in animation.Frames)
{
    GifFrameMetadata frameMetadata = frame.Metadata.GetGifMetadata();
    frameMetadata.FrameDelay = 10;
    frameMetadata.DisposalMode = FrameDisposalMode.RestoreToBackground;
}

animation.Save("output.gif", new GifEncoder
{
    ColorTableMode = FrameColorTableMode.Global
});

GIF is always palette-based, so palette selection still matters. See GIF and Quantization, Palettes, and Dithering for the full quantization story.

Configure APNG Metadata

Use PngMetadata and PngFrameMetadata when you want animated PNG output:

Continuing from the same Image<Rgba32> animation:

using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Png;
using SixLabors.ImageSharp.PixelFormats;

PngMetadata pngMetadata = animation.Metadata.GetPngMetadata();
pngMetadata.RepeatCount = 0;
pngMetadata.AnimateRootFrame = true;

foreach (ImageFrame<Rgba32> frame in animation.Frames)
{
    PngFrameMetadata frameMetadata = frame.Metadata.GetPngMetadata();
    frameMetadata.FrameDelay = new Rational(1, 10);
    frameMetadata.DisposalMode = FrameDisposalMode.DoNotDispose;
    frameMetadata.BlendMode = FrameBlendMode.Over;
}

animation.Save("output.png", new PngEncoder());

Configure Animated WebP Metadata

Use WebpMetadata and WebpFrameMetadata when you want animated WebP output:

Continuing from the same Image<Rgba32> animation:

using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.PixelFormats;

WebpMetadata webpMetadata = animation.Metadata.GetWebpMetadata();
webpMetadata.RepeatCount = 0;
webpMetadata.BackgroundColor = Color.Transparent;

foreach (ImageFrame<Rgba32> frame in animation.Frames)
{
    WebpFrameMetadata frameMetadata = frame.Metadata.GetWebpMetadata();
    frameMetadata.FrameDelay = 100;
    frameMetadata.DisposalMode = FrameDisposalMode.DoNotDispose;
    frameMetadata.BlendMode = FrameBlendMode.Over;
}

animation.Save("output.webp", new WebpEncoder());

Practical Guidance

  • ImageSharp animation frames must always be the same size as the animation canvas.
  • Set timing and disposal or blend metadata on every frame you care about rather than relying on defaults.
  • Choose GIF when broad legacy compatibility matters more than palette and compression tradeoffs.
  • Choose APNG when you want PNG-style lossless color and alpha with explicit frame blending and disposal.
  • Choose WebP when you want a modern animated format with flexible compression behavior.