Introduction
Embedding a scripting engine into a C++ application unlocks enormous flexibility: configuration as code, live-reloadable game logic, user-defined automation, and plugin systems with full programming language expressiveness. Rather than inventing a custom DSL or configuration format, embedding a JavaScript engine gives your users access to a familiar, battle-tested language with a rich standard library.
In this guide, we compare three production-grade embeddable JavaScript engines for C++ applications: Duktape, QuickJS, and JerryScript. Each targets different use cases, from resource-constrained IoT devices to high-performance server applications.
Why Embed a JavaScript Engine?
Traditional plugin systems use C ABI with dynamic loading (dlopen, LoadLibrary), but this has serious drawbacks: platform-specific binaries, ABI fragility across compiler versions, and security risks from native code execution. A JavaScript engine provides a sandboxed, platform-independent execution environment with controlled API exposure.
Embedded engines also enable hot-reloading of game logic, server-side scripting for web applications, and user automation in desktop tools — all without requiring users to install a compiler toolchain. For a broader perspective on plugin architectures, see our embeddable plugin systems guide.
Comparison Table
| Feature | Duktape | QuickJS | JerryScript |
|---|---|---|---|
| Stars | 6,207 | 10,745 | 7,398 |
| Author | Sami Vaarala | Fabrice Bellard | JS Foundation |
| ECMAScript | ES5.1 + partial ES6 | ES2020 (full) | ES5.1 + partial ES6 |
| Binary Size | ~200 KB | ~620 KB | ~170 KB |
| Memory Footprint | < 2 MB default | ~3 MB | ~2 MB |
| Performance | Moderate (interpreted) | Fast (JIT-compiled) | Moderate (interpreted) |
| C API | Simple, well-documented | Clean, minimal | Extensive, IoT-focused |
| Debugger | Basic (duk_debug) | Built-in debugger | JerryScript debugger |
| Last Update | Mar 2024 | Jun 2026 | Oct 2025 |
| License | MIT | MIT | Apache 2.0 |
| Best For | Legacy systems, small footprint | Modern JS, full ES2020 | IoT, microcontrollers |
Duktape: The Minimalist Workhorse
Duktape is designed for portability and compact footprint. It compiles to under 200 KB and can run on platforms from ARM Cortex-M microcontrollers to x86 servers. Its API is refreshingly simple — you can embed a JavaScript engine in fewer than 20 lines of C code.
| |
Duktape’s strength is its reliability in constrained environments. With configurable memory limits, a mark-and-sweep garbage collector with emergency compaction, and no external dependencies beyond libc, it runs on virtually any platform. However, its ECMAScript support stops at ES5.1 with partial ES6 — modern JavaScript features like async/await, Proxy, and class syntax are unavailable.
CMake Integration
| |
QuickJS: Full ES2020 with Surprising Performance
QuickJS, authored by Fabrice Bellard (creator of QEMU and FFmpeg), is remarkable: a complete ES2020 engine in a compact C library. It supports the full modern JavaScript specification including modules, async/await, BigInt, typed arrays, and Proxy objects — all while compiling to just 620 KB.
| |
QuickJS compiles JavaScript to bytecode with a register-based interpreter that achieves impressive speeds — often 2-5x faster than Duktape for numerical workloads. It also includes a command-line compiler (qjsc) that can compile JavaScript to C, embedding scripts directly into your binary for zero-startup-time execution.
For C++ projects managing these kinds of library dependencies, see our C++ package management comparison.
JerryScript: IoT-Optimized JavaScript
JerryScript was purpose-built for microcontrollers and IoT devices by Samsung and the JS Foundation. It implements ES5.1 with optimizations for extremely constrained memory — the engine can run in under 64 KB of RAM and flash.
| |
JerryScript’s distinctive features include heap compaction, snapshot execution (pre-compiled bytecode loaded directly into ROM), and per-call memory limits. These aren’t just nice-to-haves — they’re essential for long-running embedded applications where memory fragmentation can cause failures after weeks of uptime. For additional tools to ensure code quality in embedded C++ projects, see our C++ static analysis guide.
Why Self-Host Your Scripting Engine?
Choosing to embed a scripting engine rather than building a custom configuration language is a strategic decision that pays dividends in developer productivity and user empowerment. JavaScript is the world’s most widely understood programming language — your users and contributors already know it. Custom DSLs, no matter how well-designed, face an adoption cliff.
Self-hosting the engine means you control the sandbox, the API surface, and the execution limits. Unlike cloud-based scripting services, an embedded engine keeps user scripts local, respects air-gapped deployments, and eliminates network latency from the execution path. For security-sensitive applications, the sandbox guarantees that user scripts cannot access the filesystem, network, or process environment unless explicitly granted.
The footprint trade-off is small: Duktape adds 200 KB to your binary, QuickJS 620 KB, and JerryScript 170 KB. In an era where your application likely ships with megabytes of assets, this is negligible. The flexibility gained — live-reloadable game logic, user automation scripts, configurable server middleware — is transformative.
FAQ
Which engine should I choose for a new project in 2026?
QuickJS is the default recommendation for most new projects. It supports full ES2020, has active maintenance (Fabrice Bellard released updates in June 2026), and its 620 KB footprint is negligible for desktop, server, and mobile applications. If you need ES2020 features like async/await, modules, or BigInt, QuickJS is the only option among the three. For IoT with sub-512KB flash budgets, JerryScript is the better fit.
Can I use these engines in a commercial, closed-source product?
Yes. All three engines are distributed under permissive open-source licenses (MIT or Apache 2.0) that explicitly permit commercial use, modification, and distribution in proprietary products. No copyleft restrictions apply.
How do I debug JavaScript running in an embedded engine?
QuickJS includes the most complete debugging support with a built-in debugger that supports breakpoints, step-through, and variable inspection. Duktape provides duk_debug — a debug transport protocol that integrates with Duktape-specific debugger UIs. JerryScript offers a WebSocket-based debugger that connects to browser DevTools. All three also support console.log-style debugging via registered C callbacks.
What about Lua — why JavaScript instead?
Lua is an excellent embedded language with a smaller footprint (~120 KB for LuaJIT). However, JavaScript’s larger developer community, richer standard library (especially in ES2020), and wider ecosystem of existing libraries make it a better choice when developer familiarity and code reuse matter. If your team knows Lua and your scripting needs are simple, Lua is equally viable — see sol2 for C++/Lua bindings.
Can I run untrusted user scripts safely?
Yes, but with caveats. All three engines provide sandboxing: you control the global object, expose only whitelisted native functions, and set resource limits (memory, execution time). However, side-channel attacks and engine bugs are still possible. For running truly untrusted code, consider process-level isolation (sandbox each script in a separate OS process) in addition to engine-level sandboxing.
Do these engines support multithreading?
Duktape and JerryScript are single-threaded — each context must be accessed from one thread at a time, though you can create multiple independent contexts. QuickJS supports SharedArrayBuffer and Atomics for inter-context communication, making it suitable for multithreaded server applications that need shared state between script instances.
💰 想测试你的市场判断力?我用 Polymarket 做预测市场交易——这是全球最大的预测市场平台,从大选结果到技术监管时间线,什么都可以押注。和赌博不同,这是真正的信息市场:你懂的信息越多,胜率越高。我靠预测技术相关事件的走向已经赚了不少。用我的邀请链接注册:Polymarket.com