PDF & Image Tools

What nobody tells you about building a product entirely in the browser

By Swathik··9 min read
web-developmentarchitectureindie
An open laptop on a sunlit wooden desk showing a code editor with a project's source-file tree, a cozy blurred workshop in the background

I shipped 64 tools, an AI model, and a privacy promise without ever provisioning a server. Here's the part the "no backend" hype leaves out.

A while back I made a call that sounded either brilliant or stupid, and I honestly couldn't tell which. I'd build my entire product in the browser. No backend. Not "thin backend." Not "backend later." None. Every PDF merge, every image compress, every run of an AI background-removal model would happen on the user's own machine, inside the tab, and the files would never go anywhere.

The pitch to myself was tidy. Privacy-first tools, files never leave your device, provable by opening the Network tab and watching nothing upload. The wedge writes itself: the big names like Smallpdf and iLovePDF process your files on their servers, and I just… don't.

What I didn't appreciate at the time is that "no backend" isn't a feature you bolt on. It's a constraint that reaches into every decision you make afterward. And like all good constraints, it pays you back generously in some places and quietly mugs you in others. Sixty-four tools later, here's the honest ledger.

The wins are bigger than I expected, and boring, which is the point

Let me start with what genuinely surprised me. The surprise wasn't that browser-only worked. It was how much disappears.

There's no database, so there are no database problems. I've never written a migration for this product. Never had a schema drift between staging and prod. Never had a Sunday wrecked by a long-running ALTER TABLE that locked a table and took the app down with it. That whole category of pain, the one that's eaten actual weeks of my life on other projects, just doesn't exist here. The user's files live in the user's RAM and then they're gone. My "data layer" is the browser's memory and the occasional Blob.

There's nothing to scale. This one still feels slightly illicit. When someone new shows up to compress an image, their laptop does the compressing. A thousand people at once is a thousand laptops doing the work, not one of my boxes melting. My hosting is static files on a CDN. My "scaling strategy" is that the CDN is good at serving files, which it is, because that is the entire job of a CDN. I have never been paged. I have never watched a graph spike and felt my stomach go.

There's nothing to monitor, because there's nothing to watch fail. No request queue. No p99 latency. No "the worker pool is saturated." The compute is out at the edge of the world, on devices I'll never see.

And there's a kind of moral clarity to it. When your architecture makes it physically impossible to see a user's files, you don't have to write a privacy policy that quietly papers over what you actually do. You can't leak what you never received. That's not marketing, it's just the shape of the system, and I find it genuinely calming. The most secure server is the one that doesn't exist.

If the story ended there, I'd be writing a triumphant "just move everything to the client" manifesto. It does not end there.

The hidden costs nobody puts in the "no backend" blog posts

1. The first load is a different sport

The flagship tool is an AI background remover, and the model runs in the browser. That sentence is doing a lot of quiet work. A neural network for image segmentation is not small. The very first time someone uses it, the model has to come down the wire and into their machine before anything can happen at all.

On a server, a model just sits there warm, loaded into memory once, ready. In the browser, every new visitor is a cold start, and the cold start is a download measured in tens of megabytes. Cached after that, sure. Works offline forever after, lovely. But that first impression is a progress bar, and a progress bar is exactly where a meaningful chunk of people decide whether they trust you.

You don't get to pre-warm a stranger's laptop. So you learn to obsess over the first ten seconds in a way backend devs never have to, because their cold start happens once, on their own hardware, where nobody's watching.

2. Safari has opinions, and they are not your opinions

Here's a thing I did not expect to become an expert in: cross-origin isolation headers, and which browsers honor which value of which header, on which device.

To run a multi-threaded WASM model you need SharedArrayBuffer, and to get SharedArrayBuffer the page has to be "cross-origin isolated," which means setting a specific pair of headers (Cross-Origin-Opener-Policy and Cross-Origin-Embedder-Policy). Simple enough on paper. In practice I ended up shipping logic that sets those headers conditionally, based on the user-agent, because the exact configuration that boots the model on a desktop is the exact configuration that refuses to boot it on a phone.

My actual production code, lightly paraphrased:

const ua = request.headers.get('user-agent') ?? '';
const isPhone = /iPhone|iPod|Android/i.test(ua);
if (!isPhone) {
  // desktop + iPad get isolation ON (desktop needs it for threaded WASM / WebGPU)…
  response.headers.set('Cross-Origin-Opener-Policy', 'same-origin');
  response.headers.set('Cross-Origin-Embedder-Policy', 'credentialless');
}
// …phones run a single-thread engine with isolation OFF,
// because that is the ONLY config that actually boots on-device.

There's also a comment in there reminding me that the iPad reports a "Macintosh" user-agent and therefore quietly takes the desktop header path, even though its model runs on the lean single-thread engine. That's the kind of detail you find out about over an afternoon and a slowly forming confused face.

I want to be clear that none of this is me being clever. This is me losing a fight gracefully. The browser is not one platform. It's a dozen platforms wearing a trench coat, and on the client you meet every one of them. A backend dev picks an OS and a runtime and lives there in peace. I get whatever the user happens to own, four-year-old phone with the aggressive memory limit included.

3. Debugging in production, where you have no eyes

This one is the real tax, and it's the direct cost of the thing I bragged about earlier.

I have no server logs. By design. When a user on some particular Android phone hits a bug where the model spits out an empty mask, I cannot tail a log. I cannot reproduce it on my machine, because my machine isn't their machine, and the bug is the difference between the two machines. Half the time I don't even know it happened unless they tell me.

You trade observability for privacy, and you don't fully feel that trade until something breaks for one person on hardware you'll never hold. The most painful bugs I've shipped were memory bugs on phones, the kind where the tab just silently dies. No error. No stack trace. Nothing. The operating system reaped the tab to save itself, and from my side it looks like the user simply… left. An out-of-memory kill is not a JavaScript exception you can catch. It's a white screen and a shrug.

The closest thing I have to a debugger is owning a few real devices and a lot of patience. I keep an old iPhone around for the express purpose of reproducing the bad cases, because the simulator lies about memory. Lies cheerfully, and to your face.

4. The user's worst device is your minimum spec

On a backend, if a job's too heavy, you throw a bigger instance at it. On the client, "throw bigger hardware at it" translates to asking the user to go buy a new phone, which is not a feature I can ship.

So the budget is brutal, and it's set by the weakest device in your audience. Batch limits, image dimensions, how many PDFs someone can merge in one go, all of it is bounded not by what's reasonable but by what won't OOM a budget Android handset I can't even reliably detect. I'm upfront about this in the product, because pretending otherwise just buys you a worse white-screen experience down the line. Some of the heavy operations are desktop-only on purpose. I'd rather say so plainly than promise the world and crash on a $120 phone.

So would I do it again? Yes. And here's the career part nobody mentions.

Here's the bit I actually sat down to write.

The constraint forced me to learn things most application developers get to skip their entire careers. I understand WASM threading and SharedArrayBuffer now, not because they were trendy but because my product wouldn't boot without them. I understand how an ONNX model gets quantized and routed to WebGPU versus WASM. I've read more of the Canvas and File API specs than is strictly healthy. I know, in my gut, what a memory budget feels like, because I've watched a tab die for going one image over it.

That's real career capital. A lot of web work is gluing a frontend to an API someone else built, and it's good honest work, but you can do it for years and never once touch the metal. Building with no backend pushed all the hard problems onto the client, which meant I had to actually solve them there. There was no API to hide behind.

So the honest summary is this. You skip the server, and with it you skip a solid 90% of the infrastructure problems that usually run the show: migrations, scaling, monitoring, the 3am page. In exchange you inherit a smaller, weirder, lonelier, more interesting set of problems. First-load download as a UX problem. Every browser as its own separate platform. Debugging by mailing yourself an old phone. A performance floor set by a device you will never personally hold.

If you're a technical founder eyeing this constraint, don't read it as a shortcut. It's a different road that happens to skip the most famous traffic jam. You trade the database for the device, the server log for the silence, and the scaling bill for a low-grade obsession with someone's three-year-old phone.

Worth it. Nobody warns you about the phone, though.

I build pdfandimagetools.com, a set of privacy-first PDF and image tools that run entirely in your browser. If you want to verify the "files never leave your device" claim, open your Network tab and process a file. That's the most convincing thing I could ever say.

Every tool on PDF & Image Tools runs entirely in your browser. Your files never leave your device.

← All posts