Risor vs JavaScript

Embeddable JavaScript runtimes like goja (and its Sobek fork) power PocketBase, Grafana k6, and many production systems. As one developer put it, JavaScript "is not the most beloved language...but it's very, very familiar."

Risor is a different kind of tool: TypeScript-like syntax, purpose-built for embedding, with an opt-in sandbox where the host controls what's available.

Integration

JavaScript (via goja)

vm := goja.New()
vm.Set("user", user)
vm.Set("orders", orders)
val, err := vm.RunString(`
    const active = orders.filter(o => o.status === "active");
    const total = active.reduce((sum, o) => sum + o.amount, 0);
    total > 1000 ? "premium" : "standard";
`)

Risor

result, err := risor.Eval(ctx, script, risor.WithEnv(map[string]any{
    "user":   user,
    "orders": orders,
}))
let active = orders.filter(o => o.status == "active")
let total = active.map(o => o.amount) |> math.sum
if (total > 1000) { "premium" } else { "standard" }

Sandbox Model

JavaScript runtimes expose the JS standard library by default (Date, RegExp, JSON, Math). You restrict capabilities by removing globals (opt-out).

Risor starts with an empty environment. The host opts in to every capability and controls which language features are enabled.

Syntax

Developers who know JavaScript or TypeScript read Risor immediately. Intentional differences: == only (no type coercion), let only (no const/var), pipe operators, and try/catch as an expression.

let data = try { from_json(raw_input) } catch { {"items": []} }

Comparison

PropertyEmbedded JavaScriptRisor
Sandbox modelJS stdlib exposed (opt-out)Empty (opt-in)
Feature controlHost restricts globalsHost chooses which features to enable
SyntaxJavaScript (ES5.1+)TypeScript-like
Pipes / pattern matchingNoYes
try/catch as expressionNoYes
Ecosystem expectationsHigh (users expect Node.js APIs)None (purpose-built)

Performance

Goja's V8-style interpreter is faster than Risor for raw execution. On a simple expression, Goja runs in 0.6 µs versus Risor's 15.2 µs. On business logic, Goja finishes in 120 µs versus Risor's 273 µs (2.3×). For data processing at scale (10,000 records), Goja takes 2.42 ms versus Risor's 4.20 ms. (Full benchmarks.)

The tradeoff shows up in memory. For simple expressions the footprints are close: Goja at 3.3 KB, Risor at 2.2 KB. But for business logic, Goja allocates 114.0 KB per instance versus Risor's 7.6 KB (15×). If you're running thousands of script instances concurrently, that gap determines whether you fit in cache or thrash main memory.

ScenarioGoja (JS)Risor
Simple expression (warm)0.6 µs15.2 µs
Data processing (10k records)2.42 ms4.20 ms
Business logic (500 records)120 µs273 µs
Heap per instance (simple)3.3 KB2.2 KB
Heap per instance (business)114.0 KB7.6 KB

When to Choose Embedded JavaScript

  • Your users know JS specifically and familiarity is the top priority
  • You're on a platform that already uses an embedded JS runtime
  • You need general-purpose scripting and trust the code
  • You want AI agents to write scripts (JS dominates LLM training data)

When to Choose Risor

  • You want an opt-in sandbox with host-controlled feature access
  • You want purpose-built simplicity, not a general-purpose language in a sandbox
  • You don't want to manage the gap between "JavaScript" and "embedded JavaScript" (no fetch, no import, no npm)

Learn More


Risor © 2026 Deep Noodle