DEV Community

ggwork
ggwork

Posted on

Building an ASCII Table Tool: Why I Ditched the Spreadsheet and Made My Own

I was debugging a network protocol implementation last month when I hit a wall. I needed to quickly verify what byte value corresponded to a specific control character — was it 0x1B for ESC or was that 0x1C? My go-to approach was to open a random ASCII chart website, but every single one I found was either:

  • Buried in ads and popups
  • Missing the control characters entirely (you know, the actually useful ones)
  • So cluttered with unnecessary features that the table was half the screen

So I did what any reasonable developer would do: I spent an afternoon building my own. Because apparently I enjoy reinventing wheels.

The Initial Approach: Data First, UI Later

The first decision was the data structure. I needed all 128 standard ASCII characters (0-127), each with:

  • Decimal, hex, octal, and binary representations
  • HTML entity (for the printable ones)
  • Standard name (like "Capital A" or "Line Feed")

I started by hardcoding an array of objects:

const ASCII_CHARS = [
  { dec: 0, name: 'NUL', control: true },
  { dec: 65, name: 'Capital A', char: 'A' },
  // ... 126 more entries
];
Enter fullscreen mode Exit fullscreen mode

This was tedious but straightforward. The real challenge came with control characters — you can't just display \x00 and expect users to know what it means. I needed proper names: NUL, SOH, STX, and so on.

The AI Collaboration: Where It Helped and Where It Didn't

This is where AI-assisted development got interesting. I described the requirements to Claude — "I need an ASCII table with all 128 characters, multi-format display, search, and copy functionality" — and it generated a solid foundation on the first try.

The data generation was where AI really shined. I asked it to generate the complete character metadata, and it produced accurate names, hex codes, and HTML entities. This saved me probably 30 minutes of manual data entry and potential typos.

But here's where it went wrong: the AI's first version had a subtle bug. It was using String.fromCharCode() directly to display characters, which works fine for printable characters but breaks for control characters — they render as invisible or garbled. The AI didn't catch this because it never actually rendered the table.

I had to step in and add explicit handling:

function formatChar(dec) {
  if (dec < 32 || dec === 127) return ''; // Use visual placeholder
  return String.fromCharCode(dec);
}
Enter fullscreen mode Exit fullscreen mode

This is the difference between "code that compiles" and "code that actually works." The AI was great at generating the skeleton, but it missed the edge cases that only become obvious when you're looking at a rendered page.

Performance: The Sticky Header Problem

One thing I insisted on was a sticky table header — when you're scrolling through 128 rows, you need to see what column you're in. This turned out to be surprisingly tricky.

The naive approach is position: sticky; top: 0 on the th elements. But this breaks when the table is inside a scrollable container with a border. The header would scroll away or get clipped by the container's overflow.

The fix was to ensure the scroll container is a direct parent with proper overflow settings:

.table-wrap {
  overflow: auto;
  max-height: 600px;
}

th {
  position: sticky;
  top: 0;
  z-index: 1;
}
Enter fullscreen mode Exit fullscreen mode

Simple in hindsight, but it took me a few iterations to get the z-index right — otherwise the sticky header would appear under the table rows when scrolling.

Search: The Unexpected Complexity

Search seemed straightforward: filter by decimal, hex, or name. But I quickly realized that users think in different formats. Some search for "65", others for "0x41", others for "A", and others for "Capital A".

The solution was to normalize the input and search across multiple fields:

function filterRows(query) {
  const q = query.toLowerCase().trim();
  if (!q) return allRows;

  return allRows.filter(row => 
    row.dec.toString().includes(q) ||
    row.hex.includes(q.replace('0x', '')) ||
    row.name.toLowerCase().includes(q)
  );
}
Enter fullscreen mode Exit fullscreen mode

The AI initially generated a search that only matched exact decimal values — completely missing the use case where someone types "A" or "0x41". I had to iterate on the prompt: "Search should be fuzzy, matching partial strings across multiple fields."

The Copy Feature: Clipboard API Gotchas

Click-to-copy sounds simple, but there's a catch: the Clipboard API only works in secure contexts (HTTPS or localhost). Since this is a static tool, I need to handle the fallback for HTTP environments:

async function copyHex(hex) {
  try {
    await navigator.clipboard.writeText(hex);
    showStatus(`Copied: ${hex}`);
  } catch (err) {
    // Fallback for older browsers
    const textarea = document.createElement('textarea');
    textarea.value = hex;
    document.body.appendChild(textarea);
    textarea.select();
    document.execCommand('copy');
    document.body.removeChild(textarea);
  }
}
Enter fullscreen mode Exit fullscreen mode

The AI didn't consider this edge case at all — it just used navigator.clipboard without any error handling. This would have silently failed for users on HTTP.

Mobile Responsiveness: The Table That Wouldn't Fit

Tables are inherently non-responsive, and ASCII tables are no exception. With 7 columns (DEC, HEX, OCT, BIN, CHAR, NAME, HTML), it's impossible to fit everything on a 320px screen.

My solution was progressive disclosure:

@media (max-width: 600px) {
  .hide-sm { display: none; }
}
Enter fullscreen mode Exit fullscreen mode

This hides the HTML entity column on small screens. Not perfect, but functional. The table becomes scrollable horizontally if needed, but at least the essential columns are visible.

What AI Got Right (and What It Didn't)

AI nailed:

  • Generating the complete character dataset with zero typos
  • Setting up the basic table structure and i18n scaffolding
  • Producing a clean, consistent CSS foundation

AI struggled with:

  • Edge cases (control characters, clipboard fallbacks)
  • Understanding the actual user experience (search that works the way people think)
  • Performance considerations (sticky headers, DOM rendering at scale)

The pattern I've noticed: AI is excellent at generating the "happy path" but consistently misses the "sad path" — the error handling, edge cases, and browser quirks that make production code actually production-ready.

The i18n Decision

Since I'm building this for a global audience, I needed both Chinese and English support. The lightweight approach was a simple dictionary-based system:

const I18N = {
  zh: { title: "ASCII 表", searchPlaceholder: "搜索..." },
  en: { title: "ASCII Table", searchPlaceholder: "Search..." }
};
Enter fullscreen mode Exit fullscreen mode

This is intentional — I didn't want to pull in a full i18n library for a single tool. The trade-off is that adding a new language requires duplicating all strings, but for a tool with maybe 20 user-facing strings, it's manageable.

Lessons Learned

  1. AI is a great pair programmer, not a replacement. It saved me time on boilerplate and data generation, but I still needed to understand the domain well enough to catch its mistakes.

  2. Edge cases are where the real work lives. Any developer can render an ASCII table. Making it actually useful — with proper control character handling, intuitive search, and graceful fallbacks — is the difference between a toy and a tool.

  3. Sometimes the old ways are fine. I briefly considered using a framework like React or Vue, but for a static reference tool, vanilla JS was simpler, faster to load, and had zero dependencies to maintain.

  4. Performance matters even for simple tools. The sticky header and efficient filtering make the tool feel instant. Users notice when a "simple" tool is sluggish.

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 — just open it in a browser and it works. The complete ASCII table (0-127) with multi-format display, fuzzy search, category filtering, and click-to-copy functionality.

If you find yourself constantly opening random ASCII chart websites that are cluttered with ads, or if you just want a clean, fast reference tool, you might find it useful. Try it here.

The irony isn't lost on me — I spent four hours building a tool that saves me maybe 30 seconds each time I need to look up a character code. But those 30 seconds add up, and now I have a tool that works exactly the way I want it to. Sometimes reinventing the wheel is worth it.

Top comments (0)