Building an ASCII Art Generator with AI: The Good, The Bad, and The Figlet A developer built a single-file HTML ASCII art generator using AI pair programming, addressing issues with character handling and line wrapping. The tool supports multiple fonts, real-time preview, clipboard copy, and i18n, with the AI handling font data generation efficiently. I was staring at my terminal during a deploy, waiting for the build to finish, when I realized something: I'd been typing figlet "Hello World" into my terminal for years to generate ASCII art for commit messages and README files. But every time I wanted to share that art with someone who wasn't a developer, I hit a wall. "Just install figlet," I'd say. "Install what now?" they'd reply. The problem wasn't that ASCII art tools don't exist online. The problem was that the ones I found were either bloated with ads, required JavaScript frameworks that made the page take forever to load, or couldn't handle non-Latin characters gracefully. I wanted something that just worked in a browser tab, no installation, no server, no fuss. So I decided to build my own. Because apparently I enjoy reinventing wheels. Here's where things get interesting. I've been using AI pair programming for a while now, and this project felt like the perfect test case: it's well-defined, has clear requirements, and involves a lot of repetitive font data that would be tedious to type manually. I started by describing the requirements to an AI assistant in pretty specific terms: Build a single-file HTML tool that converts text to ASCII art. Must have multiple fonts Block, Slant, Small, Standard, Mini . Real-time preview. Copy to clipboard. Download as .txt. Support dark mode. Chinese/English i18n. Vanilla JS only. The AI came back with something surprisingly decent. It had the basic structure right, the font data was embedded, and the rendering logic was clean. But there were issues. The first problem was character handling . The AI assumed that all input would be uppercase English letters. When I tested with lowercase, numbers, and special characters, it just... broke. Not crashed, but silently dropped characters. // What the AI initially wrote simplified function getChar char, font { return font char.toUpperCase || ' '; // Silent failure } The problem? This returns empty strings for any character not in the font data. A user typing "Hello World" would get "HELLO WORLD" in ASCII art, which isn't terrible, but a user typing "Café" would lose the "é" entirely. My fix: I had to explicitly handle non-ASCII characters by falling back to the original character output, so the user sees something rather than nothing. // The fix function getChar char, font { if char === ' ' return font ' ' || ' ' ; if font char.toUpperCase return char ; // Show original char return font char.toUpperCase ; } The second issue was line wrapping . The AI's initial implementation would create a massive horizontal scroll for long text. I wanted automatic wrapping at a configurable width. The AI's approach was to just let it overflow. My approach was to split the input into chunks of N characters before passing to the renderer: js function wrapText text, width { const lines = ; for let i = 0; i < text.length; i += width { lines.push text.slice i, i + width ; } return lines; } This is simple, but it breaks words. For an ASCII art tool, that's actually fine — you're not reading prose, you're looking at a banner. Honestly, the AI handled the font data brilliantly. Typing out 5 different fonts × 36 characters × 5-7 lines each would have taken me an hour. The AI generated all of it in seconds, and the quality was surprisingly good. It also nailed the i18n pattern on the first try. I asked for a simple language toggle, and it implemented a clean dictionary pattern: js const translations = { 'zh-CN': { title: 'ASCII Art 生成器', inputLabel: '输入文本', ... }, 'en': { title: 'ASCII Art Generator', inputLabel: 'Input Text', ... } }; Here's what the real workflow looked like: This back-and-forth happened maybe 5-6 times for various features. Each iteration was faster than if I'd written the code from scratch, but I had to know what to ask for. The AI couldn't anticipate that the character set selector needed actual font data behind it. I could have used React or Vue, but for a single-file tool, that's overkill. Vanilla JS keeps the file self-contained — no build step, no dependencies, just open the HTML and it works. This is important for a tool that people might want to save locally or use offline. The core of the tool is the font data. Each font is an object where each key is a character and each value is an array of strings one string per line of the character : js const fonts = { standard: { 'A': ' ██ ', ' ████ ', '██ ██', '██████', '██ ██' , 'B': '█████ ', '██ ██', '█████ ', '██ ██', '█████ ' , // ... 36 more characters } }; This structure makes rendering trivial: js function renderLine text, font { const lines = ; for let row = 0; row < font 'A' .length; row++ { let line = ''; for const char of text { line += font char.toUpperCase || char row || ' '; } lines.push line ; } return lines.join '\n' ; } The hardest part of the whole project was clipboard functionality. The navigator.clipboard.writeText API requires a secure context HTTPS or localhost , and it returns a Promise that can reject. async function copyToClipboard text { try { await navigator.clipboard.writeText text ; showFeedback 'Copied ' ; } catch err { // Fallback for older browsers const textarea = document.createElement 'textarea' ; textarea.value = text; document.body.appendChild textarea ; textarea.select ; document.execCommand 'copy' ; document.body.removeChild textarea ; showFeedback 'Copied ' ; } } The fallback is ugly but necessary — you never know what browser someone's using. Spoiler: it was a CSS issue. It's always a CSS issue. The ASCII art looked perfect in the