JT's Scribblings

a.k.a. a blog of some description.

GIF Workflow

Sunday April 5th 2015, at 6:05 pm

GIFs seem to be making something of a comeback on the web. And not just for silly clips of kittens or cute things — they can actually be quite useful.

Recently I wrote a post on the GoSquared blog about how we re-imagined our login process. More so than in anything we've done previously, the real essence of what we had done could only be described through motion. Simple screenshot images wouldn't suffice. So I decided to go all out and make a big pile of GIFs showing off what we had done.

This is the story of how to achieve that. There're various tools for recording GIFs directly from your screen, but they don't give you anywhere near the kind of flexibility, control, and quality as you'd really want (some of the colours you get out of them are, quite frankly, hilariously awful). By figuring out a workflow like the one set out below, it's perfectly possible to achieve pretty decent-quality GIFs, which aren't absolutely HUGE in size, and which are decent enough to put in a blog post or on Dribbble or whatever.

Record a video

Start out by doing a standard screen capture video. On macs, this is easiest with QuickTime, which will give you a .mov file, on Ubuntu there's Istanbul or RecordMyDesktop. This will give you a video file to work with.

Just record absolutely everything you might want to include in the gif. Don't worry about starting over if you screw it up, just go back and do whatever's necessary and make sure the video includes any sections you want to GIF-ify. You can chop it up later.

Extract all the frames from the video

Once you've got the video file, you'll want to extract all the individual frames to separate image files. As a rule I tend to use PNG files because they're lossless and you don't want to have too many lossy conversion steps along the way.

Here's how to do it with ffmpeg:

$ mkdir frames
$ ffmpeg -i videofile.mov frames/%5d.png

This'll give you a massive pile of PNG images in your frames directory, numbered upwards from 00001.png.

Decide what sections to extract

Look through all the PNG frames you have and decide what you want to include in the final gif. Maybe you just want to capture a specific action, or maybe you recorded a long video from which you want to extract several separate gifs.

Say you've decided that the frames from 01234.png to 01500.png are good to use. Now copy them into a new directory so you only have the frames you want:

$ mkdir gif-frames
$ cp frames/{01234..01500}.png gif-frames

This should give you just the frames you want in the gif-frames directory

(Optional) crop the frames to a specific rectangle

Sometimes you might actually be interested in just a specific region of your original recording. For this you can use ImageMagick's convert command to crop each frame.

Let's say that in our frames from 01234.png to 01500.png we're only actually interested in a 400×300px rectangle, 100 pixels down from the top and 50 pixels in from the left. Instead of the cp command above, we'd instead use a loop and crop the frames:

$ for n in {01234..01500}.png; do \
    convert frames/$n -crop 400x300+50+100 +repage gif-frames/$n; \
  done

The +repage option here is necessary because otherwise it will confuse the gif-conversion stage below.

This will give you the selection of frames you want cropped to the same rectangle for each.

(Optional) remove some frames

Maybe when you were recording the original screen capture you hesitated a little longer than necessary before clicking a button, or you typed really slow. Now's the time to delete some frames. Try to leave everything continuous though, only delete frames which are essentially the same.

Don't worry about leaving the image files in a non-sequential order. That won't affect any of the rest of the process

Convert the frames into a GIF file

Here we can use ImageMagick's convert again to pull all our PNG frames into a single GIF file.

$ cd gif-frames
$ convert *.png -fuzz 1% -layers OptimizeFrames gif1.gif

The -fuzz 1% option here is the most important. Chances are that your initial video capture used a lossy encoding format, which means that sometimes, even if nothing is changing in between two frames, the colours may be slightly different. GIFs really don't like animations where lots of pixels are changing by a very small amount. This option tells convert that if a pixel hasn't changed by more than 1% in colour value since the last frame, pretend it's stayed the same. This will allow you to drastically reduce the size of your output GIF file compared to keeping the colours of the PNG files exactly as-is. You may need to play with the 1%, maybe increase it or decrease it if it's interfering too much with the visual content of your GIF.

Right, well at this point you should have a gif file that looks ready to use. If you've used the fuzz and -layers OptimizeFrames options it should be a decent file size too. But we can probably optimize it a little better.

Optimize your GIF with Gifsicle

Gifsicle is a great little command line tool for getting the most out of your GIF files. It has a bazillion options for optimizing and tweaking GIFs.

Chances are that the gif produced by the last command uses quite a lot of different colours. GIFs don't like having more than 256 individual colours because beyond that level they have to store them in a much less-efficient way. Gifsicle can re-colour your image to use fewer colours without too much visible difference.

We'll also tell Gifsicle to optimize the frames of this gif the best it can.

$ gifsicle -O3 --colors 256 < gif1.gif > output.gif

This should now give you an output-ready GIF of a decent file size.

There are plenty more options you can experiment with. It's worth playing around with the value for the --colors argument, perhaps reducing it to 128 or 64 if there's not too much going on. There's also a --dither option which you can use, which often helps reduce the blockiness you sometimes see if you reduce the colour levels a lot.