Write better looking console applications using ANSI escape codes

Console applications are a great thing: They usually solely focus on getting things done. That, however, often also means that the user experience can come short. While I don’t have a problem with a simple text-only menu, it can often scare away new users. But there’s a way to easily style your console applications so that they can have something that you could call a primitive GUI:

Figure 1: A simple GUI with a centered main menu and a custom background

You can also use this technique to develop more advanced console-based applications, like text-editors and spreadsheet programs. Anyway, let’s take a look at what the main problems are that we’re trying to solve!

The problems with a simple text-only console application

As mentioned above, the text-only approach makes sense in many cases. For example in all such programs, that don’t require the user to continually enter information. Those programs are better off just using input parameters, configuration files, or a one-time input at startup. The results then get displayed to the user. Fast, easy, and convenient!

However, many programs need to display a menu of some sort, so that a user can choose from different options. Usually, a quick and dirty solution might look like this:

Figure 2: A classic text-only console application

I have several problems with this approach. First, this requires more input validation. You’d have to check whether the correct input was made, and repeat your request, if the input was incorrect. Furthermore, this approach might open up ways for exploits.

Next, there’s no convenient way to display the progress of an operation that might take a while to finish. Note how the percentages get printed in a new line.

And last but not least, the screen usually doesn’t get cleared when you’ve made a choice. Therefore, the menu gets printed to the console again when the current task is done. This will make everything look bloated and even uglier.

A more user-friendly approach

Now let’s look at the same menu, but this time it’s styled to be a little more convenient to use:

The user can immediately see what the choices are and how to select them. When an option gets clicked on, the screen reloads, and the old contents get deleted.

In the second half of the video, you can also see that there’s now a proper progress bar that’s much easier to understand, as well as a status-bar that displays important program messages.

How to build a console menu using ANSI escape sequences

As mentioned in the title, we’ll use these ominous ANSI escape codes to control the console. To be more precise, we’ll use CSIs (Control Sequence Introducers) and OSCs(Operating System Commands). These will allow us to change the color of the text, the background color, and draw special characters. But they’ll also enable us to set the cursor position, clear a line, and much more, for example, set the window’s title!

Before we begin, here’s what all CSI sequences will look like:

\033[X;YZ

All sequences start with an escape character (which is \033 in octal) and are followed by a second byte, which is the opening square bracket in the case of a CSI command. Next, you have to supply the parameters. They get separated with a semicolon. If a parameter is omitted, a default value is automatically assumed. Lastly, the final byte is supplied. You can see that one as the command identifier. So in this example, we pass the parameters X and Y to the command Z, which is a (fictional) CSI command.

Don’t worry if this sounds confusing right now. You’ll surely understand it immediately when you see the code examples below. But for now, here’s a real example of a CSI sequence:

\033[2K\n

This sequence corresponds to the following command:

ESC CSI n K

Which will simply delete the contents of the currently selected line from the console.

It’s possible to chain different escape sequences together. In this case, the entire line gets deleted, and the cursor gets moved to the next line. You can, of course, also chain multiple CSI sequences together like so:

\033[30m\033[47m\033[1m\033[m

Again, don’t worry if it all seems overwhelming right now. We’ll take a look at various commands in the following sections!

Changing a text’s appearance

Now, let’s get to work and change the appearance of the displayed text. Let me note that you can enter these sequences in a Python or Ruby console to immediate test their effect:

Figure 3: You can experiment with ANSI sequences in a Python or Ruby console

As you can see, the last byte of these commands always gets represented by a lower-case m. The only thing that changes is the parameter. Therefore, you can use the following table to style your text in console applications:

ParameterEffectExample
0Reset\033[0m or \033[m
1Bold\033[1m
4Underline\033[4m
5Slow Blink\033[5m
7Invert\033[7m
8Hidden\033[8m
30-37 or 90-97Font color\033[30m
40-47 or 100-107Background color\033[105m

These text-styling commands get called SGR commands (Select Graphic Rendition, or Set Graphics Rendition in Linux max pages).

Note: I omitted the commands that weren’t working for me on macOS in the default terminal. You can find a full list on Wikipedia, if you’re interested.

Changing the font color and background

As noted in the table above, the parameters 30-37 or 90-97 and 40-47 and 100-107 can be used to change the color of the font or the console window. Here’s a table that explains what each number means:

Font CodeBackground CodeColor name
3040Black
3141Red
3242Green
3343Yellow
3444Blue
3545Magenta
3646Cyan
3747White
90100Bright Black
91101Bright Red
92102Bright Green
93103Bright Yellow
94104Bright Blue
95105Bright Magenta
96106Bright Cyan
97107Bright White

Note that these colors will look different in various terminal programs and operating systems. This Wikipedia article contains a table with examples of what the colors will look like in different terminal programs.

Moving the cursor

Luckily, it’s very easy to move the cursor in a console window. To do that, you can directly print the ANSI representation of an arrow-key, which will move the cursor one unit in the given direction:

CodeDescription
\033[AMove Up
\033[BMove Down
\033[CMove Forward (Right in western culture)
\033[DMove Back (Left in western culture)

If you want to move the cursor multiple times, you can either repeat the command or add a number right between the opening bracket and the capital letter. For example:

\033[10A

That’ll move the cursor up ten lines. It’s also possible to move the cursor to an absolute position in the window:

\033[X;YH

Note that the X (row) and Y (column) are parameters. Use the following command to position the cursor in the top left corner of the terminal window:

\033[1;1H

How to erase text

Two commands exist that allow you to remove content from the current console window. Those are:

CodeDescription
\033[XJClears part of the entire console window. If X = 0 (or omitted), clear from the cursor to the end of the screen. If X = 1, clear from the cursor to the beginning of the screen. If X = 2, clear the entire screen. If X = 3, clear the screen and delete the stored buffer (the content that’s present when you scroll up).
\033[XKIf X = 0 (or missing), delete the line from the cursor to the end of the line. If X = 1, delete the line from the cursor to the start of the line. If X = 2, delete the entire line. Note that the cursor position doesn’t change.

Examples:

This clears the entire screen:

\033[3J

And this command deletes the current line:

\033[2K

Putting it all together

At first, I planned to release the code here. However, I threw together a quick Python library that can be used to style text and draw shapes and menus in console apps. You can find the code on GitHub. The repository also contains the two examples that I discussed in this article. Feel free to create a fork on GitHub if you want to help, add features, or fix bugs.

Tips and tricks

  • The font styling sequences can quickly become unreadable if you chain many of them together. This is one instance where I can recommend that you use the decorator pattern (if possible) to keep the code more readable.
  • Boxes, buttons, and dialogue windows can easily be drawn by moving the cursor around and inserting characters with the same font- and background color.
  • Don’t forget to reset the text style (\033[m) after every specially styled text! Otherwise, the appearance will be applied to all elements that get drawn later.

Downsides to using this technique

  • Platform dependent, often even console depended
  • Some features are not widely or fully implemented
  • Different colors, many features not widely implemented / supported
  • Sometimes implemented differently
  • Might look glitchy if too much is drawn at once

Apart from these downsides, you’ll also have to take care of the size management yourself. If the console window gets resized, you’ll have to update the content and make sure that it still fits:

Summary

As you just saw, it’s incredibly easy to style your console applications, so that they look a bit better. However, it’s also a great way to make your simple applications incredibly complicated very quickly. Therefore, I can only recommend that you use the advanced drawing techniques where it makes sense, for example, in apps that require a lot of user input or apps that are likely to get used by beginners.

Anyway, I think some commands, for example, the ones for styling text, or cleaning the entire screen, should widely be used in almost all console applications to display error messages in a different color. That alone will already incredibly increase the readability of even simple apps.

Leave your two cents, comment here!

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.