Breaking News

How to use the VHS command to turn a Linux terminal session into a beautiful compact GIF

Have you ever seen those GIFs that animate the command line and wondered why they look so good? Or have you seen a small GIF and wondered why they are so small? It’s not as difficult as you might think, and I tackle both with two easy-to-use commands.

VHS is a command for generating GIFs from a script file and Gifsicle is a command for editing them. When used together, they can create very compact and professional-looking GIFs. I explain what VHS is, how to use it and how to compress the resulting GIF so that it is barely larger than a regular PNG file.

A VHS demo

They say a picture is worth a thousand words, so here’s one that’s literally true in this case:

This animation was for an article I wrote on fzf. Here is another image from the same article:

A user chooses an order from a list of orders using FZF. When the FZF window is open, the user types moo and FZF reduces the list of commands to a single cowsay command that says moo. The user then executes it.

They’re beautiful, aren’t they? Believe it or not, it wasn’t difficult or overly technical, nor did it result in a big, bulky image. In fact, the first image is only 223 KB and the second is even smaller at 22 KB. So how did I do it? With VHS, Gifsicle and a little witchcraft.

Key concepts of VHS explained

The basic concept behind VHS is the tape file, a sequence of scripted commands that simulates a terminal session. Here is a simple example:

Output output.gif

Type 'ls -al' Enter
Sleep 2s

This particular band does three things:

  1. Specifies an “Output” GIF file.

  2. Simulates entering a command (“Type”) then pressing “Enter”.

  3. “Sleep” for 2 seconds to allow the player to process actions.

When you run this tape, you will see the command entered into the terminal and executed. The animation will then loop after a brief pause.

Generate the GIF file by running vhs my-file.tape in your terminal.

A terminal window displays the output of the VHS command. It shows a replay of the script as text and a message that the gif was generated.

The GIF obtained:

An animated gif shows running the LS command in a terminal window.-anim

Now let’s write a tape

Now let’s write a more complex example and focus on some details.

Understanding the tape file

The following tape file will create the “needle in a haystack” GIF seen previously. I will clarify the meaning of each line below.

Output unoptimized.gif

Require fzf

Set FontSize 32
Set Width 1200
Set Height 675
Set TypingSpeed 0.15
Set Shell "zsh"
Set Framerate 5

Hide
Type 'eval "$(fzf --zsh)"' Enter
Type 'clear' Enter
Show

Type 'cat lorem.txt'
Sleep 1
Enter
Sleep 2s

Type 'cat lorem.txt | fzf'
Sleep 1
Enter
Sleep 0.5

Type "needle"
Sleep 1
Enter
Sleep 3s

The “Require” directive ensures that “fzf” is present on your system before continuing.

The “Output” directive requires no explanation, but be aware that you can render to GIF, MP4, WebM, or to a directory of individual images to be processed separately.

“Set” directives must be specified next, at the top of the tape file. They apply various options, such as “Width”, “Height”, “Shell”, “Framrate” and even “TypingSpeed”. These options are self-explanatory. You can change the typing speed anywhere in the tape file, but you must specify all other options at the top.

I deliberately set the frame rate low to reduce the image size, but this can sometimes cause rendering issues. If so, increase the “Framrate” or remove the directive altogether.

The “Hide” section is next, and this is where we do the script setup. We add here all the commands that we do not want to render. In this example, we configure the fzf environment and clear the terminal to remove all spam messages from its shell evaluation. We end the “Hide” section with a “Show” directive, which tells VHS to record the following.

Line 17 is where the visual rendering begins. First, it contains a file full of Lorem Ipsum text – this is the haystack we’re going to run through with fzf. The script often “Sleeps” because it improves the appearance of the animation.

On line 22, we grab the text file, redirect it to fzf, hit the Enter key, and on line 22, we finally type the search term: needle. We end the script with a long “Sleep” to give the user time to process the image.

The entire command should create a text file to display its contents, then run it again and search for it using fzf.

Create the tape file

Before generating a GIF from your tape file, make sure you have fzf installed. You also need to create a lorem ipsum text file. So run the following command:

{
  echo -e "You can use fzf to find a needle in a haystack.\n";
  curl 'https://pastebin.com/raw/nRc62E07';
} > lorem.txt

Now create a tape file called fzf-demo.tape, enter the contents of the tape file and run the following command to generate the GIF:

vhs fzf-demo.tape

This command will produce a GIF called “unoptimized.gif”, which we will compress in the next section.

Compress GIF

The “unoptimized.gif” we created is around 1.2 MB, which is often too large. We can compress it with a command called Gifsicle. You can install it with the following commands:

For Fedora, run the following command:

sudo dnf install gifsicle

For Debian, run the following command:

sudo apt install gifsicle

For Arch Linux, run the following command:

sudo pacman -S gifsicle

Now that Gifsicle is installed, you can compress the “unoptimized.gif” file with the following command:

gifsicle -O3 --colors 8 unoptimized.gif -o optimized.gif

This will compress the GIF to approximately 228 KB.

Instead of using a full color palette, I opted for eight bits. Besides reducing the resolution, reducing the number of colors is the biggest space saver. If your terminal session contains a wide range of colors that you want to keep, set it higher, up to 256, but be prepared for a much larger file.

Another way to reduce image size is to reduce the frame rate. I set it to 5, which seems to be a good balance between compactness and readability. Since the frame rate is so low, the output GIF will contain duplicate frames, but we can remove them automatically. When you use the “-O3” (or -O2) flag with Gifsicle, it will find duplicate images, remove extras, and add an appropriate delay to the remaining image.

If you compare these two images before and after, you can see that one image contains much fewer images and that the delay between each image has become variable: this is the optimized image.

For action-packed GIFs (e.g., rapidly changing text), removing duplicate images has a significant impact on file size. For smaller, simpler animations, the space saved is proportionately smaller but still considerable.

I didn’t mention it earlier, but VHS has a record command that will automatically create a tape from your terminal actions. You are supposed to edit the tape afterwards. Execute vhs recording > my.tapeperform your actions, then type exit in your shell when you’re done. It looks like you will exit your shell, but that is not the case; VHS will create a tape file for you.


I recommend reading the official documentation yourself (found on its GitHub page). Understand that VHS is not perfect and there may be bugs. However, it works most of the time, and it’s a lot less work than screen recording. You don’t think so? Try the “record” function, edit the tape file and run the two necessary commands.

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Back to top button