GET /blog

You might not need an app: how far the browser reaches on a phone

Abstract — Modern browsers already reach the camera, GPS, microphone, files and the OS share sheet directly from a web page. This is a tiny gallery of those capabilities, each a self-contained demo, made to show how far the web goes before you reach for a local-first native build. It ships as a single server-rendered Go binary and installs as a PWA — and the same page works on both iOS Safari and Android Chrome.

A client asked me to build a native app for something a web page does on its own. Rather than argue in the abstract, I built the demo: a handful of device features you'd assume need an app, each working in the browser, on a real phone, with nothing installed. It's live at wf.manulobato.com — open it on your phone.

What's in it

Each tile is one capability, kept deliberately small so the mechanism is the whole point:

  • Media capture — camera, gallery and video uploads driven purely by a file input's accept, capture and multiple attributes.
  • GPSgetCurrentPosition() for latitude, longitude and accuracy, posted back to the server.
  • Phonetel:, sms: and mailto: links that open the native dialer, messages and mail apps.
  • Web Sharenavigator.share() opening the real OS share sheet, including sharing a server-generated file.
  • Audio recordinggetUserMedia + MediaRecorder, uploaded as FormData.
  • File download, screen orientation, and a connectivity monitor that combines the browser's online/offline events with a server heartbeat.

How it's built

The whole thing is one Go binary: server-rendered HTML with templ, a classless/semantic stylesheet (Oat) plus a thin project shim, Lucide icons inlined as static SVG (no runtime JS, no layout shift), and Datastar for the live bits — hot reload and toasts over a single SSE connection. It's a PWA (manifest + service worker), supports light/dark, and animates page changes with cross-document view transitions.

No JS build step, no framework, no app store review. It cross-compiles to a stripped static Linux binary and deploys by copying that one file to a VPS behind Caddy.

The point isn't that you should never build native — it's that "we need an app" deserves a second look. A lot of the time, the browser already did it.

GET /blog ← back to all posts

Disagree with a post? Tell me. POST /contact → best conversations start with a code review

POST /contact →