Colorize text in the terminal with ANSI Sequence
Sebastien Filion
Posted on June 7, 2021
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
I can start typing anything...
hello
Now, I will type the first ANSI sequence to render all incoming input red.
ESC + [ + 3 + 1 + m
world
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
Now everything is blue ~~
I can also set the background color.
ESC + [ + 4 + 3 + m
It's like IKEA
Next, I can just reset the colors.
ESC + [ + 0 + m
Back to basics.
Within the boundary of my terminal I can move the cursor.
ESC + [ + H
I'm typing here!
ESC + [ + 2 + 0 + ; + 2 + 0 + H
Now, I'm typing here.
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
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
Oh, look... a heart! Which reminds me that if you're following me, I love you!
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
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
To output the number of rows/ or lines
tput lines
And, finally to output the number of colours
tput colors
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");
And, this works for all sort of sequences...
console.log("\x1b[H\x1b[J\x1b[36mAh! so clean\x1b[0m");
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.
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 ]);
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);
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
);
To finally display my art running:
await Deno.write(Deno.stdout.rid, xs);
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...
Posted on June 7, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.