Danielle Hazen
DesignProductEngineering

Image Annotation App

A browser-based callout tool that auto-renumbers annotations as you reorder them — cutting a tedious manual-editing step out of documentation workflow.

Role: Designer & developer (solo) Stack: React, Vite, SVG, embedded in Next.js Links: GitHub repo · Live demo


The problem

I write technical manuals and training content that lean heavily on numbered callouts — the little numbered leader lines on a photo pointing to "1 = release lever, 2 = safety guard," etc. Every time a step got reordered or added, I was manually re-typing every number downstream and double-checking every line still pointed the right place. Tedious, easy to mess up, and had nothing to do with the actual content.

I wanted the number to just be "position in the list" — reorder the list, everything renumbers itself.

Why not just use Illustrator?

That was the first instinct, since a lot of our source diagrams already live there. Turned out to be a dead end for a few reasons:

  • Illustrator's modern plugin platform (UXP) is still locked behind an internal Adobe program — not actually buildable against yet, despite what some marketing pages imply.
  • The older, scriptable options (ExtendScript, CEP) could technically work, but either meant no real drag-and-drop reordering, or a lot of legacy tooling and code-signing overhead for what should've been a simple tool.
  • MadCap Flare (my authoring system) is running in Parallels on my Mac, and importing and exporting files between operating systems slows me down. I need a quick way to add callouts consistently.
  • Engineers working on projects with me do not have consistent access to image editing solutions, and annotations would differ greatly between each contributor.

Once I stopped assuming it had to live inside Illustrator, the answer got a lot simpler: just build it as a small web app. No plugin architecture, no signing, no per-app constraints — just a browser.

What it does

  • Click an image to drop a numbered callout; drag the point and the label independently.
  • Drag-reorder the callout list — numbers recalculate live from whatever starting number you set.
  • One number, multiple leader lines — for parts that get called out in more than one spot.
  • Named style presets (color, line weight, cap shape — including a couple of custom ones I added, like a heart and a flower — badge color, font), so callouts stay consistent across a whole manual.
  • A zoom mode that shows the image at true 1:1 / 96 DPI, so what you see on screen matches what it'll actually look like at real size.
  • Export at a capped resolution (defaults to 800px, my usual habit) or full native size — decoupled from whatever zoom you're viewing at, so it's predictable either way.
  • Turn any callout into a clickable hotspot with a link (a store page, a sub-assembly drawing) and export it as an interactive HTML image map — either a standalone file or a snippet you can paste straight into an existing page.
  • Save/reopen a project as JSON, so a diagram stays editable instead of being a one-shot export.

A few decisions worth mentioning

  • Positions are stored as fractions of the image (0–1), not pixels. That's the one choice that made zoom, window resizing, and exporting at arbitrary resolutions all just work, without extra math to reconcile them.
  • Export size and on-screen zoom used to be accidentally linked — an early bug meant zooming in before hitting export could shrink your callouts. Fixed by basing export scale purely on image-size ratios, not UI state.
  • Old saved styles can be missing newer fields (I added a custom cap-size setting after some styles were already saved). Rather than patch it once, I added a small normalization step everywhere saved data gets loaded in, so it just fills in sane defaults instead of quietly breaking later.
  • Image maps use real <map>/<area> markup, not a div of invisible positioned links. I'd built the absolutely-positioned version first, but it doesn't match how assistive tech and scrapers actually expect an image map to be marked up (the W3C WAI pattern exists for a reason). Switching to native <area> elements turned out simpler too — browsers already scale the hit regions to match however large the image renders, so I could drop the percentage-positioning math entirely.
  • The exported HTML almost shipped a subtle bug: an early version downloaded the annotated image and the HTML as two separate files that had to land with matching names. Browsers auto-rename duplicate downloads, so a repeat export could quietly point the HTML at a stale image with no annotations on it. Caught it by actually opening the exported file rather than trusting the code — now the downloadable version embeds the image directly so there's nothing to get out of sync.

Shipping it

Packaged as a small Vite + React repo, licensed CC BY-NC 4.0 (free to use and adapt with credit, not for resale), pushed to GitHub, and embedded directly as a page on my own site — no backend needed, since the whole thing runs client-side.

Why it's here

It's a small tool, but it kills a specific, recurring annoyance in a workflow I own end-to-end. More than that, it's a decent snapshot of how I like to work: start from the actual friction, question the obvious tool once the constraints get clear (even after I'd already started down that path), and ship something usable instead of something theoretically perfect.