The reader app — go-reddit/reader¶
A native macOS Reddit reader whose entire UI is drawn by
go-widgets — compiled to WebAssembly,
blitted into a <canvas>, and hosted in a WKWebView that is opened from Go
with zero cgo (CGO_ENABLED=0). Module path github.com/go-reddit/reader.
Architecture¶
┌─ "Reddit Reader.app" (pure Go, CGO=0, self-contained) ───────────────┐
│ │
│ internal/ui go-widgets toolkit renders the feed → RGBA buffer │
│ cmd/front (wasm) blits that buffer into a browser <canvas> │
│ internal/server serves the wasm + proxies /api → Reddit │
│ internal/webview opens a WKWebView via purego (no cgo) │
│ │
│ Transport (default): a WKURLSchemeHandler serves every request │
│ in-process — no TCP, no unix socket, no listening port at all. │
│ │
│ fetch reader://app/api/feed ──► go-reddit/reddit ──► reddit.com │
└────────────────────────────────────────────────────────────────────────┘
The layout mirrors the Reddit web UI: a topbar with sort tabs (hot/new/top/rising) and the current feed, a sidebar of bookmarked subreddits, and the scrollable feed of post cards.
Three choices worth calling out:
- go-widgets, not HTML. The cards, score badges, sidebar and topbar are painted by the go-widgets painter into an RGBA buffer, exactly as in a native window — the WebView is just a surface. Text is anti-aliased TrueType rendered at device resolution, so it stays crisp at any zoom or on a Retina display.
- CGO=0 everywhere. The WKWebView, NSWindow and NSApplication are driven
through the Objective-C runtime via
purego. The shipped binary links only
system libraries; AppKit/WebKit are
dlopened at runtime. - No socket. Instead of a loopback HTTP server, the default transport
registers a private
reader://URL scheme and answers every request in-process from the samehttp.Handler. Nothing on the machine can reach the app's content, and no port is consumed. Pass-httpto fall back to a127.0.0.1server (used off macOS and for automated testing).
Running it¶
task run # == go run . (front page)
go run . -sr golang -sort top # start on a specific feed
task app # build dist/Reddit Reader.app
task demo # built-in sample feeds, no network
For live Reddit from a datacenter IP, set OAuth credentials — see Authentication.
Flags & environment¶
| Flag | Env | Meaning |
|---|---|---|
-sr |
READER_SUBREDDIT |
subreddit (empty = front page) |
-sort |
READER_SORT |
hot/new/top/rising/controversial/best |
-demo |
serve a built-in sample feed, no network | |
-http |
use a loopback TCP server instead of the URL-scheme transport | |
-no-window |
open the default browser instead of a native window | |
-serve-only |
run headless over TCP and print the URL | |
READER_OAUTH_CLIENT_ID / _SECRET |
app-only OAuth | |
READER_OAUTH_USERNAME / _PASSWORD |
script-grant OAuth |
Build layout¶
internal/ui— the go-widgets scene: layout, hit-testing, rendering. Pure, native-testable (100% coverage), no build tag.cmd/front— theGOOS=js GOARCH=wasmentry point (syscall/js glue).internal/server— static bundle +/apiproxy (100% coverage).internal/webview— the purego WKWebView +WKURLSchemeHandlerbridge.
The wasm bundle is a build artifact; scripts/build-wasm.sh (run by every
task target) produces internal/server/assets/reader.wasm.