Fork me on GitHub

go-webengine

A pure-Go, headless web engine โ€” give it a URL, get back an image of the page. HTML/CSS layout and paint with no Chromium and no cgo.

no Chromium ยท no host web viewJavaScript (goja) + real DOMflex ยท grid ยท tables ยท positiongradients ยท box-shadow ยท SVGratchet coverage gatemeasured vs headless Chrome
zsh
$ go get github.com/go-webengine/engine@latest
$ go get github.com/go-webengine/esbuildsandbox@latest
$ go get github.com/go-webengine/browserproxy@latest
$ go get github.com/go-webengine/webtest@latest
One organisation, 4 repositories

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.831 SSIM, ~18× faster on the latest run), JS-heavy and large computed pages are the honest frontier (mean SSIM ≈ 0.66). 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

engine lib + cli

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.

CI coverage gate

esbuildsandbox lib

One function, against one leak

A safe <code>ResolveDir</code> for esbuild's <code>pkg/api</code>. A caller bundling untrusted source installs an <code>OnResolve</code>/<code>OnLoad</code> plugin and it looks complete โ€” every import in its own tests goes through it. But the bundler expands a <strong>glob dynamic import</strong>, <code>import(`./${lang}/index.js`)</code>, by walking <code>ResolveDir</code> on the <strong>real filesystem</strong> through its internal resolver, bypassing the plugin entirely. Whatever that directory is โ€” <code>"/"</code> is a common default โ€” is what gets walked, following symlinks. This returns a directory where that walk finds nothing.

browserproxy 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.

CI coverage gate

webtest cli

Browser-automation CLI

An HCL-driven browser-automation CLI on the engine: test scenarios that click, fill forms and assert, with no Chromium.

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 (sized and tiled with background-size), variable-font weights, the font shorthand, sub-pixel text positioning, bold and italic monospace, 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 (icon fonts, the rest of the content grammar, conic-gradient/filter/mask, SVG filters) and the perf gap on large computed pages. List markers, ::marker and CSS counters shipped in v0.18.0. 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.