TNS
VOXPOP
Where are you using WebAssembly?
Wasm promises to let developers build once and run anywhere. Are you using it yet?
At work, for production apps
0%
At work, but not for production apps
0%
I don’t use WebAssembly but expect to when the technology matures
0%
I have no plans to use WebAssembly
0%
No plans and I get mad whenever I see the buzzword
0%
Frontend Development / Software Development / WebAssembly

Will JavaScript Become the Most Popular WebAssembly Language?

Rising interest in using JavaScript to write WebAssembly is driving maturity in tools, while the component model points to polyglot programs.
Apr 25th, 2023 6:00am by
Featued image for: Will JavaScript Become the Most Popular WebAssembly Language?
Image via Shutterstock 

Since it grew out of the browser, it’s easy to assume that JavaScript would be a natural fit for WebAssembly. But originally, the whole point of WebAssembly was to compile other languages so that developers could interact with them in the browser from JavaScript (compilers that generate Wasm for browsers create both the Wasm module and a JavaScript shim that allows the Wasm module to access browser APIs).

Now there are multiple non-browser runtimes for server-side WebAssembly (plus Docker’s Wasm support), where Wasm modules actually run inside a JavaScript runtime (like V8), so alignment with JavaScript is still important as WebAssembly becomes more of a universal runtime.

Wasm is intentionally polyglot and it always will be; a lot of the recent focus has been on supporting languages like Rust and Go, as well as Python, Ruby and .NET. But JavaScript is also the most popular programming language in the world, and there’s significant on-going work to improve the options for using JavaScript as a language for writing modules that can be compiled to WebAssembly (in addition to the ways WebAssembly already relies on JavaScript), as well as attempts to apply the lessons learned about improving JavaScript performance to Wasm.

Developer Demand 

When Fermyon released SDKs for building components for its Spin framework using first .NET and then JavaScript and TypeScript, CEO Matt Butcher polled customers to discover what languages they wanted to be prioritized. “[We asked] what languages are you interested in? What languages are you writing in? What languages would you prefer to write in? And basically, JavaScript and TypeScript are two of the top three.” (The third language developers picked was Rust — likely because of the maturity of Rust tooling for Wasm generally — with .NET, Python and Java also proving popular.)

Suborbital saw similar reactions when it launched JavaScript support for building server-side extensions, which quickly became its most popular developer language, Butcher told us.

It wasn’t clear whether the 31% of Fermyon customers wanting JavaScript support and the 20% wanting TypeScript support were the same developers or a full half of the respondents, but the language had a definite and surprising lead. “It was surprising to us; that momentum in a community we thought would be the one to push back the most on the idea that JavaScript was necessary inside of WebAssembly is the exact community that is saying no, we really want [JavaScript] support in WebAssembly.”

Butcher had expected more competition between languages for writing WebAssembly, but the responses changed his mind. “They’re not going to compete. It’s just going to be one more place where everybody who knows JavaScript will be able to write and run JavaScript in an emerging technology. People always end up wanting JavaScript.”

“I think at this point, it’s inevitable. It’s going to not just be a WebAssembly language, but likely the number one or number two WebAssembly language very quickly.”

While Butcher pointed at Atwood’s Law (anything that can be written in JavaScript will be), director of the Bytecode Alliance Technical Steering Committee Bailey Hayes brought up Gary Bernhardt’s famous Birth and Death of JavaScript (which predicts a runtime like WebAssembly and likens JavaScript to a cockroach that can survive an apocalypse).

“Rust can be hard to learn. It’s the most loved language, but it also has a pretty steep learning curve. And if somebody’s just getting started, I would love for them to start working with what they know.” Letting developers explore a new area like WebAssembly with the tools they’re familiar with makes them more effective and makes for a better software ecosystem, Hayes suggested. “Obviously we’re all excited about JavaScript because it’s the most popular thing in the world and we want to get as many people on WebAssembly as possible!”

What Developers Want to Do in JavaScript 

Butcher put WebAssembly usage into four main groups: browser applications, cloud applications, IoT applications and plugin applications. JavaScript is relevant to all of them.

“What we have seen [at Fermyon] is [developers] using JavaScript and WebAssembly to write backends for heavily JavaScript-oriented frontends, so they’ll serve out their React app, and then they’ll use the JavaScript back end to implement the data storage or the processing.”

There are obvious advantages for server-side Wasm, Hayes pointed out. “Folks that do server-side JavaScript are going to roll straight into server-side Wasm and get something that’s even smaller and starts even faster: they’re going to see benefits without hardly any friction.”

“People are very excited about running WebAssembly outside the browser, so let’s take the most popular language in the world and make sure it works for this new use case of server-side WebAssembly.”

There were some suggestions for what else JavaScript in WebAssembly would be useful for that struck Butcher as very creative. “One person articulated an interesting in-browser reason why they want JavaScript in WebAssembly, that you can create an even more secure JavaScript sandbox and execute arbitrary untrusted code inside of WebAssembly with an interface to the browser’s version of JavaScript that prevents the untrusted JavaScript from doing things to the trusted JavaScript.”

Being able to isolate snippets of untrusted code in the Wasm sandbox is already a common use case for embedded WebAssembly: SingleStore, Scylla, Postgres, TiDB and CockroachDB have been experimenting with using Wasm for what are effectively stored procedures.

Fastly’s js-compute runtime is JavaScript running on WebAssembly for edge computing, Suborbital is focusing on plugins (where JavaScript makes a lot of sense), Shopify recently added JavaScript as a first-class language for WebAssembly functions to customize the backend, and Redpanda shipped WebAssembly support some time ago (again using JavaScript).

Redpanda’s WebAssembly module exposes a JavaScript API for writing policy on how data is stored on its Kafka-compatible streaming platform, and CEO Alex Gallego told us that’s because of both the flexibility and popularity of JavaScript with developers.

The flexibility is important for platform developers. “When you’re starting to design something new, the most difficult part is committing to a long-term API,” he noted. “Once you commit, people are going to put that code in production, and that’s it: you’re never going to remove that, you’re stuck with your bad decisions. What JavaScript allows you to do, from a framework developer perspective, is iterate on feedback from the community super-fast and change the interface relatively easily because it’s a dynamic language.”

With JavaScript, developers get a familiar programming model for business logic like masking social security numbers, finding users in specific age groups, or credit-scoring IP addresses — all without needing to be an expert in the intricacies of distributed storage and streaming pipelines. “The scalability dimensions of multithreading, vectorization instructions, IO, device handling, network throughput; all of the core gnarly things are still handled by the underlying platform.”

JavaScript: Popular and Performant

Appealing to developers is a common reason for enabling JavaScript support for writing WebAssembly modules.

When a new service launches, obviously developers won’t have experience with it; but because they know JavaScript, it’ll be much easier for them to get up to speed with what they want to do. That gives platforms a large community of potential customers, Gallego noted.

“It gives WebAssembly the largest possible programming community in the world to draw talent from!”

“WebAssembly allows you to mix and match programming languages, which is great. But in practical terms, I think JavaScript is the way to go. It’s super easy. It’s really friendly, has great packaging, there are a million tutorials for developers. And as you’re looking at expanding talent, right, which is challenging as companies grow, it’s much easier to go and hire JavaScript developers.”

“When it comes to finding the right design for the API that you want to expose, to me, leaning into the largest programming community was a pretty key decision.”

“JavaScript is one of the most widely used languages; it’s always very important because of adoption,” agreed Fastly’s Guy Bedford, who works on several projects in this space. “WebAssembly has all these benefits which apply in all the different environments where it can be deployed, because of its security properties and its performance properties and its portability. All these companies are doing these very interesting things with WebAssembly, but they want to support developers to come from these existing ecosystems.”

JavaScript has some obvious advantages, Bucher noted: “the low barrier to entry, the huge variety of readily available resources to learn it, the unbelievably gigantic number of off-the-shelf libraries that you can pull through npm.”

JavaScript could become the SQL equivalent for Wasm

Libraries are a big part of why using JavaScript with WebAssembly will be important for functionality as well as adoption. “If you’ve developed a library that’s very good at matrix multiplication, you really want to leverage the decade of developer hours that it took you to build that library.” With those advantages, JavaScript could become the SQL equivalent for Wasm, Gallego suggested.

The 20 years of optimization that JavaScript has had are also a big part of the appeal. “There’s so much money being poured into this ecosystem,” he points out. “Experts are very financially motivated to make sure that your website renders fast.” The programming team behind the V8 JavaScript engine includes the original creator of Java’s garbage collector. “The people that are focused on the performance of JavaScript are probably the best people in the world to focus on that; that’s a huge leg over from anything else.”

“I think that’s why JavaScript continues to stay relevant: it’s just the number of smart, talented people working on the language not just at the spec level, but also at the execution level.”

“Single thread performance [in JavaScript] is just fantastic,” he noted: that makes a big difference at the edge, turning the combination of WebAssembly and JavaScript into “a real viable vehicle for full-blown application development”.

Similarly, Butcher mused about the server-side rendering of React applications on a WebAssembly cloud to cater to devices that can’t run large amounts of JavaScript in the browser.

“V8 has all of these great performance optimizations,” he agreed. “Even mature languages like Python and Ruby haven’t had the same devoted attention from so many optimizers [as JavaScript] making it just a little bit faster, and just a little more faster.”

“The performance has been pretty compelling and the fact that it’s easy to take a JavaScript runtime and drop it into place… I looked at that and of course, people would want a version that would run in WebAssembly. They can keep reaping the same benefits they’ve had for so long.”

But WebAssembly isn’t quite ready for mainstream JavaScript developers today.

“JavaScript has this low barrier to entry where you don’t have to have a degree or a bunch of experience; it’s a very accessible language. But if you’re a JavaScript developer and you want to be using WebAssembly it’s not easy to know how to do that,” Bedford warned.

Different Ways to Bring JavaScript to Wasm

You can already use JavaScript to write WebAssembly modules, but “there are significant updates coming from the Bytecode Alliance over the next few months that are going to enable more JavaScript,” Cosmonic CEO Liam Randall told us.

“When we think about what the big theme for WebAssembly is going to be in 2023, it really comes down to components, components, components.”

“There have been significant advancements this year in the ability to build, create and operate components and the first two languages down the pipe are Rust and some of this JavaScript work,” Randall continued.

Currently, the most popular approach is to use the very small (210KB) QuickJs interpreter originally adopted or popularised by Shopify, which is included in a number of WebAssembly runtimes. For example, Shopify’s Javy and Fermyon’s spin-js-sdk use Quickjs with the Wasmtime runtime (which has early bindings for TypeScript but doesn’t yet include JavaScript as an officially supported language) and there’s a version of QuickJS for the CNCF’s WasmEdge runtime that supports both JavaScript in WebAssembly and calling C/C++ and Rust functions from JavaScript.

QuickJs supports the majority of ECMAScript 2020 features, including strings, arrays, objects and the methods to support them, async generators, JSON parsing, RegExps , ES modules and optional operator overloading, big decimal (BigDecimal) and big binary floating point numbers (BigFloat). So it can run most JavaScript code. As well as being small, it starts up fairly quickly and offers good performance for running JavaScript — but it doesn’t support JIT.

Using QuickJs is effectively bundling in a JavaScript runtime and there’s a tradeoff for this simplicity Hayes noted: “you typically have a little bit larger size and maybe the performance isn’t as perfect as it could be — but it works in most cases, and I’ve been seeing it get adopted all over.”

Fermyon’s JavaScript SDK builds on the way Javvy uses QuickJs but uses the Wizer pre-initializer to speed up the QuickJs startup time by saving a snapshot of what the code will look like once it’s initialized. “Wizer is what makes .NET so fast on WebAssembly,” Butcher explained. “It starts off the runtime, loads up all the runtime environment for .NET and then writes it back out to disk as a new WebAssembly module. We discovered we can do the same thing with QuickJs.”

“When you run your spin build, the SDK takes the JavaScript runtime, takes your source files, optimizes it with WIZER and then packages all of that up and ships that out a new WebAssembly binary.”

If the idea of getting a speed boost by pre-optimizing the code for an interpreted language sounds familiar, that’s because it’s the way most of the browser JavaScript engines work. “They start interpreting the JavaScript but while they’re interpreting, they feed in the JavaScript files to an optimizer so that a few milliseconds into execution, you flip over from interpreted mode into the compiled optimized mode.”

“One of the biggest untold stories is how much, at the end of the day, WebAssembly really is just everything we’ve learned from JavaScript, Java, .NET — all the pioneering languages in the 90s,” Butcher suggested. “What did we learn in 15-20 years of doing those languages and how do we make that the new baseline that we start with and then start building afresh on top of that?”

Adding JIT

Shopify also contracted Igalia to bring SpiderMonkey, the Mozilla JavaScript engine, to Wasm; while Fastly (which has a number of ex-Mozilla staff) has taken an alternative approach with compontentize-js, using SpiderMonkey to run JavaScript for WebAssembly in the high-speed mode it runs in the browser, JIT compiling at least part of your JavaScript code and running it inside the WebAssembly interpreter.

Although WebAssembly modules are portable enough to use in many different places, it’s not yet easy to compose multiple Wasm modules into a program (as opposed to writing an entire, monolithic program in one source language and then compiling that into a single module). Type support in Wasm is primitive, the different WebAssembly capabilities various modules may require are grouped into different “worlds” (like web, cloud and the CLI) and modules typically define their own local address space.

“The problem with WebAssembly has been that you get this binary, but you’ve got all these very low-level binding functions and there’s a whole lot of wiring process. You have to do that wiring specifically for every language and it’s a very complex marshaling of data in and out, so you have to really be a very experienced developer to be able to know how to handle this,” Bedford told us.

The WebAssembly component model adds dependency descriptions and high-level, language-independent interfaces for passing values and pointers. These interfaces solve what he calls “the high-level encapsulation problem with shared nothing completely separated memory spaces.”

“You don’t just have a box, you have a box with interfaces, and they can talk to each other,” he explained. “You’re able to have functions and different types of structs and object structures and you can have all of these types of data structures passing across the component boundary.”

That enables developers to create the kind of reusable modules that are common in JavaScript, Python, Rust and other languages.

Compontentize-js builds on this and allows developers to work with arbitrary bindings. “You bring your bindings and your JavaScript module that you want to run and we give you a WebAssembly binary that represents the entire JavaScript runtime and engine with those bindings. We can do that very quickly and we can generate very complex bindings.”

This doesn’t need a lot of extra build steps for WebAssembly: JavaScript developers can use familiar tooling, and install the library from npm.

Although the SpiderMonkey engine size is larger than QuickJs — Bedford estimates a binary with the JavaScript runtime and a developer’s JavaScript module will be 5-6MB — that’s still small enough to initialize quickly, even on the kind of hardware that will be available at the edge (where Fastly’s platform runs).

Again, this uses Wizer to optimize initialization performance, because that affects the cold start time. “We pre-initialize all of the JavaScript up until right before the point where it’s going to call your function, so there’s no JavaScript engine initialization happening. Everything is already pre-initialized using Wizer.”

“You’re just calling the code that you need to call so there’s not a whole lot of overhead.”

That isn’t AOT (Ahead Of Time) compilation, but later this year and next year, componentize-js will have more advanced runtime optimizations using partial evaluation techniques that Bedford suggested will effectively deliver AOT. “Because you know which functions are bound you can partially evaluate the interpreter using Futamura projections and get the compiled version of those functions as a natural process of partially evaluating the interpreter in SpiderMonkey itself.”

Compontentize-js is part of a larger effort from the Bytecode Alliance called jco — JavaScript components tooling for WebAssembly — an experimental JavaScript component toolchain that isn’t specific to the JavaScript runtime Fastly uses for its own edge offering. “The idea was to build a more generic tool, so wherever you’re putting WebAssembly and you want to allow people to write a small bit of JavaScript, you can do it,” Bedford explained.

Jco is a project “where you can see the new JavaScript experience from stem to stern”, Randall noted, suggesting that you can expect to see more mature versions of the JavaScript and Rust component work for the next release of wasmtime, which will be aligned with WASI Preview2. It’s important to note that this is all still experimental — there hasn’t been a full release of the WebAssembly component model yet and Bedford refers to componentize-js as research rather than pre-release software: “this is a first step to bring this stuff to developers who want to be on the bleeding edge exploring this”.

The experimental slightjs is also targeting the WebAssembly component model, by creating the Wasm Interface Types (WIT) bindings that lets packages share types and definitions for JavaScript. So far the wit-bindgen generator (which creates language bindings for programs developers want to compile to WebAssembly and use with the component mode) only supports compiled languages — C/C++, Rust, Java and TinyGo — so adding an interpreted language like JavaScript may be challenging.

While spin-js-sdk produces bindings specifically for Spin HTTP triggers, SlightJs aims to create bindings for any WIT interface a developer wants to use. Eventually, it will be part of Microsoft’s SpiderLightning project, which provides WIT interfaces for features developers need when building cloud native applications, adding JavaScript support to the slight CLI for running Wasm applications that use SpiderLightning.

Currently, SlightJS uses QuickJs because the performance is better, but as the improvements to SpiderMonkey arrive it could switch and Butcher pointed out the possible performance advantages of a JIT-style JavaScript runtime. QuickJs itself has largely replaced an earlier embeddable JavaScript engine, Duktape.

“There’s a real explosion of activity,” Bedford told us: “there’s very much a sense of accelerating development momentum in this space at the moment.”

Improving JavaScript and Wasm Together

You can think of these options as “JavaScript script on top and WebAssembly on the bottom,” suggested Daniel Ehrenberg, vice president of the TC39 ECMAScript working group, but another approach is “JavaScript and WebAssembly side by side with the JavaScript VM beneath it”.

The latter is where Bloomberg and Igalia have been focusing, with proposals aimed at enabling efficient interaction between JavaScript and WebAssembly, like reference-typed strings to make it easier for WebAssembly programs to create an consume JavaScript strings, and WebAssembly GC for garbage collection to simplify memory management.

Making strings work better between the two languages is about efficiency, TC39 co-chair and head of Bloomberg’s JavaScript Infrastructure and Tooling team Rob Palmer explained.

“This unlocks a lot of use cases for smaller scale use of WebAssembly [for] speeding up some small amount of computation.”

“At the moment they cannot currently really be efficient, because the overhead of copying strings in between the two domains outweighs the benefit of higher speed processing within WebAssembly.”

GC goes beyond the weak references and finalization registry additions to JavaScript (in ECMAScript 2021), which provide what Ehrenberg calls a bare minimum of interoperability between WebAssembly’s linear memory and JavaScript heap-based memory, allowing some Wasm programs to be compiled. The GC proposal is more comprehensive. “WebAssembly doesn’t just have linear memory; WebAssembly can also allocate several different garbage-collection-allocated objects that all point to each other and have completely automatic memory management,” Ehrenberg explains. “You just have the reference tracing and when something’s dead, it goes away.”

Work on supporting threads in WASI to improve performance through parallelization and give access to existing libraries is at an even earlier stage (it’s initially only for C and it isn’t clear how it will work with the component model) but these two WebAssembly proposals are fairly well developed and he expects to see them in browsers soon, where they will help a range of developers.

“Partly that’s been enabling people to compile languages like Kotlin to WebAssembly and have that be more efficient than it would be if it were just directly with its own memory allocation, but it also enables zero-copy memory sharing between JavaScript and WebAssembly in this side-by-side architecture.”

For server-side JavaScript, Ehrenberg is encouraged by early signs of better alignment between two approaches that initially seemed to be pulling in different directions: WinterCG APIs (designed to enable web capabilities in server-side environments) and WASI, which aims to offer stronger IO capabilities in WebAssembly.

“You want WinterCG APIs to work in Deno but you also want them to work in Shopify’s JavaScript environment and Fastly’s JavaScript environment that are implemented on top of WebAssembly using WASI,” he pointed out. “Now that people are implementing JavaScript on top of WebAssembly, they’re looking at can JavaScript support the WinterCG APIs and then can those WinterCG APIs be implemented in WASI?”

The Promise of Multilanguage Wasm 

The flexibility of JavaScript makes it a good way to explore the componentization and composability that gives the WebAssembly component model so much promise, embryonic as it is today.

Along with Rust, JavaScript will be the first language to take advantage of a modular WebAssembly experience that Randall predicted will come to all languages, allowing developers to essentially mix and match components from multiple WebAssembly worlds in different languages and put them together to create new applications.

“You could use high performance and secure Rust to build cloud components, much like wasmCloud does, and you could pair that with less complicated to write user-facing code in JavaScript. I could take JavaScript components from different worlds and marry them together and I could take cargo components written in Rust, and I can now recompose those in many different ways.”

“You can have Rust talking to JavaScript and you can be running it in the sandbox or you could have a JavaScript component that’s alerting a highly optimized Rust component to do some heavy lifting, but you’re writing the high-level component that’s your edge service in JavaScript,” agreed Bedford.

The way compontentize-js lets you take JavaScript and bundle it as a WebAssembly component will translate to working in multiple languages with the Jco toolchain and equivalent tools like cargo-component that also rely on the component model.

Despite WebAssembly’s support for multiple languages, using them together today is hard.

“You have to hope that someone’s going to go and take that Rust application and write some JavaScript — write the JavaScript bindgen for it and then maintain that bindgen,” Beford explained. “Whereas with the component model, they don’t even need to think about targeting JavaScript in particular; they can target the component model, making this available to any number of languages and then you as a JavaScript developer just go for it.”

“That’s what the component model brings to these workflows. Someone can write their component in Rust and you can very easily bring it into a JavaScript environment. And then [for environments] outside the browser you can now bring JavaScript developers along.”

That will also open up JavaScript components for Rust developers, he noted. “Jco is a JavaScript component toolchain that supports both creating JavaScript components and running components in JavaScript.”

In the future, the wasm-compose library “that lets you take two components and basically smoosh them together” could help with this, Hayes suggested. As the component model becomes available over the next few years, it will make WebAssembly a very interesting place to explore.

“If you support JavaScript and Rust, you’ve just combined two massive language ecosystems that people love, and now they can interop and let people just pick the best library or tool.”

“I’m so excited about WebAssembly components because, in theory, it should break down the silos that we’ve created between frontend and backend engineers and language ecosystems.”

Group Created with Sketch.
TNS owner Insight Partners is an investor in: Deno, fermyon, Docker, SingleStore.
THE NEW STACK UPDATE A newsletter digest of the week’s most important stories & analyses.