Since you are just interested in the ranking, not the actual distance, you could also consider skipping the sqrt. This gives the same ranking, but will be a little faster.
- His breaking up images into grids was a poor-man's convolution. Render each letter. Render the image. Dot product.
- His "contrast" setting didn't really work. It was meant to emulate a sharpen filter. Convolve with a kernel appropriate for letter size. He operated over the wrong dimensions (intensity, rather than X-Y)
- Dithering should be done with something like Floyd-Steinberg: You spill over errors to adjacent pixels.
Most of these problems have solutions, and in some cases, optimal ones. They were reinvented, perhaps cleverly, but not as well as those standard solutions.
Bonus:
- Handle above as a global optimization problem. Possible with 2026-era CPUs (and even more-so, GPUs).
- Unicode :)
Bravo, beautiful article! The rest of this blog is at this same level of depth, worth a sub: https://alexharri.com/blog
Lucas Pope did a really nice write up on how he developed his dithering system for Return of The Obra Dinn. Recommended if you also enjoyed this blog post.
https://forums.tigsource.com/index.php?topic=40832.msg136374...
Not to take away from this truly amazing write-up (wow), but there's at least one generator that uses shape:
https://meatfighter.com/ascii-silhouettify/
See particularly the image right above where it says "Note how the algorithm selects the largest characters that fit within the outlines of each colored region."
There's also a description at the bottom of how its algorithm works, if anyone wants to compare.
Acerola worked a bit on this in 2024[1], using edge detection to layer correctly oriented |/-\ over the usual brightness-only pass. I think either technique has cases where one looks better than the other.
It reminds me of how chafa uses an 8x8 bitmap for each glyph: https://github.com/hpjansson/chafa/blob/master/chafa/interna...
There's a lot of nitty gritty concerns I haven't dug into: how to make it fast, how to handle colorspaces, or like the author mentions, how to exaggerate contrast for certain scenes. But I think 99% of the time, it will be hard to beat chafa. Such a good library.
EDIT - a gallery of (Unicode-heavy) examples, in case you haven't seen chafa yet: https://hpjansson.org/chafa/gallery/
Are you planning to release this as a library or a tool, or should we just take the relevant MIT licensed code from your website [4]?
[0] https://aleyan.com/projects/ascii-side-of-the-moon
[1] https://news.ycombinator.com/item?id=46421045
[2] https://en.wikipedia.org/wiki/Lunar_mare
> It may seem odd or arbitrary to use circles instead of just splitting the cell into two rectangles, but using circles will give us more flexibility later on.
I still don’t really understand why the inner part of the rectangle can’t just be split in a 2x3 grid. Did I miss the explanation?
Non-ascii, I tried various subsets of Unicode. There’s the geometric shape area, CJK, dingbats, lots of others
Different fonts - there are lots of different monospace fonts. I even tried non-monospaced fonts tho still drawn in grid
ANSI color style https://16colo.rs/
My results weren’t nearly as good as the ones in this article but just suggesting more ways of exploration
https://greggman.github.io/doodles/textme10.html
Note: options are buried in the menu. Best to pick a scene other than the default
Taking into account the shape of different ASCII characters is brilliant, though!
I think there's a small problem with intermediate values in this code snippet:
const maxValue = Math.max(...samplingVector)
samplingVector = samplingVector.map((value) => {
value = x / maxValue; // Normalize
value = Math.pow(x, exponent);
value = x * maxValue; // Denormalize
return value;
})
Replace x by value.in general, ascii rendering is when ascii character codes are converted to pixels. if you wish to render other pixels onto a screen using characters, they are not ascii characters, they are roman or latin character glyphs, no ascii involved. that is all.
It probably has a different looking result, though.
The display mode is actually a hacked up 80x25 text mode. So in that specific narrow case, you have a display mode where text characters very much function as pixels.
I'm hoping people who harness ASCII for stuff like this consider using Code Page 437, or similar. Extended ASCII sets comprising Foreign Chars are for staid business machines, and sort of familiar but out of place accented chars have a bit of a distracting quality.
437 and so on taps the nostalgia for BBS Art, DOS, TUIs scene NFOs, 8 bit micros.... Everything pre Code Page 1252, in other words. Whilst it was a pragmatic decision for MS, it's also true that marketing needs demanded all text interfaces disappeared because they looked old. Text graphics, doubly so. That design space was now reserved for functional icons. A bit of creativity went from (home) computing right there and then. Stuffing it all into a separate font ensured it died.
But, that stuff is genuinely cool to a lot of people in a way VIM, (for example) has never been and nor will it ever. This is a case of Form Over Function. Foreign chars are not as friendly or fun as hearts, building blocks, smileys, musical notes, etc.
(I've previously tried pre-transforming on the image side to do color contrast enhancement, but without success: I take the Sobel filter of an image, and use it to identify regions where I boost contrast. However, since this is a step preceding "rasterization", the results don't align well with character grids.)
GitHub: https://github.com/symisc/ascii_art/blob/master/README.md Docs: https://pixlab.io/art
I feel confident stating that - unless fed something comprehensive like this post as input, and perhaps not even then - an LLM could not do something novel and complex like this, and will not be able to for some time, if ever. I’d love to read about someone proving me wrong on that.
Supports color output, contrast enhancement, custom charsets. MIT licensed.
How do you arrive at that? It's presented like it's a natural conclusion, but if I was trying to adjust contrast... I don't see the connection.
nolen: "unicode braille characters are 2x4 rectangles of dots that can be individually set. That's 8x the pixels you normally get in the terminal! anyway here's a proof of concept terminal SVG renderer using unicode braille", https://x.com/itseieio/status/2011101813647556902
ashfn: "@itseieio You can use 'persistence of vision' to individually address each of the 8 dots with their own color if you want, there's some messy code of an example here", https://x.com/ashfncom/status/2011135962970218736
I found myself thinking, “I wonder if some of this could be used to playback video on old 8-bit machines?” But they’re so underpowered…
Simply trying every character and considering their entire bitmap, and keeping the character that reduces the distance to the target gives better results, at the cost of more CPU.
This is a well known problem because early computers with monitors used to only be able to display characters.
At some point we were able to define custom character bitmap, but not enough custom characters to cover the entire screen, so the problem became more complex. Which new character do you create to reproduce an image optimally?
And separately we could choose the foreground/background color of individual characters, which opened up more possibilities.
However, there might still be room for competition, heh. I always wanted to do this on the _entirety_ of Unicode to try getting the most possible resolution out of the image.
More seriously, using colors (not trivial probably, as it adds another dimension), and some select Unicode characters, this could produce really fancy renderings in consoles!
Reminds me of this underrated library which uses braille alphabet to draw lines. Behold:
https://github.com/tammoippen/plotille
It's a really nice plotting tool for the terminal. For me it increases the utility of LLMs.
I am however am struck with the from an outsider POV highly niche specific terminology used in the title.
"ASCII rendering".
Yes, I know what ASCII is. I understand text rendering in sometimes painful detail. This was something else.
Yes, it's a niche and niches have their own terminologies that may or may not make sense in a broader context.
HN guidelines says "Otherwise please use the original title, unless it is misleading or linkbait; don't editorialize."
I'm not sure what is the best course of action here - perhaps nothing. I keep bumping into this issue all the time at HN, though. Basically the titles very often don't include the context/niche.
Wait...wh...why?!? Of all the things, actual pictures of the planet Saturn are readily available in the public domain. Why poison the internet with fake images of it?