GIF Screen Recording on Linux

posted Originally published at jarabialex.hashnode.dev 4 min read

Overview

I recently realized how challenging it is to illustrate my application's functionality in documentation. Screenshots provide only static images, while videos are large and need hosting on platforms like YouTube. GIFs offer a practical middle ground. However, using a Linux machine restricted me to either online MP4 to GIF converters or awkward browser add-ons.

In this tutorial, we will learn how to create a GIF from a screen recording on Linux. We'll start by learning how to capture screen recording using FFmpeg, a command line toolbox to manipulate, convert and stream multimedia content. Later, we will use FFmpeg to convert the recording to GIF.

Screen Recording: FFmpeg

X11 (also known as the X Window System) is a standard networking display protocol that provides the framework for building graphical user interfaces (GUIs) on Linux and other Unix-like operating systems. FFmpeg uses a dedicated input device called x11grab to interact with X11.

Enough of the technical jargon. Let's record our primary display using FFmpeg:

ffmpeg -f x11grab -y -framerate 20 -s 1920x1080 -i :0.0 -c:v libx264 -preset superfast -crf 18 video_file.mp4
  • -f: Specifies the input format (x11grab) to allow screen capture on X11
  • -y: Overwrites output file with the same name without prompting
  • -framerate: Indicates the frame rate to use. 20 is good for demos
  • -s: Sets the resolution of the "canvas" to be captured. Adjustable as required
  • -i: The input display source, :0.0 for the primary monitor
  • -c:v: The audio codec to use for the output format. x264 is the gold standard for MP4 compatibility
  • -preset: Tells the CPU to prioritize encoding speed over file size. Crucial for live recording so you don't drop frames
  • -crf: "Constant Rate Factor" refers to video quality. 18 is visually lossless

Lower crf values produce a high-quality video at the expense of file size. The value ranges between 0 and 51, where 0 means "lossless".

I have named the resulting file video_file.mp4. You can give the file a name of your choice.

You may want to capture the entire screen but are not sure of the screen resolution. In that case, you can use xdpyinfo to grab the screen resolution dynamically:

xdpyinfo | grep dimensions | awk '{ print $2 }'
1920x1080

Then inject this handy snippet in the command to substitute the value for -s:

ffmpeg \
    -f x11grab \
    -y \
    -framerate 20 \
    -s "\((xdpyinfo | grep dimensions | awk '{ print \)2 }' )" \
    -i :0.0 \
    -c:v libx264 \
    -preset superfast \
    -crf 18 \
    video_file.mp4

Once the recording is complete, terminate the process using CTRL+C. FFmpeg quits gracefully.

Convert Screen Recording to GIF

Once the video is recorded, we can convert it to GIF using FFmpeg:

ffmpeg -i video_file.mp4 \
    -vf "fps=10,scale=800:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \
    -loop 0 gif_file.gif
  • -i: The input source. In this case the recorded video
  • -vf: This defines the video filters. This part uses filtergraph. Instead of a simple conversion, it runs the video through a series of "rooms" to process it. The settings are further explained below this list
  • -loop: Indicates the number of counts to loop the GIF. 0 means the GIF will loop indefinitely. If set to -1, it would play once and stop.

Here is the breakdown of the -vf settings:

  • fps=10: Drops the framerate. High FPS makes GIFs huge and hard to load; 10–15 is the "sweet spot" for demos.
  • scale=800:-1: Resizes the width to 800px. The -1 tells ffmpeg to calculate the height automatically to keep the aspect ratio perfect. The ideal scale for a GIF depends on where you plan to use it, as GIF file sizes explode quickly at high resolutions. Generally, a width between 320px and 800px is the standard range for web and social media.
  • flags=lanczos: Uses a high-quality scaling algorithm (Lanczos) that
    keeps edges sharp, preventing the GIF from looking blurry.

The "Magic" Palette Logic:

Standard GIFs are limited to 256 colors. Usually, a generic palette makes the video look grainy. This logic fixes that:

  1. split[s0][s1]: Takes the input video and creates two identical temporary streams (s0 and s1).
  2. [s0]palettegen[p]: Takes the first stream and analyzes every single frame to find the best possible 256 colors for your specific video. It saves this as a "palette" map named [p].
  3. [s1][p]paletteuse: Takes the second stream and the custom palette map, then merges them. This ensures your GIF uses the most accurate colors possible.

Summary of the Flow

  1. Grab pixels from the screen.

  2. Compress them quickly using H.264 so the CPU doesn't lag.

  3. Re-sample that video to a lower frame rate (to save space).

  4. Scan the colors to build a custom "paint box" (the palette).

  5. Paint the GIF using only those specific colors.

Combining the Commands

Running these commands separately can get quite taxing. We can use a bash script to combine the commands in one tool kit:

 #!/bin/bash

 # 1. Dynamically generate names of files 
NAME=$(date +'%Y-%m-%d_%H-%M-%S')
MP4_FILE="${NAME}.mp4"
GIF_FILE="${NAME}.gif"

 # 2. Start Recording
echo "--- Recording started: $MP4_FILE ---"
echo "Press 'q' to stop recording..."

ffmpeg -f x11grab -y -framerate 20 -s 1920x1080 -i :0.0 \
       -c:v libx264 -preset superfast -crf 18 "$MP4_FILE"

 # 3. Convert to High-Quality GIF
echo "--- Recording stopped. Converting to GIF: $GIF_FILE ---"

ffmpeg -i "$MP4_FILE" -vf \
"fps=10,scale=800:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse" \
-loop 0 "$GIF_FILE"

echo "--- Done! Created $GIF_FILE ---"

 # 4. Delete video file (optional)
rm "$MP4_FILE"

This script will handle the heavy work for you. Below is a demo of the script at work:

2 Comments

0 votes
0

More Posts

My Nginx Died at 2 AM and Nobody Noticed for 6 Hours. Now I Have a Watchdog Script

BashSnippets - May 21

How to Fix Samsung Galaxy Book4 Pro Speakers on Linux

Bernardo Lebron - May 22

Run the Real ChatGPT Desktop App on Ubuntu Linux (Not a Wrapper)

johnohhh1 - Apr 9

Rescuing Legacy Hardware: Installing Linux on Early UEFI Systems (Debian 13 Case Study)

Igor Giamoniano - Coisa de Dev - Jan 30

How to Detect and Monitor Multiple GPUs on Linux

Igor Giamoniano - Coisa de Dev - Mar 14
chevron_left

Related Jobs

View all jobs →

Commenters (This Week)

1 comment
1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!