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:
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.
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.
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);
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)