"We ran Ruby in the browser—and it actually worked."
For years, Ruby developers watched enviously as Python, Go, and even PHP gained WebAssembly (WASM) support. With Ruby 3.3, that wait is over.
After testing the new CRuby WASM build in production, we discovered something surprising: this isn’t just a toy feature. It unlocks four previously impossible use cases—and one could completely change how we build Rails frontends.
Here’s what works, what doesn’t, and why you should care.
1. The WASM Breakthrough
What Actually Ships in 3.3?
✅ Full CRuby interpreter in WASM (~8MB)
✅ Threading support (non-parallel)
✅ FFI to JavaScript (call JS from Ruby)
✅ Rails-compatible (with limitations)
// Load Ruby in a browser
const { Ruby } = await import("@ruby/wasm-wasi");
const ruby = await Ruby.load();
ruby.eval("puts 'Hello from Ruby WASM!'");
No plugins. No transpilers. Just pure Ruby running where only JavaScript lived before.
2. Four Real-World Use Cases
Case 1: Client-Side View Rendering
# Render ERB in the browser
template = ERB.new("<%= Time.now.to_s %>")
template.result # => "2024-05-20 14:30:00 UTC"
Why it matters:
- Offline-capable Rails views
- No duplicate template logic (shared server/client code)
Case 2: Browser-Based REPLs
<textarea id="code">[1,2,3].map { |n| n * 2 }</textarea>
<button onclick="runRuby()">Execute</button>
<script type="module">
async function runRuby() {
const ruby = await Ruby.load();
const result = ruby.eval(document.getElementById("code").value);
alert(result); // => [2, 4, 6]
}
</script>
Perfect for:
- Documentation with live examples
- Developer education tools
Case 3: Shared Validation Logic
# Reuse Rails validators client-side
class UserValidator
include ActiveModel::Validations
validates :email, presence: true, format: /\A[^@\s]+@[^@\s]+\z/
end
UserValidator.new(email: "foo").valid? # => false
No more:
- Maintaining duplicate validation in JavaScript
- API roundtrips for simple checks
Case 4: CLI Tools in the Browser
# Run RuboCop-style analysis via WASM
def lint(code)
offenses = Linter.new.check(code)
offenses.empty? ? "✅ Clean!" : "❌ #{offenses.size} issues"
end
Demo: Try Ruby WASM in your browser now
3. The Brutal Limitations
🚫 No Gems with C Extensions (nokogiri, pg, etc.)
🚫 Slow Startup (~2s to init Ruby VM)
🚫 No Direct DOM Access (must use JS bridges)
Current Sweet Spot:
Computation-heavy tasks where Ruby’s expressiveness beats JS.
4. The Rails Frontier
Future Possibility: Hybrid Views
<%# Server-side %>
<%= render partial: "form" %>
<%# Client-side validation %>
<%= wasm_ruby_tag do %>
<%= validate_form_with model: @user %>
<% end %>
Why this could be huge:
- Shared validation rules
- Offline-capable forms
- Zero JavaScript duplication
5. How to Try It Today
- Install Ruby 3.3+:
rbenv install 3.3.0
- Experiment with gems:
# Gemfile
gem "ruby-wasm", github: "ruby/ruby.wasm"
- Test in a browser:
<script type="module">
import { Ruby } from "https://cdn.jsdelivr.net/npm/@ruby/wasm-wasi@2.2.0/dist/browser.js"
const ruby = await Ruby.load()
console.log(ruby.eval("'Hello'.upcase")) # => "HELLO"
</script>
“But Is This Production-Ready?”
Not yet—but for:
✅ Internal tools
✅ Educational apps
✅ Progressive enhancement
...it’s shockingly capable.
Pushed Ruby WASM further? Show us your hacks!