Skip to content

Animation keeps one frame or becomes a tall image

Copy page

When an animation keeps only its first frame, becomes one tall image, or develops frame alignment errors, check the loaded page count, page-height, and animation metadata separately. They are related, but they are not the same failure.

  • An animated GIF/WebP/AVIF exports as a single static frame.
  • Resizing an animation puts frames out of order, looking like a “toilet-roll”, or reports bad grid geometry.
  • Joining several frames into one animation yields one very tall image instead of a multi-page animation.
  • The loop count and frame delay are lost after export.

libvips models GIF, animated WebP/AVIF, PDF, and multi-page TIFF uniformly as “multi-page images”. The n (page count) argument controls how many pages load:

  • Default n=1: only the first page (for an animation, the first frame).
  • n=-1: loads every page.

govips exposes this through ImportParams.NumPages. If you do not set it, an animation keeps only its first frame. This is the root cause of most “the animation is gone” reports.

import "github.com/davidbyttow/govips/v2/vips"
// loadAnimation returns a multi-page image that the caller must Close.
func loadAnimation(buf []byte) (*vips.ImageRef, error) {
params := vips.NewImportParams()
params.NumPages.Set(-1)
return vips.LoadImageFromBuffer(buf, params)
}

1. Load every page before transforming an animation

Section titled “1. Load every page before transforming an animation”

Any “whole-image” operation (resize / rotate / flip / zoom) on an animation must be preceded by loading all pages with NumPages=-1, otherwise it acts on only the first frame or scrambles the frames.

2. When building an animation from frames, remember SetPageHeight

Section titled “2. When building an animation from frames, remember SetPageHeight”

The correct way to join multiple frames into an animation is to concatenate vertically and then use SetPageHeight() to slice the tall image back into equal pages, plus SetPageDelay to set the inter-frame delay. If you omit SetPageHeight, the export is one very tall image — the cause of the “joined into a long image” reports.

3. loop and frame-delay metadata must be kept or set explicitly

Section titled “3. loop and frame-delay metadata must be kept or set explicitly”

Some sources (such as GIF87a) carry no loop count, so loop semantics may be lost after export. If your output depends on a repeat count, set loop and delay explicitly during export or metadata handling; do not assume they survive from the source.

  • Taking only the first frame of an animation is a deliberate need, for example to build a preview thumbnail. Do not treat that as a bug.
  • The toilet-roll frame-misalignment on specific scales has an official fix in a newer version; upgrade govips before debugging your own code.
  • The API and boundaries on this page were checked against govips v2.19.0. Animation support for each format still depends on the runtime libvips build.

The default multi-page behaviour of pyvips, sharp, and the CLI still needs individual verification; this page does not project govips defaults onto those bindings.