Modern "render a web page" tooling almost always means shipping a whole
browser — Chromium via CDP, a WebView, cgo bindings. go-webengine renders in
pure Go. It fetches the page, builds an owned DOM over
x/net/html, runs a real CSS cascade
(specificity + inheritance, var(), @media, dark-mode), executes the
page's JavaScript (goja) against a real DOM, lays
out a full box model (block, inline, float, flex, grid, table, position), and
paints anti-aliased text (go-opentype),
gradients, box-shadows, images and SVG onto an image.RGBA. One static binary, no
C toolchain, identical on every OS.
Fetch (go-browserhttp) → Parse (x/net/html → dom) → Cascade (css: +var()/@media/dark-mode)
→ JavaScript (js: goja + real DOM + fetch/XHR)
→ Layout: block · inline · float · flex · grid · table · position (layout)
↻ settle loop: JS reads laid-out metrics → re-cascade + re-layout to a bounded fixpoint
→ Paint: AA text/SVG/gradients/shadows + backgrounds (go-widgets/painter) + images (go-images)
→ image.RGBA → PNG (+ optional link hit-map)It is the rendering core of the
browserproxy
remote-browser service and the wasmdesk in-desktop
browser. It is not a Chromium replacement and not claimed to match it
pixel-for-pixel: example.com renders at near-parity (0.954 SSIM, ~35×
faster), JS-heavy and large computed pages are the honest frontier (mean SSIM ≈ 0.69).
What works and what does not is documented honestly, page by page and measured against
headless Chrome, in the engine's
fidelity report
and benchmark.
Repositories
The rendering core
Fetch a URL → DOM → CSS cascade (var()/@media/dark-mode) → run the page's JavaScript → full box-model layout (flex/grid/tables/position) → paint anti-aliased text, gradients, images and SVG to an image.RGBA / PNG. Pure Go, CGO_ENABLED=0, no Chromium; ships a `render` CLI.
Remote-browser service
Renders pages server-side with the engine and streams frames (plus a click hit-map) to a thin client over a JSON WebSocket protocol, forwarding clicks/scrolls/keys back as navigation. SSRF-guarded. The server half of the wasmdesk in-desktop browser.
Roadmap
Phase 0–1 · Static render + box modelshipping
HTML parse, a real CSS cascade (specificity + inheritance), and a full box model — block/inline flow, floats, flexbox, CSS grid, tables and position — painted to an image.
Phase 1.9–2.1 · CSS breadth + SVGshipping
var() custom properties, @media, dark-mode, modern colour, gradients, background-image, border-radius, box-shadow, opacity, and pure-Go SVG rasterisation.
Phase 2–2.4 · JavaScript + dynamic rendershipping
goja bound to a real DOM with fetch()/XHR and laid-out-geometry read-back; a settle-then-render loop reflects script-driven DOM mutations, on both the plain and click-hit-map paths.
Phase 3 · browserproxyshipping
A WebSocket remote-browser service (its own repo) renders server-side with the engine and streams frames to the wasmdesk clients/browser front-end.
Phase next · Remaining leversplanned
Closing fidelity residuals (list markers, icon fonts, conic-gradient/filter/mask, SVG filters) and the perf gap on large computed pages. Diminishing returns — mean SSIM ≈ 0.69, example.com at 0.954.
Pure Go, CGO_ENABLED=0, cross-built for all six of Go's
64-bit targets. The pure-logic packages (cascade, line-breaker, box metrics, DOM) are
asserted at exact geometry behind a ratchet coverage gate; a committed golden PNG covers the
offline paint path. Prior pure-Go browser work (opossum / mycel) was studied but not built
on — its layout core is stub-quality; see the survey. BSD-3-Clause throughout.