How I Built a Local Image Converter Using Go and bimg


From Scratch to Speed: How I Built a Local Image Converter Using Go and bimg

There’s a special kind of satisfaction in building a tool that solves your own problem. This one started with a simple annoyance: I kept converting and compressing images for different projects — manually — over and over again. Different tools, different formats, different quirks. Some were online (slow and sketchy), some were local but bloated.

So I thought: What if I just build my own converter? One command to launch, local, fast, and private.

Step 1: Why Go?

Go is a strange and wonderful language. It’s fast, statically compiled, and feels like someone designed C for people who enjoy sleeping. I’ve used it for backend services before, but this time, I wanted to see how well it handled something a little different — image processing.

I didn’t want dependencies that drag half the internet with them (looking at you, ImageMagick). Enter bimg, a Go wrapper around libvips, one of the fastest image processing libraries on earth.

The magic combo looked perfect:

  • Go → easy concurrency, quick builds, no runtime.
  • bimg → blazing image operations, small memory footprint.
  • libvips → the quiet powerhouse behind many production image systems.

Step 2: The Mission

I wanted my tool to handle everything I needed:

  • Convert between formats (HEIC, JPG, PNG, WEBP)
  • Compress large images
  • Handle video compression (because why stop at images?)
  • Run locally — one executable, one browser tab.

And I wanted the developer experience to be stupidly simple: ./imgtool opens a minimal web UI in Brave, ready to drag and drop.

Step 3: The Go Backend

The backend was surprisingly short. Go’s standard library gives you a web server right out of the box. Here’s the essence of it:

http.HandleFunc("/", serveUI)
http.HandleFunc("/upload", handleUpload)
log.Fatal(http.ListenAndServe(":2003", nil))

The /upload route reads the file, runs it through bimg, and spits back a new image in the format I pick. When you hit “Convert,” it’s not uploading anywhere — it just processes locally and sends you the file. Fast. Private. Simple.

Step 4: The UI That Doesn’t Try Too Hard

I kept the frontend minimal — pure HTML. No React, no Tailwind, no build step. Just a clean form:

<form action="/upload" method="POST" enctype="multipart/form-data">
  <input type="file" name="file" required>
  <select name="format">
    <option value="png">PNG</option>
    <option value="jpg">JPG</option>
    <option value="webp">WEBP</option>
    <option value="compress">Compress</option>
  </select>
  <button type="submit">Convert</button>
</form>

That’s all it needs. The point was to make the backend the star — a tiny Go service that feels instant.

Step 5: Compression That Actually Works

Here’s where I hit a snag: PNG didn’t want to compress. Turns out, PNG is lossless, so “quality” doesn’t mean what it does for JPEG or WebP. Instead, I added a condition — PNGs get zlib compression, while others use a quality-based reduction via bimg.

options := bimg.Options{
    Quality: 60,
    Type: bimg.WEBP,
}

The results were wild. A 12 MB JPG shrunk to under 3 MB with barely any visual loss. That’s when I realized libvips wasn’t kidding around.

Step 6: Adding Video (Because Why Not)

I slipped in ffmpeg for video compression. Go can just exec.Command("ffmpeg", …) and handle the rest. Suddenly, the tool could handle both image and video workloads from the same interface.

Step 7: The Final Touch

When you run the app, it automatically opens a Brave browser tab:

exec.Command("brave-browser", "--new-tab", "http://localhost:2003").Start()

No setup. No Docker run. Just:

./imgtool

and you’re live.

What I Learned

  • Local tools still matter. The cloud is great, but local tools are instant and private.
  • Go + bimg = fast, elegant utility dev. You can write serious tools with surprisingly little code.
  • Building for yourself teaches better than any tutorial. You hit real problems, and solving them builds muscle memory.

The Future of ImgTool

I’m planning to add:

  • Drag-and-drop multi-file support
  • Adjustable compression levels
  • Instant previews before download

But even now, it’s a one-binary tool that does the job faster than anything I’ve used before.

If you’re a developer who wants to stop context-switching between bloated tools, build your own. One tiny utility can save you hours — and teach you more than a dozen tutorials combined.