Skip to content

Overview ​

qzjs is an embeddable runtime written in strict C99. It provides a small C API and a WinterTC-compatible runtime, and communicates with the host over JSON messages.

For a C application that wants part of its logic in JavaScript, qzjs supplies the runtime; the host builds no event loop or thread of its own.

How the Host Fits ​

qzjs architecture

  • WinterTC standard runtime — fetch, crypto.subtle, streams, timers, fs, serve() as globals
  • Message-based host boundary — qz_post_message (in) / message_cb (out), JSON in both directions
  • Isolated runtime model — each instance runs JS on its own internal thread; internal locks and atomics coordinate the thread, host, and worker boundaries, never JS execution
  • ECMAScript engine (ES2023) — full ES2023 support, fast startup, low memory
  • WinterTC-compatible runtime — fetch, console, crypto.subtle, ReadableStream, timers, fs, URL, TextEncoder, WebSocket, and more
  • Native extensions — compression (miniz), crypto (mbedTLS), text codec, WebAssembly (WAMR, wasm3 optional)
  • Zero system dependencies — all deps built from source via CMake; libuv is built from the deps submodule
  • Multi-context + Web Workers — isolated contexts (soft suspend/resume to disk); new Worker(url) runs a real parallel thread, or — with -DQZ_PROCESS_MODEL=ISOLATED, the default — a dedicated child process

The Host Integration Path ​

The Guide follows the order a host developer works in:

  1. Quick Start — build qzjs and run the minimal C embedding
  2. Host Integration — the full loop: create → messaging → lending capabilities → destroy
  3. Lifecycle — thread ownership, readiness, graceful shutdown
  4. Multi-Context — multiple isolated contexts in one runtime
  5. Extensions — register your own C functions as JS globals
  6. Bytecode — precompile JS to bytecode (faster startup, no source shipped)

When to Use qzjs ​

Use CaseWhy qzjs
Embedded / edge scriptingC99, tiny footprint, libuv event loop built in
Plugin systemsPer-runtime isolation, multi-context handled inside the runtime
Host applications needing scriptingScript your C app's behavior in JS without shipping Node.js
Edge computeWinterTC APIs feel familiar to JS developers
Testing & simulationmock_libuv for deterministic tests, no network needed

When NOT to Use qzjs ​

  • You need the Node.js module system — qzjs has no require/import of Node built-ins. Many pure-JS npm packages work (run python3 test/compat_check.py <pkg> (see Compatible Packages)); Node-only ones do not.
  • You need DOM — qzjs provides the WinterTC/W3C subset (fetch, WebSocket, streams, localStorage, ...) but no document/window.
  • You need shared-memory concurrency — the main runtime is single-threaded; Web Workers run real parallel threads or processes but communicate via structured-clone messages, not shared memory.
  • You need JIT performance — qzjs's engine is an interpreter, not a JIT compiler.

Project Structure ​

qzjs/
├── include/qzjs/       # Public headers (qzjs.h)
├── src/                 # Core runtime
│   ├── qzjs.c           #   Core API (create/destroy/post_message)
│   ├── thread.c         #   Internal thread + libuv loop
│   ├── uv_io.c          #   libuv I/O (network, fs, timers)
│   ├── msgq.c           #   Message queue (host ⇄ runtime)
│   ├── worker.c         #   Message dispatch (onmessage/postMessage)
│   ├── bridge.c         #   JS ↔ runtime bridge
│   └── context.c        #   Multi-context
├── polyfill/src/        # WinterTC module source

MIT Licensed