Skip to main content

I Built a PDF Toolkit in a Weekend with Go and Vanilla JS (No Frameworks)

Compress, merge, convert — no npm, no React, no build step. Here’s how three of the tools actually work under the hood.

Every “free online PDF” site wants your email, throws a wall of ads at you, or quietly keeps your files. I wanted the opposite: drop a file, get a result, and have the thing forget you were ever there. So over a weekend I built pdfly — a browser-based PDF toolkit — with two rules: no JavaScript framework on the front end, and a single static binary on the back end.

Here’s what that looks like in practice, and how three of the tools are built.

The stack, and why it’s boring on purpose

The front end is plain HTML, CSS, and vanilla JavaScript. No React, no Vue, no bundler, no node_modules folder the size of a small moon. The whole UI is a handful of files the browser loads directly. Page loads are instant because there’s nothing to hydrate, and there’s no build step to babysit — I edit a file, refresh, done.

The back end is a single Go binary. Go compiles to one self-contained executable, which means “deploy” is really just “copy a file and restart a service.” No container runtime, no interpreter to install on the server. It runs behind Caddy on a tiny cloud VM, and that’s the entire production setup.

Now the tools.

Tool 1 — Compress (shelling out to Ghostscript)

Shrinking a PDF well is genuinely hard: you’re re-sampling images, subsetting fonts, and rewriting the document structure. Rather than reinvent that, pdfly leans on Ghostscript, the battle-tested PDF engine that’s been doing this for decades.

The tool exposes four quality presets — screen (72dpi), ebook (150dpi), printer (300dpi), and prepress — which map straight to Ghostscript’s -dPDFSETTINGS. The Go handler saves your upload to a temp directory, runs Ghostscript against it, streams back the smaller file, and deletes the temp directory. That defer os.RemoveAll(work) at the top of every handler is the whole privacy story: the work folder is gone the moment the request finishes.

Tool 2 — Merge (pure Go, zero external dependencies)

For combining PDFs I used pdfcpu, a PDF library written entirely in Go. That matters: merge, split, extract, encrypt, and decrypt all run inside the binary with no external program to shell out to. No Ghostscript, no system dependency — just Go calling Go.

The handler enforces one rule (“select at least two PDFs”), preserves the exact order you dragged the files into, and writes the merged result. Because the browser lets you reorder the list before uploading, the front end and back end agree on order without any clever syncing — the upload order is the merge order.

There’s a nice fallback story here too. Some PDFs in the wild are subtly malformed — a structure that strict parsers reject. When pdfcpu balks, pdfly quietly rebuilds a clean copy through Ghostscript and retries, so a slightly-broken file still works instead of throwing an error in your face.

Tool 3 — PDF → Word (knowing when to leave your language)

This is the one that broke my “single binary” purity, and I think that’s the honest part of the story. Converting a PDF back into an editable Word document — reconstructing paragraphs, tables, and layout — is a genuinely nasty problem, and the best open-source tool for it lives in the Python world (built on PyMuPDF). Go simply doesn’t have an equivalent that’s as good.

So pdfly shells out to a small Python helper for this one tool. The tradeoff: it’s memory-hungry, and the production VM is small, so the handler rejects anything over 30 MB up front rather than letting a giant file take the server down. It also only works on text-based PDFs, not scans — there’s no OCR pretending a photo is a document.

The lesson: use the right tool for the job even when it dents your architecture. One Python dependency for the one feature that truly needs it beats a worse pure-Go conversion.

What “we don’t keep your files” actually means

Every handler follows the same shape: save the upload to a temporary folder, do the work, stream the result, and defer the deletion of that folder. A background sweeper also clears out any editor sessions that were abandoned. There’s no database of your documents because there’s no database at all. The only thing that persists is a single integer — a counter of how many files have been processed — which powers the little stats number on the homepage.

Was a weekend enough?

For a working v1, yes — because I chose boring, proven pieces and let them do the heavy lifting. Ghostscript compresses, pdfcpu handles the pure-PDF operations, Python covers the one job Go can’t, and the front end stays a pile of static files anyone could read in an afternoon. No framework churn, no build pipeline, no server babysitting.

The best part of building small like this: six months from now, it’ll still just be a binary and a folder of HTML. Nothing to upgrade, nothing to break.

pdfly is free and needs no signup — pdfly.work.

Comments

Popular posts from this blog

How I used Google Sheets and Apps Script

Google Sheet is one of the most powerful spreadsheet application that exists online, rivaling with Microsoft's Excel. One of the main strengths is its strong support for collaboration with other users, much easier and popular than collaboration tools with Microsoft Office. Aside from plain spreadsheet, it also supports extensions such as macro. If you are familiar with macros on other office tools, they work almost the same. However, the most extension I use and tinker with is the Apps Scipt . Apps Script Extension One of the challenges I faced recently is how do I track or monitor reports in our department if they are submitted on time or worst, forgotten due to lack of better monitoring tools. So I thought if there can be simple applications that can be deployed or use by a more general user to allow reminding periodically what reports are approaching due dates or those that are past dues. Then I looked for a way, instead of creating a full blown app from scratc...

Sluicegate Tutorial with FlowStudio

This walkthrough shows how to use FlowStudio ’s sluice gate (rectangular channel) worksheet: upstream pool depth from specific energy, downstream gradually varied flow, and—when the case allows— hydraulic jump placement plus an empirical jump length (SI units). Open FlowStudio → https://flow.syncster.dev What you are solving A bottom sluice in a wide rectangular channel passes a discharge Q . The worksheet assumes a contracted depth at the vena contracta, y 2 = C c a , where a is gate opening and C c is a contraction coefficient (often near 0.6–0.65). From specific energy matching between the upstream pool and the contracta—together with a check against uniform normal depth y n for the approach channel—the sheet finds upstream pool depth y 1 . Downstream, it integrates Manning-based gradually varied flow from the gate. If the contracta is supercritical and you set a subcritical tailwater y t (or...

Automate Sending Email with Apps Script and Google Sheet

Introduction It has been too long that many people uses Microsoft Excel in day-to-day computing tasks. It's so big that it almost resemble a programming language where non-technical people can create their own spreadsheet programs. It has many uses with just the default grid-type data entries. But Microsoft Office developers did not stopped there. They gave it more power by adding a scripting capability to it with VBA or Visual Basic for Applications. Most of the office apps of Microsoft has this VBA at their disposal but I most used it with Microsoft Excel. It was the most appropriate application for me to use it. But then come the big competition. I'll skip the open source apps that may compete with Microsoft Office and go directly with the big one. This is the Google Sheet from Google. Introducing Google Sheet Google Sheets is an online spreadsheet application that allows users to create, edit, and format spreadsheets to organize and analyze information....