JS spec compatibility validated against Bun and Node over 679 standalone TS fixtures.
[β°β°β°β°β°β°β°β°β°β°β°β°β°β°β±β±β±β±β±β±] 72.5% 479/661 fixtures passing
| Metric | Value |
|---|---|
| Parity | 72.5% (479/661) |
| β RTS = Bun = Node | 479 |
| β RTS diverges | 97 |
| π₯ RTS runtime error | 85 |
| π οΈ Left to fix | 182 |
| 18 | |
| π« Rejected (RTS-only) | 0 |
| π¦ Total fixtures | 679 |
Updated: 2026-07-24 β how to add a fixture
RTS is a compiler + runtime that takes your .ts and spits out a native .exe.
It is not a transpiler, not a bundler, not a wrapper around V8 β it is Cranelift
generating machine code directly from the SWC AST, with a minimal Rust runtime
and a typed, boxing-free ABI.
Two paths, same codegen:
| Mode | Command | What it does |
|---|---|---|
| π JIT | rts run app.ts |
Compiles to executable memory and runs. Zero disk. |
| π¦ AOT | rts compile -p app.ts out |
Object file β linker β standalone binary (~3 KB). |
Context. The engine was rewritten from scratch on a sound value model (
PolyValueNaN-box + shapes + inline caches) and the campaign so far has been correctness-first (parity badge above). The deleted old engine's peak (Monte Carlo AOT 16.9 ms, 5.4Γ faster than Bun; HTTP 29k req/s) is the documented performance target to re-clear, not the current state. Numbers below are end-to-end process time (startup included β AOT runtime init is ~70 ms of every figure) measured now on the new engine.
End-to-end process time (includes startup/JIT compile), median of 20 runs after 3 warmups, GitHub Actions windows-latest β commit 777e6e3.
| Bench | Bun | Node | Deno | RTS JIT | RTS AOT | AOT vs Bun | AOT vs Node |
|---|---|---|---|---|---|---|---|
| Monte Carlo Ο 10M (same xorshift algorithm) | 4.81 s | 7.42 s | 3.58 s | 220 ms | 131 ms | 36.74Γ | 56.65Γ |
Monte Carlo Ο 10M (JS Math.random) |
118 ms | 267 ms | 214 ms | 218 ms | 126 ms | 0.94Γ | 2.12Γ |
| Ο decimal ~30 digits (i128 vs BigInt) | 50 ms | 55 ms | 40 ms | 124 ms | 31 ms | 1.60Γ | 1.77Γ |
| Monte Carlo 10M threaded (vs Bun Workers) | 164 ms | β | β | 195 ms | 107 ms | 1.54Γ | β |
| Ο Machin f64 (RTS only) | β | β | β | 118 ms | 31 ms | β | β |
Updated: 2026-07-24 β run locally with powershell -File bench/benchmark.ps1
Why native wins (and where the work is). RTS compiles TS to machine
code via Cranelift β no JIT warmup, no interpreter tier, native 64-bit
integer arithmetic JS engines can't touch without BigInt. The PolyValue
NaN-box only pays where code is actually polymorphic and the Cranelift
egraph folds redundant box/unbox away β the design that made the old
engine 5Γ faster than Bun is intact; wiring the new engine's hot paths
back to it (plus cutting the ~70 ms AOT startup) is the tuning phase after
the parity campaign.
40+ namespaces today β being reshaped into per-module rts:* imports
(camelCase, JS globals for everything the language already covers): see
docs/specs/rts-std-surface.md. No
dependency on OpenSSL, schannel, libuv, or any external runtime.
| Family | Namespaces |
|---|---|
| I/O & FS | io fs path process env os |
| Compute | math num bigfloat fmt hash crypto |
| Memory | gc buffer mem alloc ptr ffi |
| Concurrency | thread atomic sync parallel |
| Network | net tls http_server (embedded actix-web) |
| Data | collections string regex json date |
| Async | events (EventEmitter), native Promise + Function |
| Meta | runtime test trace hint |
π Painless HTTPS: rustls + webpki-roots (Mozilla CAs embedded in the binary)
π§΅ Threading: 4 coexisting mechanisms β spawn/join, spawn_async,
spawn_detached (8-worker pool, 5M spawn/s), scope auto-join
π Shard-aware HandleTable: 32 mutually lock-free shards
The mascot above is an HTML page β no images: just CSS boxes + @keyframes, rendered and animated by the engine.
RTS has its own HTML+CSS render engine, in pure Rust (crates/rts-dom),
following the canonical browser pipeline: DOM β CSS cascade β layout (x,y,w,h) β display list β paint. The backend (egui/wgpu) only paints β the DOM owns
everything, headless and testable without a window.
What already renders faithfully (validated number by number against real Chrome,
getBoundingClientRect element by element β the Bootstrap cover and the .row/.col
grid come out pixel-perfect):
- Full cascade (specificity,
!important, inheritance,@mediaper viewport) with per-element custom properties (var()with per-component override) - Flexbox:
grow/shrink/basis,align-self,order, real stretch, column with absorbingmargin:auto, gap, wrap - Units:
rem/em/%/vw/vhandcalc()(fluid typography), negative margins,box-sizing - Flow: rich inline (links/bold flowing inside a paragraph),
float,position: absolute/fixedv1, scroll containers with their own scrollbars - CSS animations:
@keyframes+transitionwith full easing β colors, sizes, and margins interpolating (the vulture above flaps its wings with this) - External resources: local
<link rel="stylesheet">+@import
Test any local page with one line:
rts run examples/view.ts examples/urubu.html # the mascot
rts run examples/view.ts examples/bootstrap-5.3.8-examples/cover/index.html # real Bootstrap
rts run examples/view.ts path/to/your/index.html # your siteFull state and technical backlog: issue #1793.
β
Control flow β if/else, while, do-while, for, for-of/for-in
(real iterator protocol with IteratorClose on break), switch
(native jump table via br_table when all cases are integer literals),
labeled break/continue
β
Functions β declaration, expression, arrow, closures with mutable
capture (cells), tail call optimization (return f(x) becomes
return_call), first-class function pointers, call/apply/bind/toString,
new Function (runtime compile), spread call f(...args)
β
Classes β constructor, methods, this, extends, super(...),
super.method(...), static methods/fields + static {}, getters/setters,
real C.prototype + constructor.name, shape-keyed virtual dispatch,
private fields #x, Rust-style operator overload (a + b becomes
a.add(b) at compile time)
β
Generators & async β function*/yield/yield* (lazy state
machine), async generators + for await, async/await with a real
microtask queue (.then chains in spec order), Promise combinators
(all/allSettled/race/any), thenable adoption, withResolvers
β
Objects β hidden-class shapes + inline caches, getters/setters in
literals, computed keys, spread, Object.* statics, property descriptors,
freeze/seal, prototype chain, Proxy (get/set/delete/apply/ownKeys traps)
Reflect,Symbol(+ well-known,Symbol.iteratorprotocol)
β
Data β TypedArrays/ArrayBuffer/DataView/SharedArrayBuffer +
Atomics, Map/Set/WeakMap/WeakSet (TS stdlib), WeakRef/
FinalizationRegistry (strong interim, #217), JSON (+JSON5), Date,
RegExp (exec/matchAll/named groups/d flag), big decimal (bigfloat,
i128 fixed-point ~30 digits), full destructuring (incl. assignment
targets), template literals + tagged templates + String.raw
β
Errors β try/catch/finally, real throw/instanceof over the Error
family, error slot + pending-error unwind at call edges
β
Web/Node surface β fetch/Headers/FormData/Blob/streams, URL,
TextEncoder/Decoder, timers + microtasks, EventTarget/AbortController,
console, node:fs/os/path/process/crypto/util shims, N-API addons
(.node), import.meta
β Not there yet β decorators, full generics/type-checker, real weak
semantics (#217), Intl (partial), full BigInt semantics, real async
event loop for every path (#207), var hoisting edge cases (#301)
The cutover happened. The old engine (
rts-codegen-old) and the MIR tier (rts-mir) are deleted βcrates/rts-codegen-new/is the only engine (single HIRβCranelift path;PolyValueNaN-boxed value model incrates/rts-adapters/; hidden-class shapes + AOT-safe data inline caches; data-driven dispatch). Canonical design:docs/specs/rts-codegen-new-design.md. Public-surface direction:docs/specs/rts-std-surface.md.
Cargo workspace in crates/. src/ is the facade of the rts bin (re-exports
the crates); real paths live under crates/<crate>/src/.
crates/
ββ rts-ast/ internal AST
ββ rts-parser/ SWC parse β AST (+ generator/async state-machine desugar)
ββ rts-diagnostics/ structured errors
ββ rts-engine/ β‘ heap GC + ABI contract (SPECS, AbiType, Intrinsic, symbols)
β + Registry/builder + global shape registry
ββ rts-hir/ typed HIR (I8..I128/F32/F64/Bool/Str/Handle/Array/Function/Class/Object/Any)
ββ rts-adapters/ value model β PolyValue (64-bit NaN-box), Repr lattice,
β shapes + data ICs, data-driven dispatch, runtime trampolines
ββ rts-codegen-new/ THE engine β front/run (single HIRβCranelift lowering),
β module_jit + module_aot, adapter_symbols (JIT table harvested from SPECS)
ββ rts-primitives/ PRIMORDIAL classes (String/Object/Array/Function/Promise/Boolean/Number/Errorβ¦)
ββ rts-shared/ non-primordial universal (collections/json/globals + stdlib/*.ts preludes)
ββ rts-std/ backend (io/net/tokio/console/promise/streams/audio)
ββ rts-runtime/ thin facade (pub use of the four above) + AOT staticlib
ββ rts-node/ node:* shims (fs, os, path, process, crypto, util)
ββ rts-napi/ N-API (.node addons) via libloading + HandleTable
ββ rts-dom/ headless HTML+CSS engine (DOM β cascade β layout β display list)
ββ rts-egui/ window/paint backend (egui/wgpu) + render/input namespaces
ββ rts-linker/ native link (system linker + object fallback, per-target archives)
ββ rts-cli/ run Β· compile Β· apis Β· init Β· repl Β· eval Β· ir
TS β SWC β AST β HIR (rts-hir) β lower/ (HIR β Cranelift IR, ONE path) β Cranelift egraph β JIT/AOT
There is no MIR tier and no dual AST/MIR in the new engine. The Cranelift egraph
(use_egraphs=true) is the ONLY optimizer (const-fold, CSE, DCE, FMA, strength
reduction, intraprocedural inlining). The front-end only does what Cranelift cannot
(JS semantics): ToNumber/ToString/ToBoolean coercions, the polymorphic +,
box/unbox insertion (pure IR the egraph folds), shape/IC site emission,
narrow-int wrap, exception edges. AOT/JIT share compile_program
(FnCtx.module is &mut dyn Module).
PRIMORDIAL-vs-Registry doctrine (central to the new engine): the engine NAMES only
the primordial classes; everything else resolves via the data-driven Registry β no
non-primordial name hardcoded in the front, not even in an "allow-list" (see
CLAUDE.md Β§ anti-hardcode). The non-primordial globals (console,
Map/Set, JSON, Date) live as prelude .ts (rts-shared/stdlib/*.ts) and
call private engine.* bridges; the front does not name them.
Boxing-free ABI: each namespace function is a symbol
#[no_mangle] extern "C" fn __RTS_FN_NS_<NS>_<NAME>(...). No JsValue,
no central dispatcher. i64/f64 in native bits, strings as UTF-8
(ptr, len), opaque u64 handles for resources. In the new engine the JIT symbol
table is DERIVED from SPECS (abi_gen.rs), not manual add_fn!.
# Install
git clone https://github.com/UrubuCode/rts && cd rts
cargo build --release
# Run
./target/release/rts run examples/console.ts
# Compile to a binary (~3 KB, no runtime DLL)
./target/release/rts compile -p examples/console.ts hello
./hellorts run file.ts # in-memory JIT
rts compile -p file.ts out # AOT with use-based slicing
rts apis # list APIs registered in abi::SPECS
rts ir file.ts # dump Cranelift IR (for codegen debugging)
rts init my-app # project scaffoldingWant to see exactly what Cranelift is generating?
rts ir file.ts 2>&1 | head -50Prints the IR of every user fn + __RTS_MAIN without executing. Great for hunting
redundant loads/stores in hot loops, unnecessary extern calls, and
intrinsic opportunities. See CLAUDE.md Β§ Codegen debugging.
Honest number: the new engine's real cross-runtime parity is the block π Cross-runtime parity at the top (generated by CI against Bun+Node). The old engine hit 100% (372/372) at tag
v0.0-202606072107β a local maximum of a hardcoded approach on an unsound value model; the redesign exists to break through that wall, not to repeat the number. Do NOT quote "1015/1015"/"100%" as current state.
What the new engine already covers (under construction, parity climbing):
- Core syntax: classes (extends/super/static/getters/setters), destructuring, spread in literals, optional chaining, nullish coalescing, arrow/function expressions, template literals
- Async: Promise + async/await (synchronous path without
await; real event loop still open, #207) - JS globals as prelude
.ts(data-driven): Object + statics, Boolean/Number/String prototypes, Error family, console.*, Map/Set, JSON, Date β none named in the front - Operators: JS-spec division (
/ALWAYS f64 β44100/48000 === 0.91875, even when assigned to aconst), comparisons, ternary, bitwise, shifts - try/catch/finally phase 1 (thread-local error slot; finally runs and re-propagates the error correctly)
- Diagnostics: an unresolved identifier becomes a compile error, never a segfault β and never a wrong value (the redesign's soundness floor)
Heavy items still open (some in redesign phase): real async event loop (#207), closures with mutable capture (#195), TCO, Proxy (#218), typed arrays/DataView/ArrayBuffer, Symbol/Reflect/BigInt (#216/#219). Master JS/TS parity tracker: #226.
- π οΈ
CLAUDE.mdβ internal architecture + codebase rules (includes Β§ anti-hardcode) - π
docs/specs/β technical feature specs - πΊοΈ
docs/specs/rts-codegen-new-design.mdβ canonical plan of the engine redesign - π Issues: master JS/TS parity tracker at #226
- β No
xtaskβ the build is purecargo - β No runtime-support download at build time
- β No Rust/Cargo dependency in the AOT binary's final environment
- β Single distributed binary, runs on any Windows/Linux/macOS without installing anything
Made with π¦ by UrubuCode
If Bun is a rocket, RTS is a bird of prey.
