Tiered JIT, ownership memory, and no GC. The architecture doc explains how. Read it →
alpha Haxe 4.x, natively →

Instant Haxe.
Native performance.

A Haxe compiler that starts instantly, gets faster as it runs, and has no garbage collector.

Get started GitHub
Particles.hx
// hot loop compiled after ~1k calls
class Particles {
  static function update(p:Array<Particle>, dt:Float) {
    for (i in 0...p.length) {
      var q = p[i];
      q.vy += 9.81 * dt;
      q.x += q.vx * dt;
      q.y += q.vy * dt;
    }
  }

  static function main() {
    var world = Particle.spawn(100000);
    for (frame in 0...1200) update(world, 1/60);
  }
}
Try it in the playground → edit, compile and run in the browser
coming soon
$ rayzor run Particles.hx
tier 0 · interp first output at 38ms
profile update() hot, 1,024 calls
tier 1 · compiled compiled in 4.2ms
tier 3 · optimized compiled in 61ms, -O2
steady state 3.1× interpreter throughput
<50ms
to first output. The interpreter runs before anything is compiled
Tiered
starts interpreting at once, then compiles each function as it proves hot
Hot reload
swap a changed module into a running program without restarting it
0 GC
memory is freed at points the compiler works out while building your program
Native + wasm
native binaries and WebAssembly, optimized once and shared by both
Targets

Desktop, server, and the browser.

One codebase, one runtime, four places to put it. WebAssembly is a first-class target: it is built by the same compiler and the same optimizations as the native output, and ships as a core module, a WASI P2 component, or a browser bundle.

Linux
x86_64 · aarch64

Native binaries to ship, and an instant run-from-source loop while you work. Threads, sockets and the full runtime, plus NUMA-aware worker pinning on servers.

rayzor aot main.hx -o app
macOS
aarch64 · x86_64

The same native path on Apple silicon and Intel, with a worker pool that adapts to the machine instead of fighting it.

rayzor aot main.hx -o app
Windows
x86_64 · MSVC

Native binaries through the MSVC toolchain, with the same tiered JIT and the same runtime you develop against elsewhere.

rayzor aot main.hx -o app.exe
WebAssembly
first-class target

Core modules, WASI P2 components, and a browser harness, from the same program and the same optimizations as the native backends.

rayzor build --target wasm --browser
Cross-compile --target <triple> --sysroot <dir> --linker <path> --target wasm-wasi
Policy

Haxe is a systems language.
We compile it like one.

Haxe already has the type system, the macros and the ergonomics. What it hasn't had is a compiler that goes straight to machine code, frees memory without a collector, and optimizes once for every backend. Rayzor does that, with the Haxe you already write.

01 Correctness first

An optimization ships after it's proven correct. Drop insertion and inline are guarantees, not hints, so they run at every level, including -O0.

02 No garbage collector

The compiler works out when to free things. No pauses in the middle of a frame, and you can annotate one class at a time.

03 Analysis is shared

Dominance, loop structure and escape info are computed once and reused by every pass that needs them.

04 Same source, same binary

MIR collections are ordered on purpose, so codegen is reproducible build after build.

05 Don't redo work

Parsing, type checking, module caching and bundling all skip what hasn't changed. Caching is on by default.

06 Optimize once

Every target is built from the same optimized program, so an improvement to one speeds up your native binary and your wasm module too.

Not a goal

Transpilation. The official Haxe compiler is very good at emitting JavaScript, Python and PHP. Rayzor has no such target and won't. Use the official compiler when you ship source, Rayzor when you ship machine code.

Memory

No collector. No pauses.

The compiler decides when every value is freed, by working out where it is last used. You reach for annotations only where sharing actually matters.

@:move class Texture {
  public var pixels:Bytes;
}

@:arc class Atlas {
  public var pages:Array<Texture>;
}

@:safety(strict)
class Main {
  static function main() {
    var t = new Texture();
    upload(t);
    // upload(t);  ← error: use after move
  }
}
@:moveUnique ownership

Move semantics, no aliasing. Use-after-move is a hard error, caught before you run.

@:arcShared across threads

Atomic reference counting, for state that genuinely needs more than one owner.

@:deriveChecked concurrency

Send and Sync markers, validated against Thread, Channel, Mutex and Arc.

@:safetyAdopt it incrementally

Strict mode requires every class to be annotated; non-strict wraps the rest in Rc, so ownership is never a rewrite you do first.

Benchmarks

One pipeline,
optimized once.

Same Haxe source on every target. Rayzor is faster than HashLink outright, faster than the JVM without paying its startup, and faster than hxcpp on floating-point work. Compile time is tens of milliseconds, not hundreds.

That compile column is the part you feel all day. No C++ toolchain to set up, no JVM to warm, no separate build step before you can run. rayzor run starts executing while the optimizer is still working. And it's a cold number: the BLADE cache keeps every unchanged module compiled, so the second build compilation is instant.

3 measured iterations, mean reported. AMD EPYC 7763 64-Core Processor, linux, x86_64, 2026-09-12.

Full results, regenerated by CI ↗
Rayzor · tiered 704msfastest
hxcpp 729ms1.04×
Haxe/JVM 977ms1.39×
HashLink/C 1.48s2.11×
HashLink 1.36s1.93×
lower is better

execution only, mean of 10 measured runs · milliseconds, smaller is better
axis clips at 4× the fastest. ▸ marks a bar past the edge, real value labeled

SIMD

Vectors are a type,
not an intrinsic.

The SIMD* family covers 128- and 256-bit, float and integer lanes, with tuple and array literals, real operator overloads, and a full math surface: dot, normalize, magnitude, lerp. It lowers to NEON, SSE2 or WASM SIMD128, with a scalar fallback where none exists.

The interpreter handles vector types, and functions that use SIMD are promoted on first call, so vector work runs compiled from the start and startup stays instant.

How it lowers ↗
Vectors.hx
import rayzor.SIMD4f;

var a:SIMD4f = (1.0, 2.0, 3.0, 4.0);
var b = SIMD4f.splat(2.0);
var c = a * b + a;
var d = a.dot(b);
var n = a.normalize();
Get started

Running in a minute.

Point Rayzor at a .hx file, or hand it the build.hxml you already have.

runtiered JIT, cache on by default
aotwhole program compiled ahead of time to a native binary
bundleone portable .rzb that skips compilation
buildnative, wasm or wasm-wasi from a manifest
$ curl -fsSL https://rayzor.tech/install.sh | sh
Installs to ~/.rayzor/bin. The download is self-contained. Nothing else needs installing.
then
$ rayzor init --name my-app
$ rayzor run src/Main.hx
Hello, Rayzor
tier 0 → 1 → 3 · 41ms total
Who it's for

Where a native Haxe pays off.

Servers

Long-running processes where a collector pause is a latency spike you can't explain to anyone. Memory is freed by analysis, so tail latency is a property of your code, not the runtime's mood.

The server preset optimizes aggressively for processes that stay up
Real OS threads with Channel, Select, Mutex and Arc, not a green-thread emulation
Ship one native binary, or one .rzb that skips compilation at startup
rayzor aot --preset server Channel<T> 0 GC pauses
Game development

Frame budgets don't survive a stop-the-world pause. Ownership annotations put allocation lifetimes where you can see them, and the JIT means iteration doesn't wait on a full build.

@:move and @:arc where aliasing matters; everything else stays ordinary Haxe
SIMD4f with operator overloads for transforms, physics and batch math
@:shader classes compile straight to WGSL, so shaders are Haxe too
@:cstruct gives flat, headerless layouts for C ABI compatibility. Bind only what you must
SIMD4f @:shader → WGSL @:move @:cstruct
High-performance computing

The parts that usually push people out of Haxe and into C: vector types, thread pools that don't re-spawn, and control over which core does what.

128- and 256-bit vectors with widening dot-accumulate for quantized kernels
SpinPool keeps workers alive and chunk-steals, so dispatch costs a few atomic stores
WorkerPool pins one worker per NUMA node so memory lands first-touch on the right controller
SIMD8i32 SpinPool WorkerPool CpuTopology
Case study · Nue

An LLM inference engine,
written in Haxe.

Nue runs local language models on the Rayzor runtime. The tokenizer, the quantized matmul kernels, the KV cache and the scheduler are all written in Haxe.

Every kernel is benchmarked against the Rust version it replaces. When Haxe is slower, that's treated as something to fix in the kernel, the thread pool or the compiler, not a reason to drop back to Rust.

Browse nue/ in the repo
quantized matmul KV cache SpinPool SIMD kernels GGUF
Decode throughput Apple M1 Pro · 16GB
Qwen 0.5B 138tok/s
Llama 1B 90tok/s
Pure Haxe vs the Rust kernel

Qwen2.5-0.5B, interleaved arms, medians, verified with zero FFI calls.

schemeHaxeRust Q6_K · k-quant + Q8_0 135.63 106.30 Q5_0 → INT8 113.82 119.37

Parity is met and beaten on k-quant; INT8 sits within 5% and is closing. FFI is reserved for platform APIs such as AMX, CoreML and VNNI, never for kernels Haxe could write.

In progress

Bring your own frontend.

We're working on consuming the official Haxe compiler's output directly and lowering it into Rayzor MIR. You would keep the frontend you already use, with every macro and library, and get native codegen, ownership and the tiered runtime underneath it.

No rewrite, no second dialect. The same build you run today, ending in machine code.

Follow the discussion ↗
haxe Your existing build, run by the official compiler. Same class paths, same macros
typed output Its typed output read directly, instead of Rayzor re-parsing your source
→ MIR Lowered into the same SSA every Rayzor backend already consumes
native Ownership analysis, the optimization pipeline and the tiered runtime, unchanged