Colorize text in the terminal with ANSI Sequence

sebastienfilion

Sebastien Filion

Posted on June 7, 2021

Colorize text in the terminal with ANSI Sequence

Oh, hey there!

If you are like me, you find a certain satisfaction using a well crafted CLI or terminal tool...
Especially when they use colour or redraw the terminal.

Something like that: https://github.com/erroneousboat/slack-term
Or, this: https://github.com/fdehau/tui-rs
Or, that: https://github.com/wtfutil/wtf

"how do we make on of those?" You are asking yourself? (hopefully) -- This must be your lucky day because this
is the subject of this video! > ANSI Sequence <

This article is a transcript of a Youtube video I made.

An ANSI sequence is a standard signaling to control everything from the cursor position, to styling to colours.
In other words, it's a sequence of characters that makes commands to the terminal.

I made this interactive script to show you the core concepts. It uses JavaScript and Deno, but the concept
don't change no matter what language or runtime you are using.

I will run this script that will simply redirect raw input from my keyboard to the terminal's output.

deno run --unstable echo.js
Enter fullscreen mode Exit fullscreen mode

Alt Text

I can start typing anything...

hello
Enter fullscreen mode Exit fullscreen mode

Now, I will type the first ANSI sequence to render all incoming input red.

ESC + [ + 3 + 1 + m
Enter fullscreen mode Exit fullscreen mode
world
Enter fullscreen mode Exit fullscreen mode

If I want to render the text in blue, I just need to type in a new sequence with the appropriate number for blue, 34.

ESC + [ + 3 + 4 + m
Enter fullscreen mode Exit fullscreen mode
Now everything is blue ~~
Enter fullscreen mode Exit fullscreen mode

I can also set the background color.

ESC + [ + 4 + 3 + m
Enter fullscreen mode Exit fullscreen mode
It's like IKEA
Enter fullscreen mode Exit fullscreen mode

Next, I can just reset the colors.

ESC + [ + 0 + m
Enter fullscreen mode Exit fullscreen mode
Back to basics.
Enter fullscreen mode Exit fullscreen mode

Within the boundary of my terminal I can move the cursor.

ESC + [ + H
Enter fullscreen mode Exit fullscreen mode
I'm typing here!
Enter fullscreen mode Exit fullscreen mode
ESC + [ + 2 + 0 + ; + 2 + 0 + H
Enter fullscreen mode Exit fullscreen mode
Now, I'm typing here.
Enter fullscreen mode Exit fullscreen mode

If I move my cursor back to the top-left corner, I can clear the rest of the screen, giving me a blank slate...

ESC + [ + 0 + H + ESC + [ + J
Enter fullscreen mode Exit fullscreen mode

I did promise you some drawing, let me type away.

ESC[45m
ESC[2;2H
space
space
space
ESC[2;6H
space
space
space
ESC[3;3H
space
space
space
space
space
ESC[4;5H
space
Enter fullscreen mode Exit fullscreen mode

Oh, look... a heart! Which reminds me that if you're following me, I love you!

Alt Text

Before we go any further, I think it's important to mention that not all terminal client can interpret ANSI sequence, so you might
want to test yours before getting too excited. That being said, it probably does...
If you want to confirm, go to your terminal and type echo $TERM... If the value contains one of those words, you're good to go...
It'll probably say "xterm" though...

xterm
rxvt
eterm
screen
tmux
vt100
vt102
vt220
vt320
ansi
scoansi
cygwin
linux
konsole
bvterm
Enter fullscreen mode Exit fullscreen mode

Now if you're running PowerShell, I think that it supports ANSI sequences, but the feature is turned off by default...

While we're on the terminal, another thing you might want to play with is a command called tput -- the command is useful when you need to query
the state and features of your terminal.

The commands that might interest you are the followings.

To output the number of columns

tput cols
Enter fullscreen mode Exit fullscreen mode

To output the number of rows/ or lines

tput lines
Enter fullscreen mode Exit fullscreen mode

And, finally to output the number of colours

tput colors
Enter fullscreen mode Exit fullscreen mode

So now we know that my terminal has 80 columns, 25 rows and is capable of displaying 256 colours. Awesome!

So, this video is marketed as "JavaScript Fundamentals", so I guess I better start writing some JavaScript.

So first and foremost, the easiest way to type an ANSI sequence in JavaScript is to start typing \x1b ~ which is the hexadecimal number for the Escape key.

So we can do something like this:

console.log("\x1b[31mSomething went wrong.\x1b[0m");
Enter fullscreen mode Exit fullscreen mode

And, this works for all sort of sequences...

console.log("\x1b[H\x1b[J\x1b[36mAh! so clean\x1b[0m");
Enter fullscreen mode Exit fullscreen mode

It's already obvious that using ANSI sequences this way can become very noisy. So let's wrap the commands we want to use into function.
For this demo, I will use TypedArray instead of strings. If you are not quite familiar with TypedArray, I wrote an article explaining all about it. I'll wait.

At any rate, here's a refresher: A Typed Array is an array that can only hold a specific amount of bit per item.
When dealing with character, it's typical to use an unsigned 8 bits integers. If you don't know why, again, read the article.

Alt Text

I'll explain this one briefly, you can see the final result here: https://github.com/sebastienfilion/i-y/blob/main/fundamentals/ANSI/generate.js
And get yourself a copy of the artwork here: https://raw.githubusercontent.com/sebastienfilion/i-y/main/fundamentals/ANSI/logo.js

You can also watch the video to get more explanations and the time-lapse.

First I create a few utility functions like this one to facilitate the process:

const clear = () => new Uint8Array([ 27, 91, 72, 27, 91, 74 ]);
Enter fullscreen mode Exit fullscreen mode

The function returns a TypedArray of the following character \x1b, [, H, \x1b, [, J -- \x1b being the hexadecimal number for the "escape" key.

Once I have all of my utility functions ready, all returning a TypedArray, I create a buffer of 1KB.

const xs = new Uint8Array(1024);
Enter fullscreen mode Exit fullscreen mode

Then I reduce all of the commands into the buffer:

[
  clear(),
  inverse(),
  fill(10)
]
  .reduce(
    (i, ys) => xs.set(ys, i) || i + ys.byteLength,
    0
  );
Enter fullscreen mode Exit fullscreen mode

To finally display my art running:

await Deno.write(Deno.stdout.rid, xs);
Enter fullscreen mode Exit fullscreen mode

ANSI sequence are a powerful way to make any CLI or terminal tool interactive and engaging.
I suggest that you look up the Wikipedia page to get a reference for all the various sequence and their effects.
If you're curious and want to learn more about TypedArray, Readable/Writable-Streams and ANSI sequence, you should subscribe to this channel.
I will use these concepts in an incoming video series.
Otherwise, don't forget to hit the "like" button, share or leave a comment if you've learned something.
Okay bye now...

https://en.wikipedia.org/wiki/ANSI_escape_code

💖 💪 🙅 🚩
sebastienfilion
Sebastien Filion

Posted on June 7, 2021

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

Sign up to receive the latest update from our blog.

Related