The Problem
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.
The AI-Assisted Development Journey
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.
The Initial Prompt
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.
Where AI Got It Wrong
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:
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.
What AI Got Right
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:
const translations = {
'zh-CN': { title: 'ASCII Art 生成器', inputLabel: '输入文本', ... },
'en': { title: 'ASCII Art Generator', inputLabel: 'Input Text', ... }
};
The Iteration Loop
Here's what the real workflow looked like:
- Prompt: "Add a character set selector" → AI adds a dropdown with "Standard", "Wide", "Narrow" options
- Test: I type "Hello" and switch to "Wide" → Nothing changes
- Debug: I look at the code and realize the font data doesn't have a "Wide" variant
- New prompt: "The character set selector doesn't work because there's no wide font data. Generate a wide variant of the Standard font"
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.
Key Technical Decisions
Why Vanilla JS?
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 Font Data Structure
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):
const fonts = {
standard: {
'A': [' ██ ', ' ████ ', '██ ██', '██████', '██ ██'],
'B': ['█████ ', '██ ██', '█████ ', '██ ██', '█████ '],
// ... 36 more characters
}
};
This structure makes rendering trivial:
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');
}
Copy to Clipboard
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.
The "Wait, That's a CSS Issue" Moment
Spoiler: it was a CSS issue. It's always a CSS issue.
The ASCII art looked perfect in the <pre> tag, but when I copied it to the clipboard and pasted it into a terminal or Discord, the spacing was off. The culprit? The font-family in the <pre> tag used system fonts that rendered wider than standard monospace fonts.
The fix was to force a specific monospace font stack and set a fixed font-size:
.result-box pre {
font-family: 'Courier New', Courier, monospace;
font-size: 12px;
line-height: 1.2;
letter-spacing: 0;
}
This ensures that what you see is what you get when you paste elsewhere.
Lessons Learned
1. AI Can't Read Your Mind
The AI didn't know that "character set selector" meant I wanted different font widths. I had to be specific about what each feature should do and what data it needed. The more context I gave, the better the output.
2. Test Edge Cases Early
The AI-generated code handled common cases well but fell apart on edge cases: empty input, very long text, special characters, and mixed language input. I should have tested these before asking for new features.
3. Font Data is the Hard Part
Writing the rendering logic is easy. Getting 5 fonts × 36 characters of perfectly aligned ASCII art is the real work. AI made this feasible — I would have given up on the third font otherwise.
4. Keep It Simple
Despite all the features I listed, the core functionality is just: take text, look up characters in a font object, render as a string. Everything else is sugar on top.
The Result
During this process, I built a small browser-based tool to make this workflow easier. It's a single HTML file with no dependencies, works offline, and handles the basics well. You can check it out at Craftvo's ASCII Art Generator if you're curious.
The tool supports 5 fonts, real-time preview, copy/download functionality, and automatic wrapping for long text. It handles non-ASCII characters by falling back to the original character, so "Café" renders as "Café" rather than "CAF" with a missing character.
Final Thoughts
AI-assisted development for this project was a net win. The AI handled the tedious parts (font data generation, boilerplate CSS) and I handled the architectural decisions and edge cases. But it wasn't magic — I still had to know what to ask for and how to test the results.
The best workflow was iterative: prompt, test, debug, refine. Each cycle took minutes rather than hours, and the end result was better than anything I would have written alone in the same timeframe.
If you're thinking about building a similar tool, my advice is: let the AI do the heavy lifting, but don't trust it blindly. Test everything, especially the edge cases, and be prepared to fix the things it gets wrong.
Now if you'll excuse me, I need to generate an ASCII art banner for my next commit message.
This article was originally published by DEV Community and written by ggwork.
Read original article on DEV Community