a programming language
One language,
kernel* to UI.
Freestanding kernel code, typed applications, quick scripts — same syntax, same toolchain, same mental model.
Explicit over implicit. Predictable over clever.
What it looks like
Single-file components
Regions in one file
Logic, components, and styles share a file. Each region has its own parser but compiles to the same IR. No context-switching between languages.
#code — functions, variables, logic
#view — declarative component trees
#style — property templates
#test — inline test cases
import ui from "ui";
import widgets from "widgets";
component Counter {
var count: i64 = 0;
fn increment() { count = count + 1; }
fn decrement() { count = count - 1; }
view {
ui.Column {
widgets.Label { text: count; size: 24; }
ui.Row {
widgets.Button { label: "+"; on_click: increment; }
widgets.Button { label: "-"; on_click: decrement; }
}
}
}
} enum IoError : u8 {
NotFound = 1: "file not found";
ReadFailed = 2: "read failed";
}
fn open_file(path: string) -> File or IoError {
let fd: i64 = sys_open(path);
if (fd < 0) {
return IoError.NotFound;
}
return File { fd: fd; };
}
fn load_config() -> Config or IoError {
let f: File = open_file("config.ini") or return;
let data: string = read_all(f) or return;
return parse_config(data);
} No exceptions
Errors are values
Functions return T or E where E is your own enum. No stack unwinding, no hidden control flow. Every error propagation is visible at the call site.
or return — propagate to caller
or { } — handle inline
enum descriptions — error IS the message
No generics needed
Compile-time execution
@tape {} runs tape code at compile time.
Generate type-specialized structs and functions — same language, same debugger, no inference puzzles.
@emit — inject declarations
${T} — splice type names into identifiers
@sizeof, @typeof — reflect on types
struct Event {
id: i64;
name: [16]u8;
}
@tape fn gen_queue(T: type) {
@emit struct Queue_${T} {
items: *${T}; count: i64; cap: i64;
}
@emit fn queue_${T}_push(q: *Queue_${T}, item: ${T}) {
// grow + append
}
@emit fn queue_${T}_pop(q: *Queue_${T}) -> ?${T} {
// remove + return
}
}
@tape { gen_queue(i64); }
@tape { gen_queue(Event); } component Button {
prop label: string;
event on_click();
state hovered: bool = false;
state active: bool = false;
fn on_pointer_enter() { hovered = true; }
fn on_pointer_leave() { hovered = false; }
fn on_pointer_up() {
active = false;
fire on_click();
}
view {
widgets.Panel {
widgets.Label { text: label; }
}
}
} No closures needed
Events and state
Components declare typed events. Parents bind handlers — just function pointers, no hidden allocations. Interaction state flags drive styling through #style pseudo-selectors.
event — typed callback slot
fire — invoke bound handler
state — bool flags for :hover, :active
community
Join us on Discord
Follow the development, ask questions, share ideas, and help shape the language.
Join the Discord
What's in the box
Dual backend
VM interpreter for dev, native x86-64 for prod. Same TAC IR, same semantics.
Three-address IR
Explicit data flow. Values have names, not stack positions. Trivial to optimize.
@tape {} comptime
Run code at compile time. Generate types, compute tables, replace generics entirely.
Components
First-class props, slots, events, vtables. Built into the type system, not bolted on.
Source regions
#code, #view, #style, #test — sub-languages in one file, one IR out.
Three profiles
t0 kernel, t1 application, t2 scripting. Pick your strictness per module.
Error returns
T or Error. Propagate with `or return`, handle with `or {}`. No exceptions.
Direct codegen
Emits x86-64 directly. ELF or PE. No LLVM, no Cranelift, no external toolchain.
C at the edges
Fast internal ABI. Thin trampolines at library boundaries. extern fn for C interop.