GlyphX: turning photos into ASCII art
Brightness alone makes mushy ASCII art. That's the main thing to know about image-to-ASCII converters, and it's why I built GlyphX the way I did.
A few months ago I watched a reel on customizing your GitHub profile. It pointed to a README generator that turned my profile picture into ASCII art. I pasted it in and moved on. But I kept wondering what it would take to build one myself. So I did.
Try it: glyph-x.vercel.app
Code: github.com/devsensei404/GlyphX
What it does
Upload a photo, paste one, drag it in, or snap one with your camera. Pick a width from 20 to 300 columns, choose mono or color, and download the result as a PNG or JPEG. More columns means more detail. It's that simple.
How it works
Resize the image, measure the brightness of each cell, and map that to a character. GlyphX uses a 70-character ramp ordered by how much ink each glyph puts on screen. $ and @ sit at the dense end, a plain space at the other. On a dark page, bright pixels get the dense characters. Before any of that, the image gets an auto-contrast stretch. Flat, hazy photos turn to mush otherwise.
Characters are roughly twice as tall as they are wide, so each cell has to cover a block twice as tall as it is wide. Skip that and everything comes out stretched.
Edges are the hard part. Brightness gets you something recognizable. Outlines still dissolve into whatever character happens to be nearby. So GlyphX runs a Sobel filter over the image, works out which way each edge runs, and swaps in -, |, / or \ wherever an edge is strong enough. Finding where an edge is takes one filter. Finding which direction it points took me real reading. It's also what takes the output from "recognizable" to "actually looks good."
Color mode is simpler. Each cell keeps the average color of its block from the original photo. Characters come from brightness and edges, color comes straight from the source.
Things I cared about
- Privacy. Flask normally spills larger uploads into temp files on disk. GlyphX keeps them in memory instead, and nothing about your image gets logged. When the request ends, the image is gone.
- Limits. 4 MB per image, a cap on total pixels, and two conversions at a time. Extra requests wait up to 10 seconds, then get a polite "server busy."
- No copy-as-text button, on purpose. 300 columns of ASCII pasted into a chat is a mess. You get a PNG or JPEG instead.
What's next
Nothing. It's live, it does what I wanted, and I'm not adding features. If you try it, I'd like to hear whether you liked the output. That's all I'm after.
This is my first post like this, so go easy on me.




Top comments (0)