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.
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:
- Parse the image frames and timing information
- Display the frames on the screens
- Scale for multiple animations
GoLang has a simple built in Gif library
image/gif. After decoding the file, you are left with:
- Set of frames as
- Set of delays between frames as
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
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).
In the end, I created a library that allowed me to add basic animated gifs into my son's game using a single interface.
And this is a simple example (dancing banana not included):