// Universal Future Computing

One language.
Every machine.

Systems to AI. Browser to bare metal.

NOVA (Natively Optimized Versatile Architecture) is a new systems language where one developer builds anything and runs it anywhere — at C speed, with less ceremony than Python. You write simple code. The compiler is the genius.

$ curl -fsSL novachan.org/install.sh | sh

🚧 Pre-release — see current download status →

3,590 tests passing 1,800+ stdlib modules 993 Forge framework files 3,000+ commits
hello.nova
// A struct, a function, type inference — zero ceremony.
type Point
    x: int
    y: int

fn dist_sq(a, b)
    let dx = a.x - b.x
    let dy = a.y - b.y
    dx * dx + dy * dy

fn main()
    let p = Point(0, 0)
    let q = Point(3, 4)
    print(dist_sq(p, q))   // 25
The Problem

Computing is fragmented. NOVA unifies it.

Today a typical project uses Python for the AI pipeline, Go for the backend, TypeScript for the frontend, C for the performance-critical path, and bash to glue it all together. Five languages, five build systems, five ways to define a data type. Every time data crosses a language boundary, someone writes a serializer and hopes both sides agree.

NOVA replaces that stack with one language that compiles to C-speed native code, runs concurrent servers out of the box, and targets WebAssembly for the browser — so you define your data type once, write your logic once, and deploy anywhere.

~1.04× C speed (within 4%) 0 type annotations for most code 1 toolchain, end to end
The Model

Everything is three things.

NOVA's entire computational universe is built from three primitives. Master these, and you can express any program — for any target.

Values

Everything is a value: integers, strings, structs, lists, dicts, byte buffers, even channels themselves. You write x = 42 or p = Point(3.0, 4.0) and the compiler infers the type, picks the allocation strategy (stack or heap), and decides how to free it — all without a single annotation from you.

Processes

Write spawn { work() } and NOVA creates a lightweight green task. Each process has its own memory — they never share heap, so there are no data races. If your program uses only one process, the compiler erases all the isolation machinery: zero overhead, same as writing C.

Channels

Processes talk through channels: send(ch, data) and recv(ch). Data is deep-copied on send, so the sender and receiver never share state. The same channel abstraction works in-process, across threads, and over the network — you change the target, not the code.

See It

Simple to write. Fast to run.

Every snippet below is real NOVA code — compiled and run by the same compiler. Click each tab to see what NOVA looks like for different tasks, with full explanations.

// Save this as hello.nova. Run it: nova run hello.nova

fn greet(name)
    print("Hello, {name}!")   // {name} = string interpolation

fn main()
    greet("NOVA")            // Hello, NOVA!
    greet("world")           // Hello, world!

    // No type annotation on greet — the compiler
    // sees you pass a string, infers name: string.
    // No f"..." prefix like Python — all strings
    // support {expr} interpolation by default.
// Define a struct with typed fields.
// IMPORTANT: field types MUST be lowercase (float, not Float).
// Capital types use dynamic dispatch — 150x slower for math.
type Point
    x: float        // lowercase = native 64-bit double. Fast.
    y: float

// A free function. No type annotations on a, b — the compiler
// infers they are Point from the call site below.
fn dist(a, b)
    let dx = a.x - b.x          // native fmul, no overhead
    let dy = a.y - b.y
    sqrt(dx * dx + dy * dy)      // last expression = return value

// A method on Point. self refers to the receiver.
fn Point.magnitude() -> float
    sqrt(self.x * self.x + self.y * self.y)

fn main()
    let p = Point(0.0, 0.0)
    let q = Point(3.0, 4.0)
    print(dist(p, q))             // 5.0
    print(q.magnitude())          // 5.0

// Output: 5.0
//         5.0
// NOVA uses Result<T> instead of exceptions.
// A function either returns ok(value) or err(message).
// This makes errors VISIBLE in the code — you can't
// accidentally ignore them like you can with exceptions.

fn parse_age(s) -> Result<int>
    // try = if parse_int fails, immediately return its err
    let n = try parse_int(s)

    // Additional validation on the parsed value
    if n < 0 or n > 150
        return err("age out of range: {n}")

    // Success: wrap in ok(). Note the lowercase — building a Result
    // uses ok()/err(), but MATCHING one uses capitalized Ok/Err below.
    ok(n)

fn main()
    // match forces you to handle BOTH outcomes.
    // The compiler won't let you forget the Err case.
    match parse_age("25")
        Ok(age)  => print("Valid: {age}")     // Valid: 25
        Err(e)   => print("Error: {e}")

    match parse_age("999")
        Ok(age)  => print("Valid: {age}")
        Err(e)   => print("Error: {e}")     // Error: age out of range: 999
// NOVA concurrency: green tasks + channels.
// No async/await. No colored functions. No mutexes.
// spawn = launch a lightweight task (like Go's go).
// channel = typed queue between tasks (like Go's chan).
// send() deep-copies the data — tasks NEVER share memory.

fn worker(id, ch)
    for i in 0..3
        send(ch, "Worker {id} produced item {i}")

fn main()
    let ch = channel()            // create a channel
    spawn { worker(1, ch) }      // launch task 1
    spawn { worker(2, ch) }      // launch task 2

    // Collect all 6 results (2 workers × 3 items each).
    // recv() blocks until a value arrives — but only
    // parks this task, not the OS thread.
    for _ in 0..6
        print(recv(ch))

// Output (order between workers may vary):
//   Worker 1 produced item 0
//   Worker 1 produced item 1
//   ...
// A complete REST (Representational State Transfer) API (Application Programming Interface) with NOVA's Forge framework.
// One file. One import. Compiles to a single binary.
// No npm install, no pip install, no docker, no runtime.

import forge                     // built-in web framework

// Define your data model as a struct.
// Forge auto-converts structs to/from JSON (JavaScript Object Notation).
type Todo
    id: int
    title: string
    done: bool

// A lambda body is always ONE expression — a two-step handler
// (parse, then respond) needs a named fn like this one instead.
fn handle_create(req: Request) -> Response
    let t: Todo = from_json(req.body)   // typed JSON parse: dispatches on Todo
    forge.resp_json(201, t)              // HTTP 201 + JSON body

fn main()
    let app = forge.app()        // create the app

    // GET /todos → returns a JSON array of todos.
    // Just return a list of structs — Forge serializes it.
    forge.get(app, "/todos", fn(req) [Todo(1, "Learn NOVA", false), Todo(2, "Build an API", true)])

    // POST /todos → handle_create parses the body into a Todo and responds.
    forge.post(app, "/todos", fn(req) handle_create(req))

    forge.serve_app(app, 8080)    // start on port 8080
    // curl localhost:8080/todos → JSON array
// Enums + pattern matching.
// An enum defines a type that can be ONE OF several variants.
// match inspects which variant it is and extracts the data.

enum Shape
    Circle(radius: float)
    Rect(width: float, height: float)
    Triangle(a: float, b: float, c: float)

fn area(s)
    match s
        // Each arm checks one variant and binds its fields.
        Circle(r)        => 3.14159 * r * r
        Rect(w, h)       => w * h
        Triangle(a, b, c) =>
            // Multi-line arms use indentation.
            let sp = (a + b + c) / 2.0
            sqrt(sp * (sp-a) * (sp-b) * (sp-c))

fn main()
    print(area(Circle(5.0)))           // 78.53975
    print(area(Rect(4.0, 6.0)))       // 24.0
    print(area(Triangle(3.0,4.0,5.0))) // 6.0
// File I/O (Input/Output) — read, write, and process files.
// No file handles needed for simple operations.

fn main()
    // Write a file (creates or overwrites)
    write_file("todo.txt", "Buy milk\nFix bug #42\nLearn NOVA\n")

    // Read it back
    let content = read_file("todo.txt")
    print(content)

    // Process line by line
    let lines = split(content, "\n")
    for line in lines
        if len(trim(line)) > 0
            print("  → {line}")

    // Check file info
    print("Size: {file_size("todo.txt")} bytes")
    print("Exists: {file_exists("todo.txt")}")

    // Append (doesn't overwrite)
    append_file("todo.txt", "Ship NOVA 1.0\n")

    // Directory operations
    mkdir_p("output/reports")
    let files = list_dir(".")
    print("Files: {files}")
// Iterators — lazy sequences that process data
// without creating intermediate lists in memory.

fn main()
    // Create an iterator over a range of numbers
    let it = iter_range(1, 21)           // 1 to 20, lazily

    // Chain operations — no intermediate lists!
    let it = iter_filter(it, fn(x) x % 3 == 0)  // keep multiples of 3
    let it = iter_map(it, fn(x) x * x)          // square each
    let result = iter_collect(it)                  // materialize
    print(result)
    // [9, 36, 81, 144, 225, 324]

    // Sum of 1..100 without a list in memory
    let total = iter_sum(iter_range(1, 101))
    print("Sum 1..100 = {total}")   // 5050

    // Zip two iterators together
    let names = iter(["Alice", "Bob", "Eve"])
    let scores = iter([95, 87, 92])
    let pairs = iter_collect(iter_zip(names, scores))
    print(pairs)
    // [["Alice", 95], ["Bob", 87], ["Eve", 92]]
// AI (Artificial Intelligence) / Tensors — built-in multi-dimensional
// arrays for machine learning. No external library needed.

fn main()
    // Create a 2×3 matrix from a flat list
    let a = tensor_from_list([1.0, 2.0, 3.0,
        4.0, 5.0, 6.0], [2, 3])

    print(tensor_shape(a))    // [2, 3]

    // ReLU (Rectified Linear Unit) activation
    let x = tensor_from_list([-1.0, 0.0, 1.0, 2.0], [4])
    // tensor_to_list converts back to an ordinary NOVA list — print() alone
    // shows the tensor's internal representation, not its values.
    print(tensor_to_list(tensor_relu(x)))     // [0.0, 0.0, 1.0, 2.0]

    // Matrix multiplication — the core AI operation
    let w = tensor_from_list([0.1, 0.2, 0.3,
        0.4, 0.5, 0.6], [3, 2])
    let out = tensor_matmul(a, w)
    print(tensor_shape(out))  // [2, 2]

    // Softmax → probabilities summing to 1.0
    print(tensor_to_list(tensor_softmax(x)))
    print(tensor_argmax(x))  // 3 (index of max)
// Cryptography — pure NOVA, no C library dependencies.
// SHA (Secure Hash Algorithm), HMAC, AES (Advanced Encryption Standard),
// Ed25519, X25519 — all built in.

import forge_crypto

fn main()
    // Hash a password with SHA-256
    let hash = sha256("Hello, NOVA!")
    print(hash)
    // a3b9f...64-char hex string

    // HMAC (Hash-based Message Authentication Code)
    let mac = hmac_sha256("secret-key", "message")
    print(mac)

    // Base64 encoding/decoding
    let encoded = base64_encode("NOVA is fast")
    print(encoded)           // Tk9WQSBpcyBmYXN0
    let decoded = base64_decode(encoded)
    print(decoded)           // NOVA is fast

    // UUID (Universally Unique Identifier) generation
    print(uuid4())
    // e.g. 550e8400-e29b-41d4-a716-446655440000
// Distributed computing — channels that work over the network.
// Same API as local channels: send() and recv().
// Data is serialized to JSON (JavaScript Object Notation) automatically.

// Server — remote_listen binds, listens, AND parks until a client
// connects, handing back an already-connected channel — there's no
// separate accept() step.
fn run_server(port)
    let conn = remote_listen(port)

    // Receive a value from the remote client
    let msg = remote_recv(conn)
    print("Got: {msg}")

    // Send a response back
    remote_send(conn, {"status": "ok"})
    remote_close(conn)

// Client — connects to a remote server
fn run_client(port)
    let conn = remote_connect("127.0.0.1", port)
    remote_send(conn, {"op": "hello"})
    let reply = remote_recv(conn)
    print("Reply: {reply}")
    remote_close(conn)

fn main()
    let port = 9000
    spawn run_client(port)   // client runs concurrently, server below blocks on listen
    run_server(port)
// FFI (Foreign Function Interface) — call any C function.
// extern fn declares a C function. NOVA handles the bridge.

// Declare C math functions
extern fn pow(base: float, exp: float) -> float
extern fn sin(x: float) -> float

// Link an external library
extern fn sqlite3_open(path: string, db: ptr) -> int
@link("sqlite3")

fn main()
    // Every extern (C) call must be marked unsafe — C is outside NOVA's
    // safety envelope, and unsafe marks exactly where that trust begins.
    print(unsafe pow(2.0, 10.0))   // 1024.0
    print(unsafe sin(3.14159))    // ~0.0

    // Raw pointer operations, also unsafe. ptr_write/ptr_read act AT the
    // pointer directly — offset by hand with ptr_add if you need one.
    unsafe
        let buf = alloc_raw(1024)
        ptr_write(buf, 42)
        print(ptr_read(buf))  // 42
        free_raw(buf)

What's happening here:

fn greet(name) defines a function. Notice there is no type annotation on the name parameter — the compiler sees you pass "NOVA" (a string) at the call site and infers that name is a string. You never need to write name: string for this to work.

"Hello, {name}!" — every double-quoted string in NOVA supports {expr} interpolation. No f"..." prefix needed (unlike Python). To put a literal brace in a string, escape it: \{.

Compare to Python: Python needs f"Hello, {name}!" with the f-prefix. Forget the f, and you get the literal text Hello, {name}!. In NOVA, interpolation always works.

Why NOVA

One core model. Every domain.

⚙︎

The Genius Compiler

You write fn add(a, b) → a + b with zero types. The compiler runs Hindley-Milner inference, figures out a and b are integers from the call sites, and emits a native add i64 instruction. 95% of your code needs zero type annotations — the compiler does the work so you don't have to.

C-Level Speed

NOVA compiles through LLVM (Low-Level Virtual Machine) — the same backend as C and Rust. On scalar and struct benchmarks (sieve, primes, matmul, struct-math), NOVA runs within 1.0–1.04× of clang -O2. That's not "fast for a new language" — that's the same speed as C, with no manual memory management.

🛡

Safe by Default

Processes never share heap memory — when you send data, it's deep-copied. No dangling pointers, no use-after-free, no data races. Integer overflow wraps (defined behavior), not undefined behavior like C. Bounds checks on every array access. You get safety without fighting a borrow checker.

Fearless Concurrency

Write spawn { do_work() } — that's it. No async, no await, no colored functions. Green tasks cost ~2KB each; you can run 10,000+ simultaneously. Communication goes through channels: send(ch, data) and recv(ch). The deep-copy on send means two tasks can never corrupt each other's data.

Runs Anywhere

Same .nova file compiles to native code on Windows and Linux, or to WebAssembly for the browser. The WASM (WebAssembly) target runs strings, lists, dicts, and full heap values — not just integers. One source file, multiple targets, deterministic output.

🧰

One Toolchain

The NOVA binary is the compiler, the build tool, the LSP (Language Server Protocol) server, and the package manager — all in one ~1.6MB executable. nova build app.nova produces a standalone binary. nova run app.nova compiles and runs. Your editor gets syntax highlighting, go-to-definition, and diagnostics via the same binary.

Under the Hood

The genius compiler — where all the complexity lives.

You write simple code. The compiler does everything else: infers types, manages memory, checks safety, and generates machine code that matches C.

The 5-stage compilation pipeline:

Stage 1 — Lexer + Parser: Your .nova source code is tokenized (split into words, numbers, operators, keywords) and parsed into an AST (Abstract Syntax Tree). NOVA uses indentation-based scoping (like Python) — no curly braces, no semicolons. The parser tracks indentation depth to determine which statements belong to which block.

Stage 2 — Type Inference (HM): The Hindley-Milner (HM) type inference engine walks the AST and assigns types to every expression — without you writing any. How? It creates "type variables" (unknowns) for each expression, then solves constraints. When it sees a + b where a was passed 5 (an integer), it unifies: a = int, and since + on int returns int, the result is int. The entire program is type-checked this way. If a conflict is found (you pass a string where an int is expected), the compiler gives you a helpful error with line numbers — not a cryptic template dump.

Stage 3 — AST → IR (Intermediate Representation): The typed AST is lowered to a flat, register-based IR. High-level constructs (for loops, match arms, closures) become simple sequences of instructions: load, store, call, branch. This IR is where optimizations happen — dead code elimination, constant folding, and escape analysis (determining whether a value can be stack-allocated instead of heap-allocated).

Stage 4 — IR → LLVM (Low-Level Virtual Machine) IR: NOVA's IR is translated to LLVM IR — the same intermediate format used by C (clang), Rust (rustc), and Swift (swiftc). LLVM then runs 50+ optimization passes: SROA (Scalar Replacement of Aggregates — breaks structs into individual variables), mem2reg (promotes stack allocations to registers), GVN (Global Value Numbering — eliminates redundant computations), loop vectorization (uses SIMD instructions), and more.

Stage 5 — Linking: The optimized LLVM output is compiled to object code and linked with nova_runtime.c — NOVA's C runtime. The runtime provides: the green task scheduler (M:N threading, work-stealing), the channel implementation (mutex + condition-variable queues, with cooperative parking so a blocked task never blocks its OS thread), the arena allocator (bulk allocation + bulk free, no GC tracing), the netpoller (epoll on Linux, WSAPoll on Windows), and built-in functions (print, len, read_file, etc.). The result: a single standalone binary.

Self-hosted: The NOVA compiler is written in NOVA. It compiles itself — nova_compiler.nova (~36,000 lines) is fed to the previous generation's binary, which produces the next generation. Three passes in, generation N and generation N+1 must emit byte-identical LLVM IR — verified with a SHA-256 diff, not eyeballed. This is called "reconverge," and it's the deepest correctness check the project has: it has caught real bugs (a struct-field-leak use-after-free among them) that unit tests missed entirely.

Performance

Benchmarked against C, not hand-waved.

Real measurements on real hardware. NOVA's performance claims are backed by numbers, not marketing.

BenchmarkNOVAC (clang -O2)Ratio
Scalar struct math (dot product)~samebaseline1.04×
Sieve of Eratosthenes (primes)~samebaseline~1.0×
Struct passed to functionnear-Cbaseline1.21–1.32×
Green task spawn (10k tasks, incl. 10k parked)~382msN/A (no green tasks)
Compiled binary size (hello world)~120KB~16KBruntime included

What the numbers mean:

1.04× C on scalar struct math means NOVA is within 4% of C's speed — for code written with zero type annotations and zero manual memory management. The 4% gap comes from NOVA's uniform i64 ABI (Application Binary Interface) which prevents LLVM's SROA from fully decomposing struct arguments. The ABI classification layer (LOCK-11) has been partially implemented — struct-by-value FFI works on Win64/SysV/AArch64 — and further specialization will close the remaining gap.

1.21–1.32× C for struct-passing means NOVA is 21–32% slower than C when structs are passed between functions. This is because NOVA currently passes all values as 64-bit integers through a uniform ABI — the same pointer-width representation that gives NOVA its type erasure and polymorphism. The ABI classification layer now correctly classifies struct fields for Win64, SysV x86-64, and AArch64 calling conventions, and struct-by-value FFI is working. Further specialization of internal (non-FFI) struct passing will bring this closer to 1.0×.

~382ms for 10,000 green tasks (including parking and waking every one of them) works out to roughly 38 microseconds per task on average. Each task uses ~2KB of stack, versus the 1–8MB an OS thread typically reserves — we haven't measured our own OS-thread baseline on this same machine to put an exact multiplier on the spawn-time difference, but the stack-size gap alone is real and well over 100×.

Float arrays — gap closed (S4.2 shipped): Float array access was ~160× slower than C because elements were boxed in the polymorphic runtime. The S4.2 loop versioning pass now wraps hot loops in a runtime type guard: if the array is a typed float array, the fast path uses direct double* access with alpha-renamed locals; otherwise the original boxed path runs unchanged. Sound by construction (runtime guard). Result: ~1.2× C, down from 160×. Default-on, zero developer configuration needed.

How It Compares

The strengths of five languages, in one.

NOVACRustGoPython
Native speed~
Memory safe
Easy concurrency~~
Minimal ceremony~~
No GC (Garbage Collection) pauses
One toolchain~
Runs everywhere~~~~

✓ first-class  ·  ~ partial / in progress  ·  ✗ not a strength. NOVA's "runs everywhere" is honest: native + WASM-compute + a real browser DOM (via Prism, see below) + CPU (Central Processing Unit)-GPU (Graphics Processing Unit) today; full GPU, mobile, and embedded targets are on the roadmap.

Reading the table — what each row means:

Native speed: NOVA and C both compile through LLVM to machine code — on scalar and struct benchmarks, NOVA runs at 1.0–1.04× of clang -O2. Go is "partial" because its compiler produces good but not LLVM-tier code. Python is interpreted and typically 50–100× slower than C for compute-bound work.

Memory safe: C has no safety — buffer overflows, use-after-free, and undefined behavior are the developer's problem. Rust enforces safety through a borrow checker (powerful but complex). Go and Python use garbage collection. NOVA uses process isolation (deep-copy on send) plus bounds-checked array access — safety without a borrow checker or a GC.

Easy concurrency: Go's goroutines are famously easy (go func()). NOVA matches this with spawn { ... } — same ease, same green threads. Rust has excellent concurrency safety but the API surface (Arc<Mutex<T>>, async/await, Pin) is complex. Python's GIL (Global Interpreter Lock) blocks true parallelism — multiprocessing is a workaround, not a solution.

Minimal ceremony: Python and NOVA both require minimal boilerplate — no public static void main, no header files. Rust requires lifetime annotations, trait bounds, and impl blocks. C requires manual header management and forward declarations. Go sits in the middle — simple syntax but explicit error handling (if err != nil on every call).

No GC pauses: Languages with garbage collection (Go, Python, Java) periodically pause your program to free unused memory. These pauses can spike to milliseconds — unacceptable for real-time systems. NOVA uses per-request arena allocation (bulk free, no tracing) plus reference counting for long-lived data. No stop-the-world pauses.

One toolchain: NOVA's compiler binary is also the build tool, the LSP (Language Server Protocol) server, and the package manager. Rust has a similar "one tool" story with cargo. C requires separate compiler, build system (Make/CMake/Meson), and has no standard package manager. Python has pip, venv, setuptools, poetry, conda — and they often conflict.

Real Code Comparison

See the difference — same task, multiple languages.

Each example does the same thing. Count how many lines, how many concepts, and how many annotations each language requires.

Task: Read a file, filter lines containing "error", count them.

NOVA

fn main()
    let text = read_file("log.txt")
    let errors = filter(split(text, "\n"), fn(l) contains(l, "error"))
    print("Errors: {len(errors)}")

4 lines. Zero imports. Zero types. Zero ceremony.

Python

with open("log.txt") as f:
    lines = f.readlines()
errors = [l for l in lines if "error" in l]
print(f"Errors: {len(errors)}")

4 lines, but needs with/as, f"..." prefix, context manager.

Rust

use std::fs;

fn main() {
    let text = fs::read_to_string("log.txt")
        .expect("failed to read");
    let count = text.lines()
        .filter(|l| l.contains("error"))
        .count();
    println!("Errors: {count}");
}

10 lines. Import, braces, semicolons, .expect(), closure |l| syntax.

Go

package main
import ("fmt"; "os"; "strings")

func main() {
    b, err := os.ReadFile("log.txt")
    if err != nil { panic(err) }
    c := 0
    for _, l := range strings.Split(
        string(b), "\n") {
        if strings.Contains(l, "error") { c++ }
    }
    fmt.Printf("Errors: %d\n", c)
}

12 lines. Three imports, error check, byte→string conversion, manual counter.

The takeaway: NOVA achieves the simplicity of Python (no boilerplate, no ceremony) while compiling to the speed of Rust and C. You don't choose between "easy to write" and "fast to run" — you get both. The compiler infers every type, handles memory, and produces a native binary. You just write the logic.

One Language, Every Domain

Build anything.

A single core model — Values, Processes, Channels — reaches every corner of computing. Some domains ship today; others are actively being built. The key: you never switch languages.

🖥Systemsnative, today
🧠AI / MLtensors, nn
🌐Web Backendhttp, router
🧩Frontend / WASMreal DOM, new
🔗Distributedchannels, nodes
☁︎Clouddeployed live
📟Edgev0.1
🎮Gamesecs, physics2d
🔌Embeddedroadmap

What each domain means:

Systems: Write memory allocators, file parsers, OS (Operating System) utilities, and CLI (Command Line Interface) tools. NOVA compiles to native machine code via LLVM (Low-Level Virtual Machine) — no VM (Virtual Machine), no interpreter. You get C-level control with unsafe blocks when you need raw pointers, and safety by default everywhere else.

AI (Artificial Intelligence) / ML (Machine Learning): Built-in tensor operations — tensor_from_list, tensor_matmul, tensor_relu, tensor_softmax, tensor_conv2d. No need to install PyTorch or TensorFlow. You define neural network layers, train models, and run inference in the same language you write your web server in.

Web Backend: The Forge framework ships with the compiler — import forge gives you HTTP (HyperText Transfer Protocol) routing, JSON (JavaScript Object Notation) serialization, middleware, WebSocket support, SSE (Server-Sent Events), CORS (Cross-Origin Resource Sharing), CSRF (Cross-Site Request Forgery) protection, and JWT (JSON Web Token) authentication. Per-request arena memory means zero GC (Garbage Collection) pauses under load.

Frontend / WASM (WebAssembly): Compile your NOVA code to .wasm with nova build --target wasm. The WASM target supports strings, lists, dicts, and full heap values — not just integer math. Newest: the Prism UI framework (see below) now has a real browser DOM backend — the same NOVA UI description that renders to server-side HTML also renders to a live, interactive page in the browser, verified element-for-element identical to the HTML output.

Distributed: remote_listen / remote_connect / remote_send / remote_recv — channels that work over TCP (Transmission Control Protocol). Same send/recv API as local channels. Data is serialized to JSON automatically. Write local code first, distribute later — the API doesn't change.

Cloud: A nova build produces a single static binary with no dependencies. Copy it to any Linux/Windows server and run it. No Docker required (though Docker works too). The binary IS the deployment artifact.

Games: ECS (Entity Component System) architecture with nova_ecs — entities, components, and systems for game logic. Built-in 2D physics (AABB/circle collision, velocity, gravity). Process-per-entity maps naturally to NOVA's concurrency model.

Edge / Embedded: On the roadmap. The same LLVM backend that targets x86-64 and WASM can target ARM (Advanced RISC Machine) and RISC-V. NOVA's zero-GC arena memory model is a natural fit for constrained devices.

Newest — Prism

One UI description. Now including a live browser.

Prism is NOVA's presentation-layer framework — the same three primitives (Values / Processes / Channels) applied to UI. A face is a process; what it produces is a value (a node tree); what it listens to is a channel. That one node tree renders identically to server-side HTML, an ANSI terminal, canvas/PNG, PDF, and CSV — and, as of this cycle, to a real DOM in the browser via WebAssembly.

🌐HTMLserver-side
ANSI / TerminalTUI
🪟Browser DOMvia WASM, new
🖼Canvas / PNGimage export
📄PDFdocument export
📊CSV / Sheetdata export

What's actually built, in numbers:

265 modules, ~58,000 lines, 130 known-answer tests — every one of them passing as of the last full CI run. That covers the node-tree value type, ~65 pre-built UI widgets (cards, tables, calendars, forms, charts, and more), styling and theming, accessibility (screen-reader roles, focus order, keyboard navigation), internationalization, and a working example app: a task-board SPA (Single Page Application) that compiles to WebAssembly and runs in a browser with no server round-trip for interaction — verified to render the exact same markup as the HTML backend produces on the server.

Why the browser DOM backend and not a custom canvas renderer for the web: the browser target emits real DOM elements deliberately, so accessibility, find-in-page, IME (Input Method Editor) text entry, and font shaping come from the platform for free instead of being reimplemented. A separate GPU-backed renderer is planned for native desktop/mobile targets, where there's no DOM to defer to and pixel-level control is worth owning.

What's honest about where this stands: the presentation layer above is complete and gated. What's still being built on top of it is the reactive engine — automatically re-rendering only the part of the UI that a state change actually affects, rather than recomputing the whole tree. That design work is in progress; today, a Prism UI is written by describing what to render, same as building a page with the existing HTML/ANSI backends.

Batteries Included

1,800+ modules — no package manager needed for common tasks.

Every module below ships with the compiler. No pip install, no npm install, no dependency hell. Import and use. Here are some highlights from the library:

ModuleWhat It DoesKey Functions
forgeFull web framework — routing, middleware, WebSocket, SSE, JSONget, post, serve, body_as, ws
forge_cryptoCryptography — hashing, encryption, signatures, TLS (Transport Layer Security)sha256, aes_gcm_encrypt, ed25519_sign
corexExtra core utilities — sorting, searching, functional helperssort, binary_search, group_by
csvxCSV (Comma-Separated Values) parsing and generationcsv_parse, csv_row, csv_generate
urlxURL (Uniform Resource Locator) parsing and encodingurl_parse, url_encode, url_decode
bignumArbitrary-precision integers (numbers with thousands of digits)big, big_add, big_mul, big_pow
matrixxMatrix math — multiply, transpose, determinant, inversemat_mul, mat_det, mat_inv
prngPRNG (Pseudorandom Number Generator) — seedable, reproducibleprng_new, prng_int, prng_float
uuidUUID (Universally Unique Identifier) v4 generationuuid4
proptestProperty-based testing (auto-generated test inputs)prop_int, prop_string, prop_check

Plus built-in functions (no import needed): print, len, push, pop, keys, values, split, trim, replace, to_int, to_float, to_string, read_file, write_file, spawn, channel, send, recv, http_get, http_post, regex_match, now_ms, sleep_ms, env, args, and 1,300+ more.

Standard library categories: crypto (SHA/AES/Ed25519/TLS), networking (TCP/UDP/HTTP client), data structures (trees, heaps, graphs, tries), text processing (regex, Unicode, diff), media codecs (PNG, WAV, BMP, MIDI), AI/ML (tensors, neural networks), databases (SQLite, PostgreSQL, MySQL drivers), compression (gzip, zlib, LZ4), serialization (JSON, TOML, YAML, MessagePack, Protocol Buffers), concurrency (actors, work-stealing, futures), and more — 1,800+ modules across 20+ categories, all KAT-gated (known-answer tested against authoritative vectors).

What NOT to Do

Avoid these common pitfalls — NOVA is different from what you're used to.

DON'T: Use capital-letter types for struct fields

// WRONG — 150x slower!
type Point
    x: Float   // capital F = dynamic dispatch
    y: Float
// RIGHT — C-speed native float
type Point
    x: float   // lowercase = native 64-bit double
    y: float

Why: Lowercase float tells the compiler to use native 64-bit IEEE (Institute of Electrical and Electronics Engineers) 754 doubles — the CPU (Central Processing Unit) performs fmul/fadd directly. Capital Float makes the field dynamically typed, and every math operation goes through a type-check dispatch at runtime. The difference: ~150x slower.

DON'T: Use Python-style boolean operators

// WRONG — these are NOT valid in NOVA
if x > 0 && y > 0   // && doesn't exist
if !done           // ! doesn't exist
// RIGHT — use English words
if x > 0 and y > 0
if not done

Why: NOVA uses and, or, not — English words, not symbols. This is part of the "simpler than Python" goal. Python already uses these words; C/Java/Rust use &&/||/!. NOVA follows Python's choice here because it reads more naturally.

DON'T: Forget that string interpolation is always on

// WRONG — {name} will be interpolated!
let template = "Hello, {name}!"
// If 'name' is defined, it gets substituted.
// If 'name' is NOT defined, you get an error.
// RIGHT — escape braces to get literal text
let template = "Hello, \{name\}!"
// Prints literally: Hello, {name}!

Why: Unlike Python (which needs f"..." to activate interpolation), NOVA's double-quoted strings ALWAYS interpolate {expr}. This means you never forget the f prefix — but it also means you need \{ when you want a literal brace character (common in JSON templates or regex patterns).

DON'T: Use semicolons or curly braces for blocks

// WRONG — NOVA is indentation-based
fn greet(name) {
    print("Hello");
}
// RIGHT — indent defines the block, no semicolons
fn greet(name)
    print("Hello")

Why: NOVA uses significant indentation (like Python) — not curly braces (like C/Java/Rust) and not semicolons. Indentation defines scope: everything indented under fn greet(name) is inside that function. This eliminates an entire class of bugs ("which brace closes which block?") and reduces visual noise.

Get Started

Your first NOVA (Natively Optimized Versatile Architecture) program in 30 seconds.

1

Install

One installer, one file, per platform — the NOVA compiler and a matched clang/LLVM toolchain arrive together. Nothing to separately download, no Visual Studio Build Tools, no apt install llvm. It just works.

curl -fsSL novachan.org/install.sh | sh
# Windows: iwr novachan.org/install.ps1 -useb | iex

🚧 Pre-release — the one-command installer above isn't live yet. See current download status for what's actually available today. Contributing to the compiler itself instead? See building from source.

2

Write hello.nova

Create a file. No imports, no class, no semicolons, no public static void main. Indentation defines blocks (like Python). The main() function is the entry point — the compiler finds it automatically.

fn greet(name)
    print("Hello, {name}!")

fn main()
    greet("world")

Note: Use 4-space indentation (not tabs). {name} is string interpolation — always active, no f"..." prefix needed.

3

Compile and run

nova run compiles to LLVM IR (Intermediate Representation), links with the C runtime, and executes the resulting native binary. Or use nova build to produce a standalone executable you can ship anywhere.

nova run hello.nova
# → Hello, world!

nova build hello.nova
# → produces hello.exe (Windows) or hello (Linux)
# Single binary, no dependencies, copy and run anywhere

What just happened under the hood:

Step 1 — Parsing: The compiler reads your .nova file and builds an AST (Abstract Syntax Tree) — a tree representation of your program's structure. Function definitions, expressions, and control flow become nodes in the tree.

Step 2 — Type Inference: The Hindley-Milner (HM) type inference engine walks the AST (Abstract Syntax Tree). It sees greet("world") — you passed a string, so name must be a string. It sees "Hello, {name}!" — string interpolation on a string, returns a string. All types are inferred. You wrote zero annotations.

Step 3 — IR (Intermediate Representation) Generation: The typed AST (Abstract Syntax Tree) is lowered to NOVA's internal IR (Intermediate Representation) — a simpler, flat instruction set that's easier to optimize. Dead code is eliminated. Constants are folded. Unused abstractions are erased.

Step 4 — LLVM (Low-Level Virtual Machine) Codegen: The IR (Intermediate Representation) is translated to LLVM IR — the same format used by C and Rust compilers. LLVM's optimizer (50+ optimization passes including SROA, mem2reg, GVN, loop vectorization) produces highly optimized machine code.

Step 5 — Linking: The LLVM output is linked with nova_runtime.c (NOVA's C runtime — garbage-free arena allocator, green task scheduler, channel implementation, netpoller) to produce a standalone native binary.

Total time: For a small program, this entire pipeline runs in under 2 seconds. The result: a native binary that runs at C speed, with zero runtime dependencies.

Start Building →