Animated Gifs for Text-Based UIs

stjohnjohnson

St. John Johnson

Posted on March 3, 2020

Animated Gifs for Text-Based UIs

Context

In my spare time, I've been working on a simple text-based game for my 4 year old to play. He found my 10 year old Linux laptop and has been having a blast with the terminal.

When I started writing the game, I picked GoLang as the language and researched a variety of TUI (text user interface) libraries. After some quick prototypes, I settled with tview for the solid primitives and ease of keyboard interaction.

Objective

During development, I realized it could use more feedback when getting an answer right or wrong. The way I wanted to solve that was by adding simple animations (e.g. happy vs sad cat). Nothing in tview (or the other libraries) had anything readily available.

The following is the process I went through to build a new tview object that supported displaying animated gifs:

  1. Parse the image frames and timing information
  2. Display the frames on the screens
  3. Scale for multiple animations

Step 1. Parsing Gifs

GoLang has a simple built in Gif library image/gif. After decoding the file, you are left with:

  • Set of frames as image.Paletted
  • Set of delays between frames as int

Step 2. Image to Text

I stumbled across pixelview which converts images into formatted text for tview by using colored half-block unicode characters ().

It also accepts the image.Image interface (which is what image.Paletted above uses).

Now I can display each of those Gif frames into a tview box.

Step 3. Optimizing

The above prototype wouldn't really work. Besides the single-threaded iteratation through a single gif (which prevents us from adding multiple gifs), there are performance issues to take into account. Depending on the speed and the number of frames, you would see CPU load during quick rendering and constant conversion of image objects. Additionally, the use of TextView and the added features (scrolling and highlighting) slows down rendering.

To reduce that impact and support multiple images, I switched to my own Box class and made some important changes.

First, I switched the animation to be on-demand. That way, whenever the view needed to be re-drawn it would calculate the current frame it should be on and render that.

I did that by recording the start time of the view and iterating on each frame delay until we knew where we were.

Second, I removed our usage of TextView and made my own stripped down version of the TextView draw function.

Finally, I triggered a global re-draw on a periodic basis. That way all Gifs would be animated consistently (although not as smooth).

Final Product

In the end, I created a library that allowed me to add basic animated gifs into my son's game using a single interface.

Here is the library as well as the documentation.

And this is a simple example (dancing banana not included):

working-demo

💖 💪 🙅 🚩
stjohnjohnson
St. John Johnson

Posted on March 3, 2020

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related