verve · open source
Full-stack web in pure Zig.
SSR with fine-grained reactivity, per-island WASM code splitting, single-binary deploy. No VDOM. No macros. No third-party dependencies. Targets Zig 0.16.
the bet
SSR-first.
Pages render server-side as Node trees streamed straight to the socket. Search engines and noscript clients see real content. No hydration handshake required to read the page.
Real reactivity.
The same Signal / Effect / Owner / Resource graph the server uses ships into a wasm32-freestanding client runtime. DOM updates are a consequence of Signal.set — not a parallel write path tacked on for interactivity.
One binary.
No Node runtime. No build server. No bundler config. zig build produces a single executable with the WASM client, per-island chunks, JS bridge, public assets, and manifest baked in. Deploy by scp.
what's in the box
Routing + rendering
- Comptime route parser with path params, wildcards, nested layouts.
- ProtectedRoute guards + Redirect sentinel.
- useLocation, RequestMeta (cookies, Accept-Language, etc).
Reactivity (server + WASM)
- Signal / Effect / Store / Resource — SolidJS-style runtime.
- Owner tree with on_cleanup, untrack, batch.
- Keyed-list reconciler with LIS-based diff planner.
Head + components
- setTitle / metaTag / linkTag / jsonLd with priority + replace-not-append.
- provide / use DI through the owner chain.
- show / forEach / portal — control-flow helpers.
Auth + security
- CSRF HMAC-SHA256, auto-issued cookie + __csrf field.
- Per-request CSP nonce stamped onto every <script> / <style>.
- Origin pinning, SameSite=Strict on the CSRF cookie.
SSR + client
- Streaming SSR over std.http.Server, chunked transfer-encoding.
- Typed server-fn client stubs generated at build time.
- Out-of-order Suspense streaming with <template> drains.
Islands
- Per-island WASM chunks, ~73 B each via shared linear memory.
- Build-time manifest codegen walks app.islands at comptime.
- Lazy JS bridge fetches each chunk on first encounter.
Assets + i18n
- Static asset routing at /public/* — runtime or comptime-embedded.
- Hashed URLs with immutable Cache-Control headers.
- I18nCatalog + resolveLocale (cookie → query → Accept-Language).
Dev + ops
- --dev auto-reload via WS-disconnect-reconnect.
- /events SSE + /ws bidirectional WebSocket.
- /health + /metrics, per-connection worker pool, systemd socket activation.
Animation engine (verve.anim)
- Tweens, timelines with labels + position arithmetic, keyframes, 31 easings.
- Stagger, SplitText, FLIP, MotionPath, MorphSVG — all serialized at SSR, played by the bridge.
- Declarative Node.animate(...) or imperative island animPlay; prefers-reduced-motion built in.
Scroll + gesture plugins
- ScrollTrigger: toggle actions, scrub (exact or smoothed), pin with layout spacers, snap.
- Draggable: axis locks, bounds, grid snap, analytic inertia throws.
- Observer velocity tracking + ScrollSmoother that preserves native scrolling.
3D engine (verve.gl)
- Scene graph, PBR + image-based lighting, BVH ray-picking — all in Zig/wasm; JS is a dumb WebGL2 interpreter.
- Build-time asset pipeline: .glb → packed .vmesh, .hdr → prefiltered .venv.
- Declarative ctx.glScene with orbit camera, picking, auto-rotate or scroll-scrubbed gl-target tweens.
install
Three commands, one binary.
Prebuilt binaries ship for linux and macos on x86_64 and aarch64. From source it is exactly one tool — zig — and one build file.
zig version # expect 0.16.0
zig build # native server + WASM client + per-island chunks
./zig-out/bin/verve-server # http://127.0.0.1:8080Docs: github.com/sirhco/verve/tree/main/docs. Showcase example exercises every public export.
animation engine
verve.anim is a GSAP-class engine that lives in the framework: tweens, timelines, ScrollTrigger, Draggable, MorphSVG, SplitText. Each card below is built server-side in Zig and serialized into a data-anim attribute — the bridge interprets it with one transform write per frame. No island, no inline script. Check the elements panel.
01 · timeline + overlap
three tweens, position arithmetic, scroll-gated
02 · draggable + inertia
bounds, analytic throw, 40px grid snap
03 · MorphSVG
de Casteljau path morph · reducedMotion(.skip)
04 · MotionPath + scrub
The marker follows an SVG path, scrubbed to the scrollbar with 0.3s smoothing — scroll down and it advances, scroll up and it runs backward. rotate=true keeps it oriented along the tangent. One data-anim attribute, zero JS written.
05 · the site is the demo
Everything above is also just… this site. The /work filter is FLIP + MorphSVG from a wasm chunk. The headlines everywhere are SplitText. The inertia scroll on every other page is ScrollSmoother (this page opts out — the 3D turntable below needs native scroll). The home page pins, scrubs, parallaxes, and renders a PBR scene from a 5 KB procedural mesh. View source; it's all data-anim attributes and one small bridge.
3d engine
A three.js replacement, in Zig, in wasm.
verve.gl keeps the entire engine — scene graph, PBR materials, image-based lighting, BVH picking — in pure Zig running as wasm; the browser side is one dumb WebGL2 interpreter walking a binary command stream. A verve.anim timeline scrubs the model's yaw through gl-target tweens, the same engine that animates this page.
open the live scene →live demos
This page runs on Verve.
Each card below is a separately code-split WebAssembly chunk that the framework emits at build time. Open the network panel: you will see /islands/Counter.wasm, /islands/WasmFractal.wasm, and /islands/WasmParticles.wasm — three independent binaries, each one a tiny ReleaseSmall artifact, fetched on demand.
01 · island + actions
counter island · WS push · typed _call round-trip
02 · WASM compute
drag to pan · wheel to zoom
03 · WASM physics
move pointer · gravity well
open source · pre-1.0
github.com/sirhco/verve
background reading
Why I write Zig covers the language case. the Verve case study covers the framework decisions.