DEV Community

hashem alharhashi
hashem alharhashi

Posted on

I shipped 65 tools. Then I deleted 64 of them.

A year ago I shipped a site with 65 small image tools. Resize, crop, compress, convert, rotate — the usual grid of utilities. It looked productive. It wasn't.

Analytics said something uncomfortable: almost nobody used 64 of them. One tool had returning users, real session time, and people coming back with different files. It was the image-to-SVG vectorizer.

So I deleted the other 64.

Here's what I learned rebuilding around the one thing that worked.

Grouping traced paths by exact hex is useless on real images

The vectorizer has a colour/layer editor — pick a colour, hide it or recolour it. The obvious implementation groups SVG paths by their exact fill value.

That works beautifully on the synthetic test logo I built it against. Then a user sent me a real file: a gold emblem, photographed.

The trace produced 3,428 paths across 3,122 distinct hex values. Almost every path had its own colour. The palette showed the top 24 by frequency, which together covered 83 paths — 2% of the image. Clicking a swatch did nothing you could see. The feature was technically working and practically broken.

The fix was to stop treating colours as identities and start treating them as positions in a space. Seed from the heaviest colours, reject seeds that are too close to an existing one, then assign every colour to its nearest seed using a weighted-RGB distance:

function dist(a, b) {
  const rm = (a[0] + b[0]) / 2;
  const dr = a[0] - b[0], dg = a[1] - b[1], db = a[2] - b[2];
  return Math.sqrt(
    (2 + rm / 256) * dr * dr +
    4 * dg * dg +
    (2 + (255 - rm) / 256) * db * db
  );
}
Enter fullscreen mode Exit fullscreen mode

That weighting is a cheap approximation of human colour perception — green gets the heaviest coefficient because we discriminate green far better than blue.

Result: 3,122 colours collapsed to 18 layers covering 100% of paths. Hiding the top layer now removes 610 paths and you watch the background disappear.

One detail that mattered more than expected: when recolouring, don't flatten the layer to a single value. Store each path's original colour, compute its offset from the cluster centre, and re-apply that offset to the new colour. Otherwise every gradient and shadow in that layer turns into a flat blob.

I shipped a 26MB WASM model and had to rip it out

Small inputs trace badly, so I added an ONNX upscaling model to pre-enlarge them. Locally it was great.

In production, the WASM binary ran 13–26MB depending on the build, and on some machines its compilation — not inference, compilation — locked the main thread for minutes. On my machine, and in a clean browser profile, it was fine. That's the worst kind of bug: it only exists for other people.

I removed it entirely. The site went from 18MB to 1.4MB, and the quality difference on real inputs was smaller than I'd told myself it was.

If you're shipping a model to the browser, budget for compile time on cold, weak, extension-loaded machines — not just inference time on yours.

The architecture that made deleting 64 tools worth it

Everything runs client-side. VTracer (Rust) compiled to WebAssembly, executed in a Web Worker with OffscreenCanvas so the UI stays responsive during a multi-second trace.

That's not a purity thing, it's what makes the product viable:

  • No upload — files never leave the machine, which matters when the file is a client's unreleased logo
  • No rate limit — there's no server cost to ration, so there's no reason to cap anyone
  • Works offline after the page loads
  • No account, because there's nothing to store

One parameter tuning note, since it cost me real time to measure: colorPrecision at 8 produced 3,590 paths / 1,577KB. At 6 it produced 2,856 paths / 1,139KB — 28% smaller files and 32% faster, with output I genuinely could not tell apart on logos. Default it lower than you think.

The actual lesson

Shipping 65 things felt like 65 chances to win. It was really one diluted product with 65 half-maintained surfaces, no clear reason to return, and nothing anyone would describe to a friend.

The surviving tool is here if you want to break it: https://12pixa.com

Thin script fonts and scanned line drawings are where tracers fall apart — if you find a file it handles badly, I'd genuinely rather hear that than a compliment.

Top comments (0)