The runtime I always wanted to exist.
NativeRuntime exists because after thirty years of writing systems software in C, C++, and every fashionable language that came after, I kept reaching for a tool the industry never built: a small, fast, cross-platform JavaScript runtime that could drop into a native binary and integrate cleanly with any library written in any language.
This page is the technical justification for why I built it and the parts of my background that led me here. Not a manifesto. Not a company story. Just the engineering reasoning, in case any of it is useful to you.
Thirty years of writing the same code in five different languages.
I have been a working software engineer for about three decades. Most of that time has been spent building systems-level software — backends, middleware, native desktop applications, real-time infrastructure — at organizations large enough that the engineering choices mattered. I have shipped production code in C, C++, C#, Java, Python, JavaScript, TypeScript, and a handful of others. I have been a solution architect on platforms used by tens of millions of people. I am not a beginner, and this is not my first project.
What I learned across those years was that the underlying problems do not change much from job to job. The languages and frameworks do. The fashionable answer of any given decade does. But the fundamental needs — read a file, talk to a database, render a window, accept a network connection, run a thread, process some data — recur in every project. And every project, in every shop, in every language, ends up writing approximately the same code to solve them. Slightly differently, slightly worse than the time before, with slightly different bugs.
That observation alone is not unusual. Most experienced engineers feel it. What I think is unusual is that I refused to accept it as inevitable.
The things I kept running into.
Over the years, every cross-platform application project I worked on hit the same set of dead ends. None of them were dealbreakers in isolation. Stacked together, they were the reason I kept looking for an alternative that never quite existed.
-
2010s
Node packaged binaries shipped 30–40 MB of runtime overhead
For an application that needed perhaps 200 KB of actual business logic, the standalone executable carried tens of megabytes of Node, libuv, V8, and a standard library most of which the application never touched. The attack surface alone made it inappropriate for anything sensitive.
-
2010s
Electron made the problem worse
If 30 MB was unacceptable, 150 MB of bundled Chromium was indefensible. And yet Electron became the default answer for cross-platform desktop, because the alternative was writing the same UI three times in three different toolkits.
-
2015+
Bridge-based runtimes paid a marshalling tax on every call
React Native, NativeScript, and similar architectures put a JavaScript world on one side, a native world on the other, and a serialization layer between them. Performance was acceptable until it wasn't. Debugging across the bridge was always miserable. The mental model was always two languages, never one.
-
2020+
Rust solved real problems but at significant cost
For new systems work, Rust's memory safety and performance characteristics are genuinely valuable. But the learning curve, the borrow checker, and the smaller developer pool meant that any team adopting Rust paid in onboarding time, hiring difficulty, and code that future maintainers would struggle to read.
-
2022+
Bun and Deno improved things but kept V8/JSC
The newer runtimes were faster, friendlier, more developer-respecting than Node. But they still embedded a 20+ MB JavaScript engine inside every binary. They were better Node alternatives, not better answers to "how do I ship a small native app with JavaScript inside it."
By the time I started seriously sketching what would become NativeRuntime, my mental wishlist was clear: a JavaScript engine small enough to embed in a 2–3 MB binary, a way to call native libraries without marshalling overhead, support for every major platform from one codebase, and zero framework lock-in.
QuickJS was already the answer. The industry hadn't noticed.
QuickJS was released by Fabrice Bellard in 2017. If you do not know Bellard's work, he is the author of FFmpeg, QEMU, TinyCC, and several other foundational pieces of infrastructure that the entire software industry runs on. QuickJS is, by any measure, a remarkable JavaScript engine: small (a few hundred kilobytes), MIT-licensed, standards-compliant through modern ES2023, and surprisingly fast for a non-JIT interpreter.
The key technical detail that mattered to me is the QuickJS object binding model. JavaScript objects are represented in a way that lets native C code create them, mutate them, and access them with no serialization layer between worlds. When JavaScript calls a function exposed by a C library, the call goes through QuickJS into the native function with effectively the same overhead as a regular function call. There is no IPC. There is no JSON. There is no bridge.
This is categorically different from how Node, Electron, React Native, and most JavaScript-adjacent runtimes handle native interop. The closest precedent is probably n-api in Node, but n-api still exists inside Node's much larger runtime model, with all the overhead that implies.
QuickJS is the foundation. NativeRuntime is the last mile.
None of this work exists without Fabrice Bellard's engine. NativeRuntime does not modify QuickJS — it embeds it unmodified, at a pinned version, with full attribution. The adapter pattern, build tool, and reference adapters are my contribution. The engine that makes any of it possible is his.
QuickJS sat in plain sight for nearly a decade. Most engineers I know who write JavaScript professionally have either never heard of it or know it only as a curiosity. That is the gap NativeRuntime is meant to close: take a remarkable engine, give it the build tooling and adapter pattern it deserves, and make it accessible to the millions of developers who already know JavaScript.
The adapter pattern is the thing that scales.
Once I had QuickJS as the engine, the remaining question was how to connect it to the libraries developers actually need: a UI toolkit, a database, a 3D engine, a networking layer. I did not want to build any of these myself. Real engineers had spent decades building libraries like SQLite, Magnum, NATS, and dozens of others. Reimplementing them in JavaScript would have been an absurd waste of effort.
What was needed was a clean, repeatable adapter pattern: a small layer of C or C++ that wraps a native library, exposes its functions to QuickJS, and follows a consistent shape so that every adapter looks like every other adapter. Two or three core files form the substrate. Each adapter slots in the same way. The build tool composes them into a final binary.
The first time I tested this seriously, I built adapters for SQLite, AppGUI (desktop UI), and an early Magnum binding over a single weekend. They all worked. The performance was better than I expected. The pattern was simple enough that I knew it would generalize to anything else — Rust crates via cbindgen, C++ libraries via thin wrappers, networking layers, audio engines, anything.
That weekend was the moment I knew NativeRuntime had to exist as a public project. The technical pieces all fit together. The pattern was real. The only remaining work was packaging it for other developers to use.
What this project is, and what it is not.
A few commitments shape every technical decision in this repository. They are worth stating up front so contributors and users can decide whether the philosophy matches what they need.
01Small core, growing edge
The runtime itself stays small. New capabilities arrive as adapters, not as features in the core. The build tool does one thing. The CLI does one thing. The engine is borrowed unmodified.
02JavaScript as the orchestration layer
Application logic is JavaScript. Native libraries handle what they are good at. No frameworks above the runtime. No marshalling between worlds. The line between JS and native is a function call, not a protocol.
03Honest about what ships
If something is preview, it is labeled preview. If a benchmark hasn't been measured, it is not published. If an adapter is incomplete, the docs say so. Aspiration belongs in the blog; the docs reflect what actually works today.
04Compensate upstream
The libraries this project depends on — QuickJS, Magnum, SQLite, NATS, AppGUI — represent decades of work by people who deserve attribution and, where appropriate, financial support. That is a commitment, not a marketing line.
05Open source forever for the runtime
NativeRuntime itself, the build tool, the CLI, and the reference adapters are MIT-licensed and will remain so. The core artifact developers depend on is not a loss leader for something else. It is the project.
06Built by someone who uses it daily
This is not a side project I will lose interest in. NativeRuntime is the runtime my own infrastructure runs on. Every release ships against my own production systems before it reaches the public repository. The fix-it-myself loop is tight.
Where this goes from here.
The 1.0 release is intentionally small: QuickJS embedded cleanly, the adapter pattern documented, a build tool that produces native binaries for the major platforms, one fully documented reference adapter (SQLite), and three working hello-world templates for desktop, mobile, and middleware.
Following 1.0, the catalog of reference adapters will expand — AppGUI for desktop UI, Magnum for 3D, plus Rust crate and C library examples that show developers the pattern in their preferred ecosystem. Cross-runtime benchmarks will be published as they're produced.
Further out, I plan to invest personal engineering time in an ahead-of-time compiler that extends QuickJS's existing qjsc bytecode generator into a real C/assembly emitter. When that lands, every adapter in the catalog gains true native compilation automatically. There is no fixed date for that work. It will ship when it ships.
What I can promise is that the runtime, the build tool, and the reference adapters will keep shipping at the cadence and quality that justify their existence. This is a project that earns its place by being useful, not by being announced loudly. If it earns yours, I am glad. If it does not, the engineering reasoning above will at least be a useful read.