In order to demonstrate some new features / issues etc. it's sometimes quite handy to use short screen captures. The only problem is that they are most of the times too large to share easily or it's just not possible to seamlessly add them to GitHub issues - but what other cool way is there to get your point across? ;)

For the lazy ones: the video2gif.sh script can be found as a Gist.

Of course there are plenty of apps for all system to do this conversion for you but as a developer I don't want an extra app for every little task at hand and a plain bash script works like a charm - also on macOS/OS X.

Requirements for my ideal Script

There are a few key points I want my ideal video to GIF conversion script to support:

  1. Scaling - while converting immediately scale down the video to another size
  2. FPS - specify the frame rate of the GIF (to be able to make them really smooth)
  3. Trim the end - while converting leave out some seconds at the end (used during automation later)

Existing Scripts and Commands

I started some research and found some existing solutions like this Gist or in this question on StackOverflow. Both of them make use of ffmpeg for the initial video conversion but either revert back to the usage of convert or gifsicle for GIF file creation. Trying these out I got some serious quality errors and problems, especially when using gifsicle. One of the examples from the Gist above is:

ffmpeg -i in.mov -s 600x400 -pix_fmt rgb24 -r 10 -f gif - | gifsicle --optimize=3 --delay=3 > out.gif

However, this command at least includes two of the major requirements I had for my script: allow scaling of the output (-s 600x400) and specifying the target frame rate (-r 10).

Conversion Steps

After some extensive research and trying out various commands the best solutions for me where produced by first generating a color palette from the video material and then do the conversion using a complex filter (-lavfi). The basic filter I use is as follows:

fps=$FPS,scale=iw/$SCALE:ih/$SCALE:flags=lanczos

Here I can substitute $FPS with my designated frame rate and include the $SCALE as a scale down factor, i.e. SCALE = 2 will result in a GIF with half the size of the video (iw and ih match the input file's width and height respectively).

Generating the palette file can be done with the command:

ffmpeg -i $FULLPATH -vf "$FILTERS,palettegen" -y $PALETTE

Here $FULLPATH is the path to the input file and $FILTERS are the filters from above. This will generate a PNG file stored as $PALETTE. The palette is subsequently used as input besides the original movie file. The final conversion step is executed with:

ffmpeg -i $FULLPATH -i $PALETTE -lavfi "$FILTERS [x]; [x][1:v] paletteuse" -y $GIF

Using the filters and palette constructed above this will result in a very nice quality GIF file like the example below (created on a Retina Mac, 10 fps, scale 2).

video2gif Demo

The only thing missing: trimming the end. In the example above you can see the -c|--cutoff option in the help message. Luckily, ffprobe which comes with ffmpeg allows us to get time information from a video file. Executing the following command gives us the duration of the video in seconds:

ffprobe -loglevel quiet -of compact=p=0:nk=1 -show_entries format=duration -i $FULLPATH

Last we take the cutoff at the end, subtract it from the duration and get our final video length to process (where $DURATION is the length in seconds):

DURATION_DIFF=$(echo "$DURATION - $CUTOFF" | bc -l)

By specifying the -t $DURATION_DIFF parameter to our conversion commands from above we can also include the trim operation as we like directly into the processing.

The final script is available here.

Workflow Integration

As a final step I wanted to facilitate the creation of GIFs out of screen captures on my Mac even further - including the step of creating screen captures. Thus, I wrote myself a custom workflow for Alfred.

The workflow allows me to easily start a screen capture by typing the short command scr to start the capture.

Start screen capture with Alfred

Once finished, I use Alfred to stop the capture and convert it to a GIF file using the script from above all in one step:

Stop capture and convert to GIF

Of course the workflow also supports saving the capture as a plain .mov file.


I hope my script is useful for some of you - if you have any suggestions for improvements just leave a comment here or on the Gist! Also feel free to use my Alfred workflow if you like to :)