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:8080

Docs: 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

anim.timeline(a)
.add(step, .{ .rel = -0.25 })
.scrollTrigger(...) — done

three tweens, position arithmetic, scroll-gated

02 · draggable + inertia

throw me

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

count0

counter island · WS push · typed _call round-trip

02 · WASM compute

Mandelbrot · f64 escape-timeinside ·

drag to pan · wheel to zoom

03 · WASM physics

1500 particles · 60 fps loopstep+draw ms

move pointer · gravity well

02 · always-on infrastructure

Every Verve server ships /events (SSE), /ws (WebSocket), /health, and /metrics by default — no setup, no extra deps. Open a second tab to /events and you'll see a 1s tick from the same binary that served this page.

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.