TechEarl

How to Combine Animated GIFs Into One With gifsicle

Combine animated GIFs into one from the command line with gifsicle. Merging is the default mode, the loop quirk that makes it look broken, plus matching sizes, the 256-color ceiling, and the ffmpeg alternative.

Ishan Karunaratne⏱️ 9 min readUpdated
Share thisCopied
Combine animated GIFs into one from the command line with gifsicle: merging is the default mode for sequential playback, set the loop count, match sizes, and work around the 256-color limit.

To play several animated GIFs back to back as one file, concatenate them with gifsicle. The shortest command that does it needs no flag at all, because combining inputs into one multi-frame animation is gifsicle's default mode:

bash
gifsicle first.gif second.gif > merged.gif

That writes every frame of first.gif followed by every frame of second.gif, so the result plays the first animation through, then the second. The explicit name for this default mode is --merge (see the gifsicle manual), and --append does the same join from a different angle, so all three of these produce the identical sequential GIF:

bash
gifsicle --merge first.gif second.gif > merged.gif   # explicit default
gifsicle --append first.gif second.gif > merged.gif  # append second to first

All three forms produce a single animated GIF that plays one clip and then the next. That is the whole job for most people searching "merge GIFs". The rest of this page is the detail: the one flag people misread, the loop quirk that makes a merge look like it "only plays the first clip", the size and color constraints GIF imposes, and the ffmpeg route for when gifsicle is not enough.

Install gifsicle first if you do not have it: brew install gifsicle on macOS, sudo apt install gifsicle on Debian or Ubuntu, sudo dnf install gifsicle on Fedora.

--merge, --append, and the "only plays the first clip" myth

There is a persistent claim online that --merge is broken for joining GIFs and that you must use --append instead. That is not what the gifsicle manual describes, and it is worth being precise because it is the thing people get wrong.

Per the manual, --merge "combine[s] all GIF inputs into one file with multiple frames" and is explicitly "the default mode". So gifsicle a.gif b.gif, gifsicle --merge a.gif b.gif, and the --append form all produce a sequential A-then-B animation. None of them drops the second clip.

Where the myth comes from: the merged file inherits the loop count of the first input, so if first.gif was set to loop only once, the combined GIF also plays once and stops, which looks like "it only played the first part" even though every frame is present. The fix is to set the loop count explicitly rather than to switch flags:

bash
gifsicle --merge --loopcount=forever first.gif second.gif > merged.gif

--loopcount=forever makes the merged GIF loop indefinitely; --loopcount=0 is the same thing, and --no-loopcount plays once. A quick map of the relevant options:

FlagWhat it actually does
--merge (-m)The default mode: combine all inputs into one multi-frame animation. This is sequential A-then-B playback.
--appendAppend the listed GIFs' frames to the input GIF's frames. Same sequential result, different framing.
--loopcount[=N]Set the loop count of the output (forever/0 = loop, omit =N for forever, --no-loopcount = play once). This, not the flag choice, is what fixes a merge that stops after the first clip.
--multifileReads as many images as possible from each input; built for scripting an unknown number of files.

Match the dimensions first

gifsicle appends frames as-is. It does not resize anything for you, so if first.gif is 480 wide and second.gif is 320 wide, the merged file inherits a logical screen size and the smaller clip sits in a corner of it rather than filling the frame. Resize the odd one out to match before you append:

bash
# Force second.gif to the same width as first.gif, keeping aspect ratio
gifsicle --resize-fit-width 480 second.gif -o second-480.gif
gifsicle --append first.gif second-480.gif > merged.gif

--resize-fit-width (and --resize-fit-height) scales while preserving aspect ratio; --resize WxH forces exact dimensions if you genuinely want both clips at the same box and do not mind distortion. Sort the sizes out, then append.

The 256-color ceiling and the "too many colors" error

GIF is hard-capped at 256 colors per frame. When you append two GIFs that each use a different 256-color palette, the combined file can exceed what a single global color table allows, and gifsicle reports a "too many colors" error. The fix is to force a shared palette by capping the merged output at 256 colors:

bash
gifsicle --colors 256 --append first.gif second.gif > merged.gif

There is no way around the 256-color limit while staying in the GIF format: it is a property of the format itself, not a gifsicle restriction. If both source clips are photographic and you need more than 256 colors total, GIF is the wrong container. Convert to MP4 or WebM instead (see the ffmpeg route below), which is also far smaller on disk.

Merge a whole directory of GIFs

To chain every GIF in a folder, glob them. Shell globbing expands *.gif alphabetically, so name the files in the order you want them to play (01-intro.gif, 02-body.gif):

bash
gifsicle --colors 256 --append *.gif > merged.gif

If a merged GIF stops after the first clip, that is the loop-count quirk from the section above, not the glob: add --loopcount=forever.

For a large or unknown number of files where you would rather stream them in, --multifile reads images from standard input until it runs out:

bash
cat *.gif | gifsicle --multifile - > merged.gif

The ffmpeg alternative

When the GIFs differ in size or palette enough that gifsicle fights you, ffmpeg's concat filter is the sturdier route. It decodes both inputs, scales them to a common size, joins the streams, and re-renders a clean GIF. This example normalizes both clips to 480 wide and builds an optimized palette so the output does not look dithered:

bash
ffmpeg -i first.gif -i second.gif -filter_complex \
  "[0:v]scale=480:-1[a];[1:v]scale=480:-1[b];[a][b]concat=n=2:v=1[v];[v]split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \
  merged.gif

concat=n=2:v=1 joins two video inputs end to end (sequential play, same as --append), the scale filters make the dimensions match, and the palettegen/paletteuse pair builds a proper 256-color palette so the merge does not introduce banding. If the two inputs are already identical in size, codec, and frame rate, the concat demuxer (ffmpeg -f concat -safe 0 -i files.txt -c copy out.gif) is faster, but for arbitrary GIFs the filter above is the safe default.

Sequential, not side by side

Everything above plays the clips one after another. If what you actually want is two animations running at the same time next to each other, that is a different operation: a horizontal stack, not a concatenation. Use ffmpeg's hstack (or vstack for top-and-bottom).

One catch the docs are strict about: hstack requires both inputs to share the same height (and vstack the same width), or the filter errors out. Animated GIFs straight off the internet rarely match, so scale them to a common height first. This pins both clips to 240 pixels tall, stacks them side by side, then builds a shared palette:

bash
ffmpeg -i left.gif -i right.gif -filter_complex \
  "[0:v]scale=-1:240[l];[1:v]scale=-1:240[r];[l][r]hstack=inputs=2,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \
  side-by-side.gif

The scale=-1:240 filters fix the height so hstack is happy; for vstack you would scale to a common width instead (scale=240:-1).

For side-by-side output you almost always get a better result by stacking the source videos before they were ever turned into GIFs, then converting the combined video to a GIF once at the end. Compositing two already-quantized 256-color GIFs side by side compounds their palette loss; stacking the originals keeps full color until the single final GIF conversion.

FAQ

See also

Sources

Authoritative references this article was fact-checked against.

TagsgifsicleGIFanimated gifmerge gifsCLIffmpegLinux

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years building software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Currently Chief Technology Officer at a healthcare tech startup, which is where most of these field notes come from.

Keep reading

Related posts