Free ASCII art generator
An ASCII art generator that hands you the ramp
This generator turns a picture into monospace text and lets you decide which characters do the shading. Four ramps are built in — 70 levels, 16 that survive a code fence with nothing escaped, 10 for small sizes, and the five Unicode shade blocks — and any string of up to 96 characters replaces them. It is free, no account exists to make, and the work happens in the tab you are reading: the picture is averaged onto a grid 20 to 300 characters wide, each cell is matched to a level of your ramp, and the text comes back with an exact count for every character it used.
- 100% free
- No signup
- 20 to 300 wide
- 4 ramps or your own
- Ink measured per glyph
Press Ctrl+V anywhere on the page to drop in a picture, or a ramp string you copied out of somebody else's converter — no need to click the field first.
20 to 300. 80 is the width a terminal window and a plain-text email both hold without folding a line, and it is the safest number to leave here. The depth follows from the shape of your picture.
The brightest part of the picture becomes the last character in the ramp, and anything transparent is flattened onto white first.
Densest character first, blank last, up to 96 of them — 70 at the moment. The 70-character ramp Paul Bourke published and nearly every converter since has copied. It holds the most gradation plain ASCII can carry, and it is also the one that turns to mush below about 9 px because half its levels differ by a single stroke.
Multiplies each cell’s distance from mid-lightness before the ramp sees it. Above 1 the mid tones split apart and the ends flatten; below 1 everything crowds into the middle third of the ramp.
Shifts the whole picture along the ramp afterwards. A scan on gray paper usually needs a positive nudge here rather than more contrast, which would only tear the mid tones apart.
How to get a picture to read as characters
What you bring in settles how much the width and the ramp are still able to rescue.
Bring a picture that has edges, not just tone
A logo, a line drawing, a screenshot of a chart and a high-contrast portrait all convert well, because each character is being asked to stand for one recognizable thing. Work out the budget before you drop the file: at 100 characters a line every glyph is describing roughly a hundredth of the width, so a face that occupies a fifth of the frame gets twenty characters to be a face in. If the subject is smaller than that in the original, crop to it first — the converter will not find detail that is only two pixels wide.
Set the width, then argue with the ramp
Width is the decision that cannot be undone by any other control, because it fixes how much of the picture each character has to carry. 80 is the width a terminal and a plain-text email both hold without folding; 100 to 140 suits a page you control. Only once the width is settled does the ramp matter: a 70-level ramp at 60 characters a line is spending gradation nobody can see, and a 10-level ramp at 200 will band across a sky.
Read the ink column, then cut what is not being used
Beside every level of the ramp is its measured ink coverage and the number of cells that chose it. Levels sitting at zero are ones the picture never reached, and a ramp with six of them is longer than this picture needs — the shorter ramp will read better at the same size, not worse. If the counts are piled onto two or three levels instead, that is contrast rather than ramp length: raise the multiplier until the counts spread, and nudge brightness rather than contrast when the whole picture is simply too dark.
Technical specifications
| Line width | 20 to 300 characters, 100 by default. The depth is not a control — it comes from the source proportions at a fixed cell ratio of 0.50 |
|---|---|
| Character sets on offer | 4 built in — 70, 16 and 10 levels of ASCII plus the 5 Unicode shade blocks U+2591 to U+2588 — plus any string of 2 to 96 characters typed or pasted in |
| Ink coverage measurement | 99 glyphs, space through tilde plus the four shade blocks, each drawn once into a cell-sized canvas in this page's own monospace face and scored on the alpha it lays down. Antialiasing counts, because it is ink |
| What decides a character | Oklab lightness of the cell's mean color, averaged in linear light, then a gain of 0.20 to 3.00 about mid-lightness and an offset of −0.50 to +0.50 |
| Cell proportion assumed | 0.50 wide to tall — a 0.60 em advance at a 1.20 line-height. Fixed here on purpose; the photo-to-ASCII page measures it instead |
| Transparent pixels | Composited onto white for a light page and onto black for a dark screen before the cell is averaged, so they land at one end of the ramp rather than in the middle of it |
| Ways to take the text away | Clipboard, a .txt named for the grid it came from, and a print sheet at 5.5 pt — 163 characters across a portrait A4 with 10 mm margins, 238 across a landscape one |
| What crosses the network | Nothing once the page has loaded. No upload, no API call and no font fetched at conversion time |
Frequently asked questions
Why does my ASCII art come out stretched vertically?
Because the place you pasted it into sets its lines further apart than this page assumes. A character cell is not square: the glyph advance of a monospace face is around 0.60 em and the leading is usually 1.2 em, which makes a cell twice as tall as it is wide, and this page divides the row count by two to compensate. Paste that result somewhere with a 1.6 line-height — many chat clients and blog templates — and every row is suddenly a third taller than the arithmetic assumed, so the picture grows. The fix is either to set the leading where you paste it, or to convert on the photo-to-ASCII page, which measures the ratio instead of assuming it.
Which ramp should I pick?
Start at 10 levels and only go longer if the result looks banded. Ramp length buys gradation, and gradation is only visible when a character is big enough to tell apart from its neighbors: below about 9 pixels the 70-level ramp collapses because half its levels differ by a single stroke. The 16-character paste-safe set is the one to reach for when the destination is a code fence, an HTML block or a shell heredoc, since it contains no backtick, backslash, angle bracket, ampersand or dollar sign and therefore needs no escaping anywhere.
Why did my ramp reorder itself when I ticked the measurement box?
Because the order it shipped in was decided by eye in a different typeface. Whether a percent sign lays down more ink than a hash depends on stroke weight, counter size and the advance the face was drawn to, and none of that transfers between fonts — the published ramps in circulation were all ordered once, by one person, in whatever they were using at the time. Ticking the box draws each of your ramp's glyphs into a cell-sized canvas in the monospace face this page is actually rendering in and sorts by the fraction of the cell it darkens. The count of characters that moved is printed beside the box, and on the 70-level ramp it is rarely fewer than twenty.
The result breaks when I paste it into a message. What is going wrong?
Almost always a backslash, a backtick or an underscore being read as formatting rather than as ink. The standard 70-level ramp contains all three, so a chat client with Markdown turns runs of them into italics, code spans and stray escapes, and a shell heredoc eats the backslashes outright. Switch to the paste-safe 16-level ramp, which was assembled by removing exactly those characters, or wrap the text in a fenced code block before sending it. The block-element ramp has the opposite problem: it is clean everywhere Markdown is concerned and arrives as question marks anywhere the pipe is not UTF-8.
What is the space at the end of the ramp for?
It is the paper, and a ramp without one has no white in it. The last entry is what the brightest cells become, so a ramp ending in a period fills every highlight with periods and the result reads as a rectangle of texture rather than as a picture with a subject. If you paste in a ramp copied from somewhere else, check whether the trailing space survived the copy — it is the character most often lost on the way, and losing it is the single commonest reason a borrowed ramp looks worse here than it did where you found it.
Can I feed it a photograph rather than a drawing?
You can, and it will work better if you push the contrast well past 1 first. A photograph's lightness is bunched into the middle of the scale, so a straight conversion reaches four or five levels of a ten-level ramp and none of a seventy-level one, which is why photographs so often come back as flat gray texture. This page gives you a gain and an offset to fix that by hand. If you would rather have the correction computed from the picture's own histogram, along with the aspect ratio measured rather than assumed, that is what the photo-to-ASCII page is for.
About character ramps, and why every one of them is somebody's guess
Every ASCII converter rests on one list: a string of characters ordered from the one that fills the most of its cell to the one that fills none. The string almost everybody uses is Paul Bourke’s 70-character ramp, published decades ago and copied since into more tools than anyone has counted, with a 10-character short version beside it. Both are good lists. Neither is a measurement — they were ordered by eye, once, in whatever typeface was on the machine at the time, and the ordering does not survive the trip to yours. Whether % outweighs # is a question about stroke weight and counter size, and two monospace faces will answer it differently. That is why this page measures instead: each glyph is drawn once into a cell-sized canvas in the face you are reading this in, and the alpha it lays down is its coverage. On the 70-character ramp the measurement usually moves twenty or more characters out of the published order.
The second thing converters get wrong is quieter. A cell of the picture has to be reduced to one number before a ramp can be indexed into it, and the obvious number — the mean of the red, green and blue bytes — is wrong twice over. Averaging gamma-encoded bytes darkens every edge in the picture, which is why so much ASCII art has a sooty look around fine detail; and a byte value is not proportional to perceived lightness anyway, so a mid-gray that a person calls halfway lands about a fifth of the way up a ramp indexed by it. Both are fixed here in the same place they are fixed for the rest of this site: cells are averaged in linear light, and the number that indexes the ramp is Oklab lightness, where a value of 0.5 genuinely looks halfway between black and white. If you want the same idea applied to outlines rather than to shading, that is photo to line art, which keeps the edges and throws the tone away.
What this page deliberately does not do is guess where the text is going. The row count here is divided by two on the assumption that a character cell is twice as tall as it is wide, which is true of a 0.60 em advance at a 1.2 line-height and false in a chat window that sets its lines 1.6 apart. Guessing is the right call when the destination is unknown, and the wrong one when it is not — so photo to ASCII measures the advance and the leading instead, and adds the tone mapping and the ANSI color a photograph needs. And if what you actually want is a word rendered large in block letters rather than a picture rendered in glyphs, that is a typesetting job rather than a rasterizing one: text to ASCII art does it with figlet-style typefaces and no image at all.
Product and company names shown here are the trademarks of their respective owners. Photo2Pattern is an independent tool and is not affiliated with, sponsored by or endorsed by any of them.
Where the characters get counted
Your picture is opened by JavaScript running in the tab you are reading this in. It is never uploaded, never sent to a server and never stored anywhere — there is no upload step to undo, because there is no upload. Close the tab and nothing of it remains.
A ramp lives as long as the tab does and no longer. Nothing writes it down, nothing syncs it and nothing is attached to your browser between visits, so a ramp you spent time on has to be copied out of the field if you want it tomorrow — and a ramp pasted in from elsewhere is read from the clipboard event and never sent anywhere.