Projects

localViewer — a file viewer that never uploads your files

The position: a file viewer has no business seeing your file. Almost every one of them does, because uploading is the easy way to build one.

Search for a way to look at an STL, a 3MF from a slicer project, or a STEP file from a supplier, and you get a page that asks you to upload it. That is a strange trade for something you only want to look at. The file goes to a machine you do not control, under a retention policy you did not read, and you get a picture back. For a hobby model that is merely untidy. For a supplier's STEP file under NDA, or a client's CSV, it is the kind of thing you have to explain afterwards.

So the constraint for localViewer was fixed before anything else: the file content makes no network requests. Not "encrypted in transit", not "deleted after an hour" — no request. That is a much stronger promise than a privacy policy, because it is structural. There is no server to trust, so trusting me is not part of it.

What the constraint costs

Everything gets harder, and each format got harder in its own way.

The parsers have to come to the file. Normally the heavy lifting sits on a server: a STEP importer, an OpenSCAD compiler, a mesh parser. Local-first means all of that has to run in the browser, which means shipping it to the browser. The naive version of this is a first paint that takes ten seconds because you loaded a CAD kernel to display a Markdown file. So the heavy dependencies — the STEP importer and the OpenSCAD compiler — load lazily, on first use of the format that needs them, and nothing else pays for them.

There is no build step. The whole app is static files with framework-free logic, tested with Vitest and jsdom. That was a deliberate choice, and it is the one people argue with: no bundler means no tree-shaking, no automatic code splitting, and you do the module boundaries by hand. It buys something specific in exchange — the thing that ships is the thing you wrote, it can be served from any static host including a folder on a disk, and there is no build to rot. For a tool whose entire pitch is "nothing between you and the file", a build pipeline was one more thing between you and the file.

Writing back is the hard direction. Reading a file the user drops on a page has been possible for years. Editing the Markdown and saving it back to the original file on disk is what the File System Access API is for, and it is why the Markdown editor works as an editor rather than a viewer with a download button. Where that API is not available, the same action degrades to a Save-As dialog. The feature is the same; the fidelity is not.

Offline had to be real. After the first visit the app registers as a PWA and caches the app shell and its libraries, so subsequent visits work with no network at all. This is the part that makes the privacy claim self-evidencing rather than asserted: you can watch it work with the network off. That is also the honest test of local-first — if it needs the network, it was never local.

The rule, and its boundary

The rule I take from this: when the trust model is the product, put it in the architecture, not in the copy. A privacy page is a promise. A page that makes no requests is a fact, and anyone can check it in devtools in about four seconds.

That only works when the whole job genuinely fits on the device. It fits here because viewing a file is bounded work on bounded input — one file, one machine, one person looking at it.

Where this breaks

The moment the work stops fitting on the device, the rule stops applying and you should not pretend otherwise.

If your file is genuinely too big for the machine in front of you, you want a different architecture, and it will have a server in it. That is a fine answer. It just is not this one.

Details

Formats
Markdown (with RTL/LTR bidirectional text), YAML as a collapsible tree, CSV with delimiter auto-detection, STL, 3MF including multi-part production-extension files from Bambu Studio / OrcaSlicer / PrusaSlicer, STEP, OpenSCAD, images
Runs as
Static site; installable PWA on Windows and Android; optional Windows Explorer "Open with" via a PowerShell helper
Tests
Vitest + jsdom
Licence
Apache-2.0

Launch localViewer → · Source →