I was setting up a new CLI tool last month and wanted a startup banner. Something that would display the tool's name in the terminal when it launches. My first instinct was to use figlet, the classic Unix program that converts text to ASCII art. But then I realized: most developers I know have never heard of figlet, and the ones who have think ASCII art is a novelty from the 1990s.
It's not. ASCII art is alive in terminal applications, code comments, log outputs, email signatures, README files, and anywhere that plain text is the only medium available. Understanding how it works -- and how to generate it programmatically -- is a genuinely useful skill.
How ASCII art actually works
ASCII art uses the 95 printable ASCII characters (codes 32-126) to create visual images on a character grid. Each character occupies a fixed-width cell, and the illusion of an image comes from choosing characters whose visual density matches the brightness of the corresponding pixel.
This is the core principle. Characters have different amounts of ink. A @ is dense. A . is sparse. A space is empty. If you map pixel brightness to character density, you get a recognizable image from pure text.
The standard density ramp, from dark to light:
$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,"^`'.
Each character in that sequence is visually lighter than the previous one. To convert an image to ASCII art, you:
- Convert the image to grayscale
- Resize it to the target character dimensions (accounting for the fact that characters are taller than they are wide -- typical ratio is about 2:1)
- Map each pixel's brightness to a character from the density ramp
- Output the result as a grid of characters
In code:
from PIL import Image
def image_to_ascii(image_path, width=80):
chars = "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/|()1{}[]?-_+~<>i!lI;:,\"^`'. "
img = Image.open(image_path).convert('L') # grayscale
aspect_ratio = img.height / img.width
height = int(width * aspect_ratio * 0.55) # 0.55 for char aspect ratio
img = img.resize((width, height))
pixels = img.getdata()
ascii_str = ''.join(chars[pixel * len(chars) // 256] for pixel in pixels)
lines = [ascii_str[i:i+width] for i in range(0, len(ascii_str), width)]
return '\n'.join(lines)
The 0.55 factor compensates for the fact that monospace characters are roughly twice as tall as they are wide. Without this correction, the output would appear vertically stretched.
Text-based ASCII art (figlet style)
Converting images is one use case. The other major category is text-to-ASCII, where you take a word or phrase and render it in large block letters using characters.
The figlet program does this using font files (.flf format) that define how each letter looks. There are hundreds of figlet fonts:
____ _ _
/ ___|| |__ ___| | __
| | | '_ \ / _ \ |/ /
| |___ | | | | __/ <
\____||_| |_|\___|_|\_\
That's "Check" in the "standard" figlet font. Other fonts get creative:
______ __ __ ______ ______ __ __
/\ ___\ /\ \_\ \ /\ ___\ /\ ___\ /\ \/ /
\ \ \____ \ \ __ \ \ \ __\ \ \ \____ \ \ _"-.
\ \_____\ \ \_\ \_\ \ \_____\ \ \_____\ \ \_\ \_\
\/_____/ \/_/\/_/ \/_____/ \/_____/ \/_/\/_/
Figlet fonts work by mapping each printable ASCII character to a grid of sub-characters. The font file specifies the width and height of each letter, how they connect (some fonts have "smushing" rules that merge adjacent letters), and which characters to use for the rendering.
Where ASCII art is actually used today
CLI startup banners. Tools like neofetch display system information alongside ASCII art. Custom CLI applications use ASCII banners for branding. It's the first thing users see when they run the tool.
Code comments. Large section dividers in code files:
/************************************
* ___ ___ ___ _____ *
* / _ \| _ \|_ _|_ _| *
* | | | | |_) || | | | *
* | |_| | __/ | | | | *
* \___/|_| |___| |_| *
* *
************************************/
Emails and plain text contexts. Anywhere rich formatting isn't available, ASCII art provides visual structure. It works in every email client, every terminal, every text file.
Error pages and easter eggs. The classic "you are not connected" pages, hidden messages in source code comments, and developer easter eggs often use ASCII art because it renders correctly everywhere.
Tips for better ASCII art
Match your font to your medium. A font that looks great in a 120-column terminal will be unreadable in a narrow chat window. Simpler fonts (fewer characters per letter) work better in constrained spaces.
Account for line height. Different terminals and text renderers have different line heights. ASCII art designed for one terminal might have visible gaps in another. If you're distributing ASCII art, test it in at least two different environments.
Use a monospace font. Always. ASCII art depends on characters being the same width. If it's displayed in a proportional font, everything misaligns. In HTML, wrap it in <pre> tags or apply font-family: monospace.
Keep the character count reasonable. A 200-column ASCII banner that requires horizontal scrolling defeats the purpose. Aim for 60-80 columns for terminal output, which fits most default terminal widths.
For generating ASCII art without installing figlet or writing conversion code, I built a tool at zovo.one/free-tools/ascii-art-generator that converts images and text to ASCII art with multiple style options. It's useful for quick banners, README decorations, and anywhere you need plain-text visuals.
ASCII art is a constraint-based medium: limited character set, fixed-width grid, no color (usually). Those constraints are what make it interesting. Working within them teaches you to think about visual density, proportion, and readability in ways that transfer to other areas of design.
I'm Michael Lip. I build free developer tools at zovo.one. 350+ tools, all private, all free.
Top comments (0)