# ruby.wasm: CRuby compiled to WebAssembly, with no threads and no network

> ruby.wasm is the official port of CRuby to WebAssembly, packaged as npm builds for several Ruby versions and shipped as prebuilt binaries for two target triples. The two limitations that matter are stated without softening: the WASI target has no thread APIs and no networking, because WASI itself has neither yet.

**ruby/ruby.wasm** — ruby.wasm is a collection of WebAssembly ports of the CRuby.

- Repository: https://github.com/ruby/ruby.wasm
- Website: https://ruby.github.io/ruby.wasm/
- Stars: 876 · Forks: 68
- Language: Ruby
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/ruby-ruby-wasm

## What this is and what it is not

ruby.wasm is a collection of WebAssembly ports of CRuby, and it enables running Ruby applications in browsers, WASI compatible WebAssembly runtimes and edge computing platforms. The distinction from the many Ruby-to-JavaScript compilers is the important part, because those translate Ruby into JavaScript and then behave like JavaScript, with a different object model and a different set of available libraries. This is the actual interpreter and the actual standard library compiled to WebAssembly, so a Ruby program that behaves the same on a server behaves the same here, subject to what the host platform provides. The project is the one hosted by the Ruby project itself, the homepage is at ruby.github.io/ruby.wasm, and you can try it with no installation at all on TryRuby, which has a playground link wired to a CRuby 3.2.0dev build.

## Running Ruby in a browser page in four lines of HTML

The browser example is the smallest possible demonstration and shows how the interop works. You load the browser script bundle from a CDN and put Ruby in a script tag with the text/ruby type, then require the js library to reach the host page.

```html
<html>
  <script src="https://cdn.jsdelivr.net/npm/@ruby/4.0-wasm-wasi@2.10.1/dist/browser.script.iife.js"></script>
  <script type="text/ruby">
    require "js"

    puts RUBY_VERSION # (Printed to the Web browser console)
    JS.global[:document].write "Hello, world!"
  </script>
</html>
```

The two lines to look at are the require of the js library and the JS.global access to the document. That library is the bridge: it exposes the host JavaScript environment to the Ruby code, so document.write is a call into the page. Everything else in the script is ordinary Ruby. The puts goes to the browser console rather than to a page element, which is a small detail worth noticing, because output routing is one of the things that differs between a browser and a terminal.

## Packaging a Ruby app as a WASI component

The other direction is packaging your own application as a WASI program, and it needs wasmtime as the runtime. The sequence starts with installing the ruby_wasm gem, then downloading a prebuilt Ruby release and extracting it, and then moving the ruby binary out of the way so it does not pack itself.

```console
$ gem install ruby_wasm
# Download a prebuilt Ruby release
$ curl -LO https://github.com/ruby/ruby.wasm/releases/latest/download/ruby-4.0-wasm32-unknown-wasip1-full.tar.gz
$ tar xfz ruby-4.0-wasm32-unknown-wasip1-full.tar.gz

# Extract ruby binary not to pack itself
$ mv ruby-4.0-wasm32-unknown-wasip1-full/usr/local/bin/ruby ruby.wasm

# Put your app code
$ mkdir src
$ echo "puts 'Hello'" > src/my_app.rb

# Pack the whole directory under /usr and your app dir
$ rbwasm pack ruby.wasm --dir ./src::/src --dir ./ruby-4.0-wasm32-unknown-wasip1-full/usr::/usr -o my-ruby-app.wasm

# Run the packed scripts
$ wasmtime my-ruby-app.wasm /src/my_app.rb
Hello
```

The rbwasm pack command is the interesting part. It takes the interpreter binary plus two directory mappings, one for your application at /src and one for the Ruby installation under /usr, and produces a single .wasm file that contains both. The result runs under wasmtime as a WASI component, taking the script path as an argument. One file, self-contained, no Ruby installation on the target machine.

## Two target triples and two profiles

The prebuilt binaries in GitHub Releases are described by a build, which is a combination of Ruby version, profile and target. The target triples are wasm32-unknown-wasip1, targeting WASI preview1 compatible environments such as Node.js, browsers with a polyfill and wasmtime, and wasm32-unknown-emscripten, targeting JavaScript environments including Node.js and browsers. The profiles are minimal and full. The minimal profile has no standard extension libraries, and the README names json, yaml and stringio as examples of what is missing. The full profile has all of them. So there is a size versus capability trade-off that you resolve at download time rather than at runtime, and the choice matters more than it might look: a minimal build that is missing the json library cannot parse a JSON configuration file, which is a fairly ordinary thing for a service to do.

## The npm packages, one per Ruby version

For JavaScript host environments the project publishes npm packages, one per Ruby version, and the README says to see the README of each package for detail and usage. There is @ruby/4.0-wasm-wasi for CRuby 4.0 built on WASI with JS interop support, then 3.4, 3.3 and 3.2 in the same shape, and @ruby/head-wasm-wasi for HEAD CRuby on WASI with JS interop support. There is also @ruby/head-wasm-emscripten for HEAD CRuby built on Emscripten, and that one is marked not well tested. The version spread is the point. A published package per released Ruby version means you can pin the interpreter to the version your application is tested against, rather than tracking whatever HEAD happens to be, and the HEAD packages exist for people who need a fix that has not shipped yet.

## Threads and networking are the two walls

The Notable Limitations section is short and blunt. The current WASI target build does not yet support Thread related APIs, specifically because WASI does not yet have an API for creating and managing threads. There is also no support for networking, and the README notes that networking is one of the goals of WASI to support in the future but is not yet implemented. Both of these are properties of the host standard rather than defects in the port, which is worth stating because it changes the shape of what you can build. A Ruby program that opens a socket, talks to a database driver or spawns a worker pool is not portable to this target. The realistic uses are computation, text processing and anything that reads from a filesystem or receives input through the host.

## How the project is built

The repository is a hybrid, and the layout reflects that. There is a Rust Cargo workspace at the root whose only member is ext/ruby_wasm, with a comment in the Cargo.toml explaining that the file exists so that external tools such as IDEs know this is a Rust project, and that extension dependencies belong in the ext directory Cargo.toml. The Ruby side is a gem, with ruby_wasm.gemspec, a Gemfile, a Rakefile and a rakelib directory, plus the exe directory for the command line tools and the bin directory. There is a sig directory with type signatures and a Steepfile, so the Ruby side is type checked, and a lib directory. The JavaScript side is a package.json with npm workspaces over packages/npm-packages, using Rollup, typedoc for the API documentation and Playwright for the tests, along with jco from the Bytecode Alliance for the JavaScript tooling. There is also a sbom.spdx.json, a software bill of materials, which is a sign the build output is treated as something that gets audited.

## Where ruby.wasm is the wrong choice

Four limits apply. It is not a replacement for a server runtime, because the absence of networking and threads rules out the whole class of application that opens a connection, and the WASI sandbox model means a program that wants a socket has to ask the host for one. It is not a drop-in for native Ruby either, since a gem that compiles a C extension will not compile to WebAssembly without an explicit port, and the npm packages ship a Ruby standard library rather than the ecosystem. The Emscripten build is the less mature of the two, with the head package explicitly marked not well tested. And the startup cost is real, because a WebAssembly interpreter is larger and slower to boot than a JavaScript implementation of the same logic, so if your goal is to run Ruby in a browser for a small utility, a Ruby-to-JavaScript transpiler will beat it on load time even though it is less faithful. The case for this project is faithfulness, and the case against it is everything else.

## Conclusion

Use ruby.wasm when you need real Ruby rather than a JavaScript lookalike, in a browser, a WASI runtime or an edge platform, because it is CRuby compiled to WebAssembly and not a reimplementation. Do not plan on threads or outbound network calls in the WASI build, since the project says neither is available. Verify first which target triple your host needs, because the WASI preview1 build and the Emscripten build differ in what they can do, and which profile you want, since the minimal profile ships without the standard extension libraries such as json, yaml and stringio, and that a pure-Ruby application with gems that need native extensions will not work in a WASI sandbox at all.

## FAQ

### What is ruby.wasm?

It is a collection of WebAssembly ports of CRuby, enabling Ruby applications to run in browsers, WASI compatible WebAssembly runtimes and edge computing platforms. You can try it with no installation on the TryRuby playground.

### How do I package a Ruby application as a WASI program?

Install the ruby_wasm gem, download and extract a prebuilt Ruby release, move the ruby binary out of the way so it does not pack itself, then run rbwasm pack with directory mappings for your app and the Ruby installation, producing a .wasm file you run with wasmtime.

### What are the differences between the minimal and full profiles?

The minimal profile has no standard extension libraries, with json, yaml and stringio given as examples of what is missing. The full profile has all the standard extension libraries. A build is a combination of Ruby version, profile and target.

### Does ruby.wasm support threads or networking?

Not currently. The README states the WASI target build does not yet support Thread related APIs because WASI has no API for creating and managing threads, and that there is also no support for networking, since that is a future goal of WASI that is not yet implemented.

### Which Ruby versions are available as npm packages?

There are packages for CRuby 4.0, 3.4, 3.3 and 3.2 built on WASI with JS interop support, plus a HEAD build on WASI and a HEAD build on Emscripten that is marked not well tested. The names follow the pattern @ruby/<version>-wasm-<target>.

## Sources

- [License: MIT](https://github.com/ruby/ruby.wasm/blob/main/LICENSE)
- [Project website](https://ruby.github.io/ruby.wasm/)
- [README](https://github.com/ruby/ruby.wasm/blob/main/README.md)
- [Releases](https://github.com/ruby/ruby.wasm/releases)
- [ruby/ruby.wasm on GitHub](https://github.com/ruby/ruby.wasm)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/ruby-ruby-wasm
