Whatever, Jamie logo

Whatever, Jamie

Subscribe
Archives
July 27, 2025

The many, many, many JavaScript runtimes of the last decade

Jamie jogging on a sports track, wearing a JavaScript vest

This last decade has seen an inundation of new JavaScript runtimes (and engines in equal measure), enabling us to run JavaScript in all manner of contexts with precise fitness for task. Through these, we've seen the language spread to the Cloud, the edge, Smart TVs, mobile devices, and even microcontrollers.

In this article, we'll explore what's driving this diversity, and why no one runtime or engine suffices for all purposes.

Edge computing

The first consumer "edge computing" solution was introduced in 2002 by Akamai, who enabled building on the edge using Java and .NET. But it would be a long time before JavaScript would join those languages – for one thing, JavaScript wouldn't be useful as a server-side language until Node.js emerged in 2009, and for another, Node.js wouldn't be employed in a serverless context until AWS Lambda in 2014. It was only with the announcement of Lambda@Edge (in preview from December 2016; released March 2017) that JavaScript would finally be seen on the edge, again running on Node.js.

But Amazon's monopoly was to be short-lived. For just six months later came the release of Cloudflare Workers, a purpose-built minimal runtime revolving around the Service Worker API. It was devised to open an "entirely new market" to Cloudflare: compute and storage. And this was notably different to Amazon's venture – for the first time in the over twenty years since JavaScript's inception, a company had managed to directly productise a JavaScript runtime.

Cloudflare Workers was a phenomenal success, with billions of tasks run in the half year following its beta release. This scent of opportunity triggered a gold rush. Deno appeared shortly after in 2018 as a challenger to Node.js, and within three years, Deno Land Inc had formed, launching Deno Deploy, an edge network intended to chase after Cloudflare. With an initial treasure chest of $4.9 million and raising a $21 million Series A funding round the year after that, it is clear that Cloudflare weren't the only ones who believed in the value of the niche.

The rush continued in 2022. No sooner had WinterCG been formed to start defining a common ground for all these new JavaScript runtimes popping up than Bun appeared, quickly raising $7 million [archive] to provide serverless hosting, continuous integration, and… edge computing. Wasmer followed suit in 2023, stepping into the Service Workers arena with WinterJS and Wasmer Edge. And not to be outdone, the very next year AWS responded with LLRT, another low-latency runtime just right for the edge (though not yet having an edge worker offering).

For all the overlap in strategy, notable is the variety in engines underpinning all these runtimes. While Deno continues Node.js's tradition of using V8, we see Bun employing JavaScriptCore, WinterJS using SpiderMonkey, LLRT on QuickJS, and Cloudflare Workers on the tailor-made workerd. No longer is the backend solely a stage for Node.js and V8 – it's now fashionable to pick a runtime and engine optimised for the task.

Microcontrollers

Many of the above runtimes would have a hard time running on a microcontroller, though. For the unfamiliar, AWS defines them as follows:

… a microcontroller is the basic computing unit inside smart electronic devices like washing machines and thermostats. It's a very tiny computer with its own RAM, ROM, and I/O systems, all embedded on a single chip. It can process digital signals and respond to user input, but its computing capacity is limited.

At the low end, microcontrollers may cost as little as 3 cents, have mere bytes of RAM, run in under a milliamp of current, have an 8-bit architecture, and/or have under a kilobyte of storage.

Given these specs, full-blown Node.js would be well out of the question, so many took to producing slim-as-possible engines – Duktape, Espruino, and mjs in 2013, JerryScript in 2014, Moddable in 2018, and elk in 2021. These projects pride themselves in running on under 64 kB of RAM (in fact apparently little more than 100 bytes, in the case of Elk!), though do make tradeoffs in other areas like performance to do so.

These engines then spawned new runtimes, though not necessarily serving the very lowest end of microcontrollers. JerryScript underlies IoT.js (2015) and Microlattice.js (2016), both Internet of Things runtimes; XS (2018?) underlies Moddable; and DukTape underlies low.js (2018), a low-resource reimplementation of Node.js.

It goes to show that venture capital needn't be a lure to develop a JavaScript runtime; sometimes people just want to run JavaScript on a device no matter what. Sometimes, they want to call JavaScript even when working in a different language – which brings us onto our next topic.

Polyglot engines

Although most JavaScript engines are based on a bespoke virtual machine (VM) used only by the engines themselves, some are based on established VMs, enabling zero-cost interop with other languages. In this space, C++ isn't necessarily king, and it's interesting to see how many ways there are to write a JavaScript engine.

The earliest polyglot engine was Rhino, which was made in 1997 as an effort to write Netscape Navigator – JavaScript engine and all – fully in Java. Rhino supports two-way interop between Java and JavaScript, based on the JVM. That is to say, it allows JavaScript to implement Java interfaces and call Java class methods, while allowing Java to define JavaScript classes, run scripts, and more. By 2006, it was included in JDK 6, and by 2008, it was the basis of the Helma runtime, nowadays known as RingoJS.

It was, however, superseded in JDK 8 (2014) by Nashorn, itself superseded in JDK 11 by Graal.js (2018) which supports both the JVM and GraalVM. This is an interesting line of history, as – all being Mozilla or Oracle projects – these three may be the only JavaScript runtimes rightfully allowed to use the JavaScript trademark (pending Deno vs. Oracle). Graal.js well deserves its seat on top however, going as far as supporting the whole Node.js SDK, by virtue of embedding its entire codebase.

This lineage is just the tip of the iceberg, though. There's jint (2013), for .NET, written in C#. There's PyNarcissus (2009), langjs (2009), and jispy (2014), for Python. There's twostroke (2011) and Opal (2013), for Ruby. There's elsa (2020) for Go. There's Boa (2018), toyjs (2020), spiderfire (2021) and jaws (2024) for Rust. There's Kiesel (2023) for Zig, and the list surely goes on. Though arguably not all of these projects are fully "polyglot" – some are merely interpreters and don't offer two-way language interop.

JavaScript engines are even being implemented in JavaScript! Narcissus (2007-2010), originally created by Brendan Eich, was used by Mozilla as a test-bed for rapidly prototyping new JavaScript language features. Jscomp followed in 2015, an AoT compiler written in JavaScript that generated C++ executable code. More recently, Oliver Medhurst has carried on the torch with Porffor (2023), compiling instead to WASM (or, as an extra step, C). It follows on from their equally notable web engine, Shadow (2023), similarly written in JavaScript. While starting out as a hobby project, Porffor has shown such worth as to attract financial support from Chris Wanstrath, enabling Oliver to work full-time on it as of 2024, leading to a 58% conformance score on Test262 less than a year later.

This demand to call JavaScript from other languages and even build new JavaScript engines in it underlines the strength of its ecosystem and the ergonomics of the language. Though nowhere is this ecosystem advantage as clear as it is in the world of native apps.

Native apps

JavaScript's web origins make it a language well suited for building GUI apps, and so it was only a matter of time before it began being employed in native apps, often as a basis for cross-platform frameworks. A point of constant contention in this space is what balance to strike between fidelity to the web platform and fidelity to the native platform, so where better to start than the frameworks that went all in on the web?

Web view based apps

On mobile

The iPhone's release in 2007 set a new bar for mobile hardware, normalising the use of desktop-grade web tech on mobile. But with iOS, Android, Windows Phone, Blackberry, WebOS, Symbian, Samsung Bada, and Firefox OS all vying for market share at once, developing for just one platform posed a significant opportunity cost. In 2009, Adobe answered this quandry by releasing PhoneGap, a framework for embedding web apps into native apps via a "web view" (which of course included a JavaScript runtime).

PhoneGap proved popular, with over 23,000 apps having been built on their build service (according to a contradictory chart) by 2011, and having been "downloaded over 1 million times" and "used by over 400,000 developers" by 2012. Along the way, it was opensourced as Cordova, and a UI toolkit was created for it, Ionic. The team behind Ionic would go on in 2019 to announce their successor for Cordova, Capacitor, which brought an improved native plugins API and added support for targeting desktop via another up-and-coming framework, Electron.

On desktop

Electron, now a household name for web view-based desktop app development, traces its roots back to node-webkit, a project with releases dating as far back as 2012. While the latter was originally based on Node.js and WebKit, it was renamed to NW.js in 2015 due to plans to migrate to Chromium (an effort which was completed with v0.13.0 in 2016). Electron's history runs alongside this – in 2013, GitHub began work on Atom Shell, an NW.js-like framework built by a former NW.js core contributor to use as the foundation for their Atom text editor. While Atom itself would one day be sunset, Atom Shell would live on, being renamed to Electron in 2015. Although NW.js is an active project to this day, Electron has very much taken the limelight, coming to dominate the market for desktop apps, with many of the industry-leading apps like Discord, Slack, Linear, and Visual Studio Code all being based on it.

On Smart TVs

Beyond mobile and desktop, there's also the esoteric Smart TV platform. Many Smart TVs run forks of desktop web browsers, with additional APIs for interacting with native TV functionality such as the broadcast stream, object carousel, remote control keysets, and DRM systems.

Web-based Smart TV specifications/platforms include OIPF (2008-2015), HbbTV (2010 to present), and ATSC 3.0 (2016 to present). There are also endless platforms with different SDKs, such as webOS, YouView, Freeview Play, Freesat, and more. On some platforms, one negotiates with an infrastructure provider to serve an app over broadcast; on other platforms, one negotiates with the platform owner to have the app distributed with the devices; and on others (e.g. platforms based on Android TV and Apple TV), one submits the app to an app store.

The popular Amazon Fire TV (2014) and Fire TV Stick (2014) are based on Chromium, as Samsung Tizen TVs have been since 2017 (using WebKit before that), and all three of these employ Cordova, showing it's not purely for mobile devices. Roku is different, with apps being programmed in BrightScript (not an ECMAScript language). And while Apple TV used to offer a framework known as TVMLKit JS (2015), based on JavaScriptCore, it was not based on web technologies, beyond implementing a very small amount of the DOM spec.

With more than 1.26 billion internet-connected TV devices installed worldwide and HbbTV devices present in over 100 million homes in Europe alone, Smart TVs account for a surprisingly large segment of JavaScript runtimes. Only through this common language and the ubiquitous web platform is it practical to deploy apps to the sheer number of different device types in this market.

React Native

On mobile

As the mobile landscape began to settle down to a landscape dominated mostly by iOS and Android, Facebook announced a new cross-platform framework in 2015 targeting the both of them: React Native. The framework enabled developers to write a React app that would render native platform views, based on a JavaScript runtime that could call out to native functionality and respond to native events via posting serialisable messages across a "bridge".

Initially, this JavaScript runtime was based on JavaScriptCore, as it came included in the iOS SDK (and had done since iOS 7 in 2013) and so saved significant app size for iOS users. Being also heavily optimised for mobile usage, it was the smart choice at the time. However, after a few years of seeing how it performed in practice, Facebook would seek to improve upon it – and so in 2019, they announced Hermes, a new engine tailor-made for React Native.

Hermes boasted improved startup times thanks to various tricks such as Ahead of Time (AoT) compilation (working around Apple's ban on JIT) and memory mapping of its bytecode into RAM. It also cut down on bundle size for Android (where JavaScriptCore is not a system framework) and memory utilisation for both platforms. Hermes delivered so well on its promises that it was made the default from 2022, and Meta are even preparing a successor, Static Hermes (2023), which will support an arbitrary mix of native and interpreted code, both "typed" and "untyped", to afford performance on a par with C/C++.

Hermes wasn't the only option explored, however. From 2018, React Native began to implement an engine-agnostic JavaScript Interface (JSI), which (besides eschewing message-posting in favour of direct native bindings) has enabled it to be adapted for use with various engines. JSI has been implemented for Hermes and JSC (both by Meta), V8 (independently by Microsoft and Expo; see Kudo Chien's talk on the latter), QuickJS (by Microsoft, for a hackathon), Chakra (by Microsoft, for use in React Native Windows, as Chakra is included with Windows), and even Duktape (by Semmy Purewal)!

In fact, React Native looks set to become the most engine-agnostic JavaScript runtime around, as Microsoft, Callstack, and the React Native community (myself included!) are currently working hard on delivering support for Node-API, which is supported even more widely than JSI (more on that later). Considering also the first-class native code interop promised to come with Static Hermes, it's come a long way since its roots as a single-engine, message-posting runtime.

But, how popular is it compared to web view-based app development frameworks? Evan Bacon reported that by 2023, only 74 of the top 2,479 iOS apps used either Cordova or Capacitor, and in fact most still used only the former. Whereas, by 2025, 30 of the top 100 iOS apps were written in React Native, compared to only 3 web view-centric apps. RevenueCat (the leading In-App Purchases solution, used by over 50,000 apps), reports that React Native is "on track" to becoming the most popular app development framework using their platform, with 34% of RevenueCat-using apps being based on it, and fewer than 1% to be accounted for by frameworks such as Cordova.

On desktop

It's a rather different story for desktop platforms, however. While Microsoft maintains fully-featured implementations of React Native for macOS and Windows (independent from ptmt's macOS implementation in 2015), the ecosystem for these platforms is a small fraction of what it is for iOS and Android, with just 20 native modules in React Native Directory indicating support for desktop in contrast to 1,698 for mobile at the time of writing. This is reflected in the smaller usership, with only around 100 correspondents in the State of React Native 2024 survey targeting desktop, in contrast to over 3,000 targeting mobile.

The recommended meta-framework for using React Native, Expo, still lacks support for desktop, and while Microsoft has employed React Native in various Office apps and even the Start Menu, it is mainly as a brownfield solution, making small content islands within the apps. For greenfield apps, Electron is very much more the vogue, with hundreds of apps listed on their showcase alone.

Some attempts have been made to target Linux, such as React Native Linux (a Qt-based fork by Canonical, later renamed to React Native Desktop) (2017-2021) and React Native Skia (2020), but none see mainstream usage, with the latter used by just 20 correspondents in the aforementioned survey. Again, Electron is presumably the bigger fish here.

On Smart TVs

React Native does have a significant foothold in the Smart TV space, though, with implementations for both tvOS (2016) and Android TV (2018) seeing popular usage. With the fall of You.i TV (whose proprietary You.i Engine One deployed to 14 different platforms), the leaders in this space are now Callstack, deploying to a similarly large stable. Notable React Native apps include Crunchyroll, NFL, Bloomberg, Yahoo Finance, and more.

NativeScript

By no means were native apps a two-horse race between Cordova and React Native, though. NativeScript, announced in 2014, came out of the doors offering three separate runtimes with full JavaScript bindings to the iOS, Android, and Universal Windows platforms (via JavaScriptCore, V8, and V8 again respectively), abstracted by a cross-platform app development framework. While the decline of Windows Phone led to the Windows runtime being sunset in 2016, the iOS and Android runtimes continue to this day, standardising on V8 in 2019 (essentially once V8 supported JITless mode to enable its use on the App Store). And while the proof-of-concept tvOS runtime demonstrated in 2015 never took off, the Android Wear (2015) and Android TV runtimes (2018) have seen use in industry.

More recently, NativeScript has increased focus on enabling the runtimes to be used standalone (rather than as part of an end-to-end app development framework), rearchitecturing the runtimes as engine-agnostic libraries of JS↔native bindings via Node-API. These new iOS (2023) and Android (2024) libraries can upgrade any JavaScript engine to give it full platform access, provided the engine supports Node-API, which is quite a long list. Indeed, Node-API has been implemented for V8 (by Node.js, Deno, and ByteDance), JSC (by Bun, Hummer, and ByteDance), Hermes (by Microsoft and Hummer), QuickJS (by OpenHarmony, Hummer, and ByteDance), Chakra (by node-chakracore) and JerryScript (by IoT.js).

While none of the top 100 iOS apps use NativeScript today, the new Node-API rearchitecture will allow it to integrate with more dominant frameworks such as React Native and Electron, responding to pain-points of native API access, much like the (pre-Node-API) Capacitor integration in 2021. A proof-of-concept Expo integration (by me!) demonstrated this in 2024, though work is ongoing to allow it to be used without patching and forking.

Node.js

While the above-mentioned runtimes each form part of an end-to-end (and usually cross-platform) app development framework, Node.js has mostly been employed in native apps just as a tool for accessing the Node.js SDK and/or native addons, with little success getting involved with creating graphical user interfaces.

It wasn't until nearly a decade after its release that Node.js was ported to mobile – work that was undertaken by Janea Systems in 2017. One of the major obstacles to deploying on iOS was that the App Store did not allow apps that used JIT compilation. Janea Systems worked around this by forking Node-ChakraCore, a Microsoft project that swapped out V8 for ChakraCore to run without JIT. This was a fashion at the time, with SpiderNode (2017) adopting Spidermonkey, node-jsc (2018) adopting JavaScriptCore (which led to node-native-script the same year, demoed here), and node-jerryscript (2019) adopting JerryScript.

Janea Systems were eventually able to adopt V8 once JITless mode propagated from V8 in 2019 to Node.js in 2020. Although they had demoed using Node.js alongside React Native as early as 2017, it never really caught on for that use-case, and Node.js for mobile remained a smaller player in mobile app development. It continues to see usage, however, with the baton passing from Janea Systems to a new fork by André Staltz in 2023.

Besides Janea Systems's port, Samsung created their own Lightweight Node.js (2021) based on their earlier engine Escargot (2016) for "mid-range devices such as mobile phone, tablet and TV", which probably sees use on Tizen OS, though the project may not see much use outside of Samsung.

On the desktop side, there have been countless libraries to expose native app development functionality via Node.js. For macOS AppKit, there has been NodObjC (2011), objc (2017), and NativeScript (2023), for example. For WinRT, NodeRT (2014) (echoed by the Deno-based deno_winrt in 2023). And for Qt, nodeQt (2011), node-qt (2012), NodeGUI (2019). Despite affording full platform access, though, none of these have taken off as end-to-end app development solutions competitive with Electron.

Summary

JavaScript has ridden the wave of the personal device boom like no other programming language. While motivated to break free of the browser to make full use of native APIs, no framework has been successful in departing fully from the conventions of the web platform, with GUI programming still being firmly inspired by browser APIs (as with React Native), if not entirely delegated to web views (as with Electron).

Conclusion

Developers want to run JavaScript in every context possible, and over the last ten years, they have raced to do so. From previously being confined to the browser, now the language is seen in basically every device category, and – in serverless contexts – people are even paying to execute it.

JavaScript runtimes exist to cater to all manner of resource constraints, from the cheapest chip to the meanest machine (though perhaps with less of a foothold in supercomputers, which did not come up during this research). There are great options for interop with other languages, often to facilitate access to system APIs (which frameworks like React Native make extensive use of).

Lastly, JavaScript continues to show its strength as a language for GUI programming, being employed in a variety of ways to develop native apps on mobile phones and Smart TVs, though with web view based apps still being the vogue on desktop.

Why is there no one "best" runtime, then? Simply, with so many different contexts to run JavaScript in, there are just too many conflicting factors to optimise for. Startup performance, runtime performance, bundle size, API support, and ease of native access all fight for priority. While the browser-native engines V8 and JavaScriptCore have maintained their popularity outside the browser, they are beginning to face competition from runtimes based on new engines such as Hermes, workerd, and QuickJS in certain areas.

But healthy competition and freedom of choice are only ever good things. The old guard are forced to innovate, and a high standard is maintained for newcomers. For developers, it's the most all-purpose language one could learn and the single safest technology choice for the future.

Addendum

I started writing this article in April 2024, stretching the limits of Buttondown's edit history at over 5,260 revisions by August, before continuing to hack away at it on and off for another 12 months. Even now, I can hardly call it finished. I wanted to do the subject justice, but there's just too much to cover and I'd like to finally post this thing so that I can write something else. There's surely far more to say about WebAssembly, emulators, wearables, and runtimes for gaming (with a side-dish of ActionScript?), but let's keep that Pandora's Box closed for now.

During the course of writing, a few new runtimes were released. Most notably, ByteDance announced the cross-platform framework Lynx (2025), whose (same-named) runtime is based on their new PrimJS engine (itself based on QuickJS). I've not had any time to dig into it, but I can say it fascinates me that the runtime additionally supports JavaScriptCore, QuickJS, and V8 through Node-API. Curiously, while Android defaults to using PrimJS throughout, iOS defaults to a mixture of PrimJS on the main thread and JavaScriptCore on the background thread. This makes it rather an oddity amongst all other runtimes covered above.

And in focusing too hard on non-browser contexts, I appear to have embarrassingly omitted LibWeb, a browser engine (including a JavaScript runtime) based on the LibJS engine from the up-and-coming Ladybird browser (2020), which I've been following closely.

Besides those, I have a list of honourable mentions that I simply didn't manage to weave into the story (though admittedly with some being outside the "last decade" boundary). Namely:

  • gjs (2008): A JavaScript runtime based on SpiderMonkey, with bindings to GNOME.
  • MuJS (2013): A highly embeddable JavaScript engine.
  • JavaScript for Automation (JXA) [archive] (2014): Apple's JavaScript runtime based on JavaScriptCore that includes a full JavaScript projection for the (Objective-C-based) macOS SDK.
  • dukluv (2014): A libuv-based JavaScript runtime using the Duktape engine.
  • Napa.js (2016): A multi-threaded JavaScript runtime build on V8.
  • txiki.js (2019): A libuv-based JavaScript runtime using the QuickJS engine (the successor to dukluv).
  • lo.js (2023): I'm not clear what this JavaScript runtime is all about, but I do keep hearing about its incredible C interop.

... and there are surely many more that deserve a mention!

With that, thank you for reading! I hope this article raises awareness of JavaScript runtimes beyond the usual "browsers and Node.js" narrative, and begins to convey the true scale of the iceberg.


If you thought this was a jolly good read, then the excellent news is that there is a mechanism below for obtaining more of it. With any luck, see you in the next issue where again, I'll talk about Whatever.

Don't miss what's next. Subscribe to Whatever, Jamie:
GitHub X
Powered by Buttondown, the easiest way to start and grow your newsletter.