← Blog

jsg: a small helper for ruby.wasm projects

ruby.wasm runs CRuby in the browser, and the js gem is the bridge between your Ruby code and the JavaScript world. Getting a project off the ground still involves a few manual steps: an HTML page, the browser script, a Gemfile, a build command and a local web server.

In 2024 I wrapped those steps into a small gem called jsg. Its gemspec sums it up in one line: “JSG helps setting up ruby.wasm projects and comes with a nicer syntax”. So there are two halves: a command line tool that sets up and builds a project, and a Ruby file that you load in the browser.

A fair warning first: jsg is an early experiment. The current version is 0.1.2 from May 2024, the README on GitHub is still the Bundler placeholder, and there is no real test suite yet. This post is the closest thing to documentation it has.

Installation

jsg needs Ruby 3.0 or newer and depends on the js gem (~> 2.6) and erb:

gem install jsg

The command line tool

The gem ships a jsg executable with three commands:

jsg new myapp   # create a project from the template, then build it
jsg build       # bundle install, then build assets/ruby-app.wasm
jsg server      # serve the current directory with WEBrick

jsg new myapp copies the template into a new myapp folder. You get:

Right after copying, it changes into the folder and runs the build. jsg build does exactly two things: bundle install and rbwasm build -o assets/ruby-app.wasm. The comment in the template’s src/main.rb explains the idea: your code goes there and ends up inside the .wasm file after jsg build.

jsg server (or jsg serve) starts WEBrick on port 8080 and moves on to the next port if 8080 is already taken. The examples folder uses Ruby’s built-in one-liner instead, which works just as well:

ruby -run -e httpd . -p 8080

Both rely on WEBrick, which is no longer part of Ruby’s standard library since Ruby 3.0, so you may need a gem install webrick first.

The “nicer syntax”

The second half is jsg.rb. It defines a JSG module with a few shortcuts and adds a set of methods to JS::Object. This is the Ruby that the example page in the repository runs:

require 'jsg'

$window = JSG.global
$d = $window.document

$d.getElementById("spinner").style.display = "none"

$d.createElement("h2").tap {|e|
    e.innerText = "Examples"
    $d.body.appendChild(e)
}

Instead of JS.global[:document] you write $window.document, and you call DOM methods like you would in JavaScript. Here is what the source adds:

The template’s index.html also shows a trick for development: it redefines require_relative to load files over HTTP through JS::RequireRemote. The comment there says this is only needed if you want to use require_relative instead of jsg build.

Status

jsg is small and still rough around the edges. If you try it and something does not work, the source is short: one Ruby file for the browser side and one script in exe/jsg. Issues and pull requests are welcome on GitHub. The license is MIT.

ruby.wasm also shows up in another project of mine: the Hackety Hack port behind clogs has a browser build that runs CRuby compiled to WebAssembly.