TechEarl

How to Turn an Image Into ASCII Art From the Command Line

Turn any image into ASCII art from the terminal: jp2a for the classic monospace look (with ANSI color and an invert flag for dark terminals), and chafa for truecolor, Unicode-detail, and even animated GIFs.

Ishan Karunaratne⏱️ 10 min readUpdated
Share thisCopied
Turn a JPG or PNG into ASCII art from the command line with jp2a for the classic look and chafa for truecolor Unicode detail and animated GIFs.

The two tools that turn an image into terminal art are jp2a for the classic monospace ASCII look, and chafa for high-detail, truecolor output.

Every command on this page converts the same source image, so you can compare what each tool and flag actually does to the same picture. Here is the original:

The original 1024 by 1024 anime workstation illustration used as the source image for every jp2a and chafa example
The source image, a 1024-pixel anime workstation scene. Every output below is this exact picture run through jp2a or chafa (the jp2a examples call it photo.jpg, the chafa ones photo.png, same image).

The minimal versions:

bash
# Classic ASCII characters, 80 columns wide
jp2a --width=80 photo.jpg

# Modern, full-color, far more detail
chafa photo.png

jp2a gives you the recognizable typewriter-grain ASCII portrait. chafa is the one to reach for when you want the image to actually look good in the terminal, because it uses Unicode block and braille characters and 24-bit color instead of a 70-odd character ramp. The rest of this page is when to pick which, the flags that matter, and the one setting that changes the result more than any other: how wide you make it for the wide terminals everyone actually runs in 2026.

jp2a: the classic ASCII look

jp2a maps each block of pixels to a single ASCII character chosen by brightness, which produces the familiar grayscale-text portrait. Despite the name, it reads both JPEG and PNG.

bash
jp2a --width=80 photo.jpg
Classic monochrome ASCII art portrait of the source image produced by jp2a at 80 columns
jp2a --width=80: the classic typewriter-grain look, one ASCII character per cell chosen by brightness. 80 columns is the portable width, it fits comfortably in any terminal, README, or email. Recognizable, but coarse. It gets much sharper wider, more on that below.

The most useful flags:

bash
# ANSI color in the terminal (truecolor where supported, otherwise the terminal's ANSI palette)
jp2a --colors --width=200 photo.jpg

# Invert brightness for a light-on-dark terminal
jp2a --invert --width=120 photo.jpg

# Save the plain-text art to a file instead of printing it
jp2a --width=80 --output=art.txt photo.jpg
The same source image as ANSI-colored ASCII art from jp2a at 200 columns
jp2a --colors --width=200: the same ASCII characters, now carrying the image's colors via ANSI escape codes, and wide enough to actually read the scene.
jp2a output with inverted brightness mapping so it reads correctly on a dark terminal
jp2a --invert --width=120: brightness flipped for a dark terminal. Without it, the default ink-on-paper ramp comes out as a photo negative.

A few things worth knowing:

  • --colors prints with ANSI escape codes so the characters carry the image's colors. It looks great in the terminal but is no longer plain text, so do not pipe it somewhere that expects clean ASCII (more on saving below).
  • --invert flips the brightness-to-character mapping. The default ramp assumes a dark character on a light background (like ink on paper). On a normal dark terminal that comes out as a photo negative, so --invert is the flag you almost always want there.
  • --output=art.txt writes the result to a file. Without --colors, that file is portable plain text you can paste into a README or a code comment. jp2a can also emit HTML with --html if you want a colored version for the web.

If you only pass --width, jp2a works out the height from the image's aspect ratio, correcting for the fact that terminal cells are taller than they are wide. You rarely need to set both.

chafa: the modern, high-detail tool

chafa is the one I reach for when the output needs to look like the actual picture. Instead of a single ASCII character per cell it draws from Unicode block, quadrant, sextant, and braille glyphs, and it renders in 24-bit truecolor, so a single character cell can carry far more visual information than one ASCII glyph ever could.

bash
chafa photo.png
High-detail truecolor terminal rendering of the source image by chafa filling a wide modern terminal with Unicode block characters and 24-bit color
chafa photo.png with no flags: truecolor Unicode-block character art, sized to fill the terminal. At about 200 columns this is as close to the source as character art gets. (On a terminal that speaks a graphics protocol, chafa sends an even sharper real bitmap instead, more below.)

With no flags chafa does two things automatically, and both matter. First, it sizes the art to your whole terminal, not some small default, so on a wide modern window it has hundreds of columns to work with (much more on that below). Second, it auto-detects the best format your terminal supports: if the terminal speaks a real graphics protocol (sixel, Kitty, or iTerm2) chafa sends an actual bitmap; otherwise it falls back to Unicode character art. That detection is the whole appeal: you get the best your terminal can show, at full size, without configuring anything.

Useful flags:

bash
# Constrain to a width x height in character cells
chafa --size=100x40 photo.png

# Pure monochrome braille art (no color, dense detail)
chafa --colors none --symbols braille photo.png

# Force a color mode if auto-detect guesses wrong: none, 2, 8, 16, 240, 256, full
chafa --colors 256 photo.png

# Force plain ASCII-only symbols (closest to the jp2a look)
chafa --symbols ascii photo.png
Dense monochrome braille-character rendering of the source image from chafa
chafa --colors none --symbols braille: no color, but braille dots pack the most pixels per cell, so the detail is the sharpest of any mode here.
chafa rendering of the image forced to 256-color mode
chafa --colors 256: drop from truecolor down to 256 colors for terminals or screen recordings that mangle 24-bit color.
chafa output restricted to plain ASCII symbols but still rendered in color
chafa --symbols ascii: ASCII-only glyphs (copy-paste friendly) but still colored, the middle ground between jp2a and full chafa.

The --symbols classes (block, braille, ascii, half, quad, and more) let you trade detail for portability: braille packs the most pixels per cell, ascii keeps it copy-paste friendly. They only apply when chafa is drawing character art, so on a terminal with a graphics protocol add -f symbols to force character output instead of a bitmap. --colors full is the truecolor mode; chafa already picks a sensible color depth from your environment by default, drop to 256 or 16 for terminals or recordings that mangle 24-bit color.

Animated GIFs

chafa also plays animated GIFs straight in the terminal, looping the frames in place:

bash
chafa animation.gif

This is genuinely useful for previewing a GIF over SSH without a desktop. jp2a does not do this.

How wide should the art be?

Width is the single biggest lever on how good the output looks, and the right number depends entirely on what you are doing with it.

80 columns is the safe portable width. It is the convention that reads comfortably almost everywhere: a README code block (anything wider starts to need horizontal scrolling, and looks worst on a narrow or mobile view), a Git commit message, an email, or an SSH login banner that has to fit whatever window someone connects with. It is coarse, but it is universal, which is exactly why it is still the default. Use 80 whenever the art is going to be saved and read somewhere else, the --output=art.txt case.

But a terminal in 2026 is rarely 80 columns. It depends on your font size and display scaling, but a maximized terminal on a retina MacBook commonly lands somewhere around 200 to 260 columns (on my 16-inch I get 266). A 1440p or 4K monitor, or a smaller font, goes higher still. Check your own:

bash
tput cols

Whatever that prints is how much detail your terminal can actually show, commonly two to four times the old 80. The same jp2a command, just wider, climbs from "recognizable" to "you can read the scene" (compare these to the 80-column version higher up):

jp2a ASCII art of the source image at 120 columns, noticeably more detailed than 80
jp2a --width=120: a common modern editor width. The figure, the monitors, and the desk start to resolve.
jp2a ASCII art of the source image at 200 columns, reading clearly as the original photo
jp2a --width=200: a full maximized terminal. The same plain ASCII characters, but now it genuinely reads as the picture.

chafa behaves the same way, and by default it sizes the art to your detected terminal, which is why its no-flag output (the large one above) looks so good. Starve it of columns and the detail collapses just like jp2a does:

chafa truecolor output forced down to 80 columns, blocky and coarse
chafa --size=80x40: even chafa goes blocky when you pin it to 80 columns.
chafa truecolor output at 120 columns, sharper than 80 but short of full width
chafa --size=120x60: sharper, but still short of what your full terminal gives it for free (the big version above).

So feed your real column count straight in:

bash
# jp2a has to be told the width; chafa uses the detected terminal size
jp2a --width=$(tput cols) photo.jpg
chafa photo.png

The rule for art you are viewing live: do not ask for more columns than the terminal has. Art wider than the window wraps every line and turns the picture into noise, so tput cols is the ceiling for direct viewing. (Saving to a file, or viewing with wrap turned off, is the exception, there you can go as wide as you like.) Below that ceiling, wider is better for looking at; 80 is better for saving and sharing.

Plain text vs colored output: which to save

This distinction trips people up, so it is worth being explicit:

  • Plain ASCII text (no color flag) is portable. jp2a --width=80 --output=art.txt photo.jpg gives you a .txt you can drop into a README, a Git commit, or an email, and it renders anywhere a monospace font does, as long as the destination keeps the whitespace and has room for the width.
  • Colored ANSI output (jp2a --colors, or chafa's default) embeds terminal escape codes. Redirect it to a file and the file contains those raw codes; it only displays correctly when cat-ed back into an ANSI-capable terminal, not in a plain text viewer.
bash
# Portable plain text
jp2a --width=80 --output=banner.txt photo.jpg

# Colored, terminal-only (escape codes baked in)
jp2a --colors --width=80 photo.jpg > color-art.ansi
cat color-art.ansi   # replays the colors

So: if the destination is a document or anywhere outside a terminal, keep it plain. If it is going to live in a terminal (a login banner, a cat-able file, an SSH session), color is fine.

Other tools worth knowing

jp2a and chafa cover almost every case, but two more come up in roundups and are worth a sentence each:

  • img2txt ships with the libcaca text-mode graphics library (the caca-utils package). It reads PNG, JPEG, GIF, and BMP, and its real strength is the output formats: besides colored ANSI it can emit HTML, SVG, IRC color codes, and PostScript. If you need ASCII art as SVG or HTML rather than terminal escape codes, it is the tool that does it directly. The character output itself is coarser than chafa's Unicode rendering.
  • ascii-image-converter is a single cross-platform Go binary (Linux, macOS, Windows) that does colored ASCII and braille art, reads JPEG, PNG, BMP, WEBP, TIFF, and GIF, and saves straight to a PNG or text file. It is the easiest to install on Windows where jp2a and chafa are fiddlier.

For most of what I do, the split is still: chafa for fidelity, jp2a for the classic plain-text look. The other two are situational.

FAQ

It depends on the destination. Use 80 columns for anything you save and share (a README, a commit message, an email), because 80 is the one width that renders un-wrapped everywhere. For art you are just looking at in your own terminal, use your full width: run tput cols to see it (a maximized terminal on a modern retina display is often 200 to 266 columns, far more than 80), then jp2a --width=$(tput cols) photo.jpg. chafa already fills the terminal by default. Wider always means more detail, up to your column count; going past it just wraps the lines into noise.

Use chafa when you want the output to look like the actual image: it uses truecolor and Unicode block and braille characters for far more detail, and it auto-sizes to your terminal. Use jp2a when you specifically want the classic monospace ASCII-character look, or portable plain text you can paste into a README.

Both are in the usual package managers. On Debian or Ubuntu, sudo apt install jp2a chafa. On macOS with Homebrew, brew install jp2a chafa. On Arch, sudo pacman -S jp2a chafa.

Current jp2a reads both JPEG and PNG, despite the name (the "j2" is historical). If you have an older build that only handles JPEG, convert first with ImageMagick: magick in.png in.jpg, then run jp2a on the JPEG.

jp2a's default brightness ramp assumes a dark character on a light background. On a dark terminal that inverts the apparent brightness. Add --invert and it will read correctly on a light-on-dark terminal.

For portable plain text, use jp2a without a color flag: jp2a --width=80 --output=art.txt photo.jpg. Avoid --colors for that purpose, because it bakes in ANSI escape codes that only render inside a terminal, not in a document or a code comment.

See also

Sources

Authoritative references this article was fact-checked against.

Tagsjp2achafaASCII artCLIterminalimage conversionLinux

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Software Systems Architect · Senior Software Engineer · Engineering Leadership

Software systems architect and senior software engineer with more than two decades designing, building, and running production software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Now a CTO, though what I write here is drawn from the full arc of that work, across architecture, engineering, and operations, not any single job.

Keep reading

Related posts