Skip to content

Polyglot Genes — Write Genes in Rust, AssemblyScript, Go, or C

The Rotifer IR layer is WASM-Native, Polyglot by Design. The CLI today ships a built-in TypeScript → WASM path via Javy (QuickJS). Every other language — Rust, AssemblyScript, Go (TinyGo), C/C++, Zig — produces its own WASM and joins the Rotifer pipeline through the Bring-Your-Own-WASM (BYO-WASM) channel:

Terminal window
# 1. Compile your gene to WASM with the toolchain of your choice
wasm-pack build --target nodejs # Rust
npx asc index.ts --outFile gene.wasm # AssemblyScript
tinygo build -target=wasi -o gene.wasm . # Go
emcc gene.c -O3 -s STANDALONE_WASM -o gene.wasm # C / Emscripten
# 2. Hand the WASM to the Rotifer CLI
rotifer compile my-gene --wasm path/to/gene.wasm
rotifer arena submit my-gene

Why this works. The Rotifer IR Specification §1.2 Positioning of Rotifer IR defines IR as a language-agnostic intermediate representation — multiple language frontends can target it. The CLI’s Javy path is one frontend among many. As long as your WASM honours the ABI contract below, it’s a first-class Rotifer Gene.


Your WASM module must export one of two entry points plus memory:

StyleRequired exportsTypical producer
Rotiferexpress(i32, i32) -> i32 + memoryRust (#[no_mangle] extern "C"), AssemblyScript, Zig, hand-rolled WAT
WASI_start + memory (no params, no return)Anything that defaults to a WASI entry point

For the Rotifer style, the two i32 parameters are (input_ptr, input_len) into linear memory; the i32 return is a packed (output_ptr << 32) | output_len. The host reads / writes JSON across that buffer.

The WASI style is simpler — useful for hello-world fixtures and any language with a built-in WASI runtime.


Cargo.toml:

[package]
name = "my-rotifer-gene"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[profile.release]
lto = true
opt-level = "z"
strip = true

src/lib.rs:

#[no_mangle]
pub extern "C" fn express(input_ptr: i32, input_len: i32) -> i32 {
// Real genes would:
// 1. Read JSON input from linear memory at [input_ptr .. input_ptr + input_len].
// 2. Compute the result.
// 3. Allocate output buffer in memory and return packed (ptr, len).
0
}

Build and submit:

Terminal window
rustup target add wasm32-unknown-unknown
cargo build --release --target wasm32-unknown-unknown
rotifer compile my-rust-gene --wasm target/wasm32-unknown-unknown/release/my_rotifer_gene.wasm
rotifer arena submit my-rust-gene

A working minimal Rust source lives in tests/fixtures/polyglot/rust-style/source/ in the playground repository.


AssemblyScript is the lowest-overhead path for TypeScript developers who want smaller WASM than Javy produces (~10-50 KB instead of ~700 KB).

index.ts:

export function express(inputPtr: i32, inputLen: i32): i32 {
// Real genes would read input from memory, compute, and return packed (ptr, len).
return 0;
}

asconfig.json:

{
"targets": {
"release": {
"outFile": "gene.wasm",
"optimize": true,
"runtime": "stub"
}
}
}

Build and submit:

Terminal window
npm install --save-dev assemblyscript
npx asc index.ts --target release
rotifer compile my-as-gene --wasm gene.wasm
rotifer arena submit my-as-gene

A working minimal AssemblyScript source lives in tests/fixtures/polyglot/as-style/source/.


Terminal window
tinygo build -target=wasi -o gene.wasm ./gene.go
rotifer compile my-go-gene --wasm gene.wasm

TinyGo emits a WASI-style module; the IR pipeline accepts it via the WASI ABI. Watch out for TinyGo’s compatibility caveats — most of the Go standard library is supported, but reflection-heavy packages (encoding/json with arbitrary types, text/template, etc.) may not work.


Terminal window
emcc gene.c -O3 -s STANDALONE_WASM=1 -s EXPORTED_FUNCTIONS='["_express"]' -o gene.wasm
rotifer compile my-c-gene --wasm gene.wasm

STANDALONE_WASM=1 is critical — without it Emscripten emits a glue layer that depends on a JavaScript runtime, which the IR sandbox does not provide.


Status: BYO-WASM today, built-in compilation under review

Section titled “Status: BYO-WASM today, built-in compilation under review”
LanguageStatusPath
TypeScript✅ Built-in (auto-detect index.ts)esbuild + Javy/QuickJS
Rust✅ BYO-WASM--wasm target/.../*.wasm
AssemblyScript✅ BYO-WASM--wasm gene.wasm
Go (TinyGo)✅ BYO-WASM--wasm gene.wasm (WASI ABI)
C / C++✅ BYO-WASM--wasm gene.wasm (STANDALONE_WASM)
Zig✅ BYO-WASM--wasm gene.wasm
Python❌ Not viable (Pyodide ~10 MB exceeds WASM fuel budget)

Promoting Rust + AssemblyScript from BYO-WASM to built-in compilation paths is under evaluation. The trigger is genuine community demand — open a GitHub issue or join the Discord if you’d find it useful.


Python’s runtime cost in WASM is incompatible with the per-call fuel budget:

  • Pyodide (full CPython in WASM) is ~10 MB and exhausts the fuel budget on module instantiation alone.
  • MicroPython-WASM (~1 MB) is small enough but lacks most of the standard library and the AI/ML packages that would justify a Python frontend.
  • CPython-WASI (3.12+, ~3 MB) is the most realistic option but is still 4× larger than Javy’s QuickJS.

More importantly, the libraries that make Python attractive for AI work (PyTorch, NumPy, Transformers) are C/Fortran extensions that cannot be compiled to WASM. A “Python Gene” would only be able to express pure-Python business logic — which TypeScript already covers without the runtime overhead.

For Python-heavy workloads, use a Wrapped Gene instead: declare the gene’s phenotype with a metadata pointer to your existing Python service, and let the Cloud Binding execute it. See Hybrid Gene Guide for the composition pattern.