DEV Community

Lank_M
Lank_M

Posted on

Why I built another image tool when Photoshop already exists

Image editing is a solved problem. Photoshop exists. So does every AI background-removal site you can find in one search. I assumed the same — until I watched a colleague actually do the work.

Her task was mundane: take a batch of product photos, cut out the background, swap in a white one, compress everything under the marketplace size limit. She'd tried both routes.

Photoshop was installed. But launching it to cut out a dozen images meant waiting through startup before doing anything. She isn't a designer — she uses maybe 5% of what's in there.

Online AI tools meant upload, queue, wait. Thirty-plus seconds per image, worse at peak hours. And if the edge came out wrong, she'd do the whole round again.

What she said, roughly: I just want it done. I don't want to install something for this, and I don't want to sit here waiting.

No technical vocabulary in that sentence, but it names the problem precisely. Existing options are either too heavy or too slow — and neither of those is an algorithm problem.

Where the weight and the wait actually come from

The slowness first. An online AI tool's pipeline looks like this:

pick file → upload (capped by your upstream)
          → queue (capped by their load)
          → process → download → done
Enter fullscreen mode Exit fullscreen mode

Only one step does real work. Everything around it is waiting. Upstream bandwidth is typically a fraction of downstream, so pushing a few megabytes costs seconds before anything starts. Queueing is pure luck.

Now the weight. Desktop software has a fixed startup cost — the same whether you're editing 1 image or 100. Heavy users amortize it. Someone touching a dozen images occasionally just eats it.

So the idea was blunt: move the processing step into the user's own browser. No upload, no queue, no install. Just the work.

That became the single constraint the whole project runs on. Everything downstream — including the things it can't do — follows from it.

(The tool ended up being called ImgIng — 图映 in Chinese — and lives at imging.cn. I'll use the name below when talking about specific implementation details.)

First consequence: you have to measure capability, not look it up

Putting processing local means finding out what the browser can actually do. This is where I got burned.

Asking canvas for WebP looks like this:

canvas.toBlob(blob => {
  download(blob, 'output.webp');
}, 'image/webp');
Enter fullscreen mode Exit fullscreen mode

Callback fires. Blob isn't null. Size looks sane. Everything succeeds — except it may not be WebP at all.

The HTML spec is explicit: if the user agent doesn't support the requested type, it must create the file using PNG instead. Silently. No error, no warning. I found out when a user reported a file wouldn't open — hex dump started with 89 50 4E 47.

Could you sniff the UA instead? No. Every iOS browser is WebKit underneath. In-app WebViews track the system version independently. Users can flip on "Request Desktop Website" and hand you a macOS UA from an iPhone. The UA answers who are you; I need can you encode this right now. Engine version, OS version, host app, build flags all sit between those two questions.

Worse: canvas encoding has no capability query at all. Media has MediaRecorder.isTypeSupported(). Images have nothing.

So you measure. Encode once, then check what came back:

const blob = await new Promise(r => canvas.toBlob(r, mime));
const ok = !!blob && blob.type === mime;   // the second half is the point
Enter fullscreen mode Exit fullscreen mode

Six formats on desktop Chrome: WebP and JPEG encode fine. AVIF, HEIC, TIFF and GIF all silently fall back — and all four blobs come back byte-identical in size, because they're the same PNG. Check only for null and all six "succeed."

Format support matrix

Those three chip styles in ImgIng's UI come straight out of this: solid means the browser does it natively with nothing uploaded, outlined means a WASM codec downloads once and then runs locally, grey means the browser genuinely can't and it goes server-side.

Decoding needs its own probe. Being able to read a format says nothing about writing it — Safari 16 decodes AVIF but can't encode it. So the rule became: decode the input, encode the output, both must pass before anything runs locally.

Then it turned out the browser could do more than I expected

The plan was format conversion and compression. The ceiling was further out than I thought.

Compression. Reducing a PNG to 256 colors means finding the nearest palette entry for every pixel — 1.6M pixels × 256 candidates = 410M distance calculations. Brute force measured 958 ms. Unusable.

The textbook fix is a quantized lookup table. That got me to 12 ms — 77× faster. Then I checked correctness: 14.57% of pixels picked the wrong color, because the table is an approximation.

Then I counted the actual colors in that image: 1.6M pixels, only 183K unique. Eighty-nine percent of the work was recomputing the same answers. Caching on the full RGB value instead: 153 ms, zero error. Six times faster than brute force and exact.

The problem was never that I needed a smarter data structure. I hadn't noticed the redundancy.

Conversion result

ImgIng classifies the image and picks a quality preset on its own — this sample got tagged as a photo, WebP at quality 75, and came out at 211.5 KB from 3.19 MB. The line underneath tells you which encoder actually ran.

Animated formats were an accident. GIF, APNG, animated WebP and animated AVIF encoders all ended up hand-written, plus frame-level editing — reorder, retime, delete. Re-encoding an existing animation preserves per-frame duration and loop behavior, so the rhythm survives.

AI background removal is the one I expected to fail. Running models in a browser sounded like a toy. It isn't: models download on demand (42 MB for the fast tier, 219 MB and 447 MB above it), inference prefers WebGPU and falls back to WASM. Once cached, cutouts are seconds.

That's the direct answer to the "too slow" complaint. No upload, no queue, and after the first run, no download either.

Capability overview

Multi-page design, PDF compression and PDF-to-HTML came later. Every addition passed the same test: can this finish locally?

Where the constraint runs out

Push a constraint far enough and you hit a wall. Those walls are results too, and hiding them is worse than naming them.

HEIC reads but doesn't write. Reading goes through libheif compiled to WASM. Writing needs an HEVC encoder — patent licensing is a real minefield, and a usable WASM build is large. Shipping megabytes for an output format almost nobody requests doesn't pay. That one goes server-side, labeled in the UI.

PDF compression skips font subsetting, so text-heavy PDFs typically gain only 5–20%. Dedicated tools do better there. Scanned documents and image-heavy reports are where it wins.

PDF-to-HTML can produce a larger file. It rebuilds pages as fixed-layout SVG with selectable text, but expanding content streams into static SVG can exceed the original. The workspace reports the real size rather than hiding it.

None of this looks good in a feature list. But users hit these within two sessions, and by then you've spent the credibility of the entire tool.

Two things that fell out of it

Images never leave the device. This wasn't the goal — it's what happens when processing is local. Common-format conversion, compression and matting produce no upload requests. You can verify it yourself: open DevTools, watch the Network panel, process an image. You'll see WASM codecs and AI models coming down. Nothing going up. Opposite directions.

For a colleague working on unreleased product photos, that side effect mattered more than I expected.

No server cost. The work runs on the user's machine; I'm serving static files. A thousand conversions cost me exactly what one does. So there's no reason to meter usage, watermark output, or require signup. Upload-based tools aren't withholding a free tier out of greed — every conversion is CPU time they pay for.

So why build another one

Back to the question in the title. Image tools are mature — but what's mature is the capability, not the cost of reaching it.

Photoshop is far more capable than ImgIng; you pay for it in installation and learning curve. Online AI services may run better models; you pay in upload time and waiting. For someone processing a dozen images occasionally, neither price is worth it. The features were never the problem — the toll to reach them was.

Moving processing into the browser drops that toll to roughly zero: opening a page is the entire install, and not queueing is the entire wait. The ceiling is bounded by what browsers can do — that's the price of this choice, and I've tried to map it out above rather than pretend it isn't there.

ImgIng is at https://imging.cn — nothing to install, no signup, no usage cap.

Top comments (0)