QuickBEAM: A JavaScript Runtime for the BEAM VM, JavaScript Meets Erlang's OTP

QuickBEAM: A JavaScript Runtime for the BEAM VM, JavaScript Meets Erlang's OTP

lschvn5 min read

There's a long-running joke in the Erlang community: "Why do Erlang developers laugh at other languages? Because their processes have supervisors watching them." The BEAM VM's fault-tolerance model, where crashed processes are automatically restarted by supervisors, where errors in one process don't bring down others, where hot code reloading is built into the runtime, is genuinely different from what most developers are used to.

QuickBEAM (at github.com/elixir-volt/quickbeam) puts JavaScript inside that model. It runs JavaScript runtimes as BEAM GenServer processes, integrates them into OTP supervision trees, and lets JavaScript code call Elixir functions and OTP libraries directly.

What QuickBEAM Actually Is

At its core, QuickBEAM is a BEAM GenServer that wraps a JavaScript runtime (QuickJS-NG, embedded as an Erlang NIF). Each JS runtime is a process in the BEAM VM's scheduler, with all the guarantees that entails. You start a runtime:

{:ok, rt} = QuickBEAM.start()
{:ok, 3} = QuickBEAM.eval(rt, "1 + 2")
{:ok, "HELLO"} = QuickBEAM.eval(rt, "'hello'.toUpperCase()")

State persists across calls within the same runtime, and you can call named JavaScript functions:

QuickBEAM.eval(rt, "function greet(name) { return 'hi ' + name }")
{:ok, "hi world"} = QuickBEAM.call(rt, "greet", ["world"])

JavaScript Meets OTP

The interesting part is the bridge between JavaScript and the surrounding BEAM ecosystem. You register Elixir handlers when starting the runtime:

{:ok, rt} = QuickBEAM.start(handlers: %{
  "db.query" => fn [sql] -> MyRepo.query!(sql).rows end,
  "cache.get" => fn [key] -> Cachex.get!(:app, key) end,
})

{:ok, rows} = QuickBEAM.eval(rt, """
  const rows = await Beam.call("db.query", "SELECT * FROM users LIMIT 5");
  rows.map(r => r.name);
""")

JavaScript can also send messages to any BEAM process, monitor processes for exit, and be linked bidirectionally, so a JavaScript crash can trigger a supervisor restart just like any other OTP process.

Built-In TypeScript Toolchain

QuickBEAM ships with a built-in TypeScript toolchain, which is notable because it makes TypeScript a first-class citizen in the BEAM ecosystem rather than an afterthought. The project targets Zig 0.15+ and distributes via Hex:

def deps do
  [{:quickbeam, "~> 0.7.1"}]
end

Supervision Trees for JavaScript

The practical benefit of all this is supervision trees that include JavaScript components. If a JavaScript runtime crashes, OTP's supervision strategy handles recovery, restart the process, potentially on a different BEAM node in a distributed setup. For applications that need the reliability of BEAM but have components written in JavaScript, this is a direct answer.

You can also use QuickBEAM.ContextPool to create a pool of JS runtime contexts for high-concurrency scenarios, multiple runtimes shareable across requests.

The Niche

QuickBEAM isn't trying to replace Node.js or Deno. It's a specialized runtime for Elixir/Erlang teams that want to write some components in JavaScript without abandoning the BEAM reliability model. The integration surface, Beam.call(), process messaging, supervision, is the point, not the runtime itself.

For the broader JavaScript ecosystem, it's an existence proof that BEAM's concurrency model is accessible from JavaScript. Whether it finds a real user base depends on whether teams building high-reliability systems in Elixir see enough value in writing specific components in JavaScript rather than Elixir itself.

The project is at version 0.7.1 and actively developing. If you're running Elixir in production and have JavaScript code you wish you could trust more, it's worth a look.

Frequently Asked Questions

Related articles

More coverage with overlapping topics and tags.

Deno 2.8 Ships Audit Fix, CI Subcommand, and Native Pack Tool
runtimes

Deno 2.8 Ships Audit Fix, CI Subcommand, and Native Pack Tool

Deno 2.8 drops with four new CLI subcommands, improved Node.js compatibility, and a Rust-based package packager targeting npm registries.
Bun v1.3.12 Ships Headless Browser Automation and Native Explicit Resource Management
runtimes

Bun v1.3.12 Ships Headless Browser Automation and Native Explicit Resource Management

Bun's latest release adds WebView for headless browser automation, lands TC39's using/await using in JavaScriptCore, and delivers a 2.3x speedup to URLPattern.
Deno 2.7 Stabilizes the Temporal API, Adds Windows ARM Support and npm Overrides
runtimes

Deno 2.7 Stabilizes the Temporal API, Adds Windows ARM Support and npm Overrides

Deno 2.7 is a substantial mid-cycle release: the Temporal API is now production-ready, native Windows on ARM builds land, npm overrides work like in Node, and dozens of Node.js compatibility improvements land across worker_threads, child_process, zlib, and sqlite.

Comments

Log in Log in to join the conversation.

No comments yet. Be the first to share your thoughts.