DEV Community

swift king
swift king

Posted on

Why I Stopped Using Cloud Converters and Built My Own

After a client asked me to convert 200 product photos from WebP to PNG, I uploaded the first batch to a popular online converter. It took 45 minutes, and halfway through I realized I was sending proprietary product images to a server I knew nothing about.

That was the moment I decided to build something that ran locally.

The Privacy Problem Nobody Talks About

Most free online converters upload your files to a server for processing. Their privacy policies often include broad licenses to "process and analyze" uploaded content. For client work, this is a non-starter.

I built webp2png.io as a browser-local alternative. Files stay in memory, processed via the Canvas API. Open DevTools Network tab while using it — you won't see a single upload request.

What I Learned About Canvas Performance

Processing 200 images taught me a few things:

  1. OffscreenCanvas is worth it. Moving rendering off the main thread kept the UI responsive during batch operations. The difference was dramatic — 200 images processed in under 10 seconds vs. 45+ minutes with upload-based tools.

  2. Memory management matters. Each canvas context holds a decoded image in memory. For batch processing, I had to explicitly revoke object URLs and let garbage collection do its job. Otherwise Chrome would crash around image #150.

  3. The WebP format is quirky. Chrome supports it natively, but Firefox took years to add full support. I had to add fallback handling for Safari versions that don't handle animated WebP correctly.

I later applied these same patterns to an SVG converter at svg2png.org and learned that SVG rendering brings its own set of headaches — crossOrigin issues, foreignObject limitations, and font embedding problems that Canvas simply can't solve.

The Code

The core pattern is surprisingly simple:

const img = new Image();
img.onload = () => {
  const canvas = new OffscreenCanvas(img.width, img.height);
  const ctx = canvas.getContext('2d');
  ctx.drawImage(img, 0, 0);
  canvas.convertToBlob({type: 'image/png'}).then(blob => {
    const url = URL.createObjectURL(blob);
    download(url, 'converted.png');
  });
};
img.src = URL.createObjectURL(file);
Enter fullscreen mode Exit fullscreen mode

If you work with client data or proprietary files, stop uploading them to random servers. Browser-local processing works today. It's faster, private, and puts you in control.

Top comments (0)