A small, explicit language for application software
WebAssembly is the primary target, not an afterthought. Errors are values and the compiler enforces it. There is one concurrency concept. Nothing hides control flow.
fn main() {
let (cfg, err) = config.load("app.toml")
check err
ui.run(App{ title: cfg.title })
}
Run Kite in your browser — the playground is the compiler itself, built for WebAssembly. Nothing there talks to a server.
Why it exists
JavaScript and TypeScript grew into application development by accident. Every serious web application today ships a compiler, a bundler, a type checker bolted on from outside, a virtual DOM, and a runtime that re-derives structure the compiler already knew and threw away.
WebAssembly 3.0 — ratified 13 June 2026 — removed the last technical reason to accept that. It standardises garbage collection, native exception handling, tail calls and typed function references, and all of it is baseline across Chrome, Firefox and Safari. A language targeting Wasm today does not need to ship a garbage collector inside its own binary. That single fact is the difference between a 300 KB "hello world" and a 5 KB one.
What is different
An error you have not checked makes the value unreadable
Go's (T, error) shape is right: failures are ordinary values
and every one is visible in the source. Its flaw is that nothing enforces
it — and the value on a failure path is a zero value that flows onward
looking valid.
fn load_user(id: int) -> (User, error) {
let (raw, err) = db.query("SELECT ...", id)
// `raw` cannot be read yet: on the failure path there is no value at all.
check err
return parse_user(raw)
}
Reading raw before the check is E0301. Letting
err go out of scope unexamined is E0302.
Concurrency is one concept
Some operations take time: mark them async and
await them. Calling an async fn starts it and
yields a Task<T>, which is how two things happen at
once. There are no channels, no goroutines and no select.
let a = fetch("alpha", 100)
let b = fetch("beta", 50)
let (first, second) = await task.both(a, b) // 100ms, not 150
The source never says how many threads exist. Share — a
marker the compiler infers structurally — is what will make the same
program parallel on the web the day shared-everything-threads ships.
Exhaustive matching, and a compiler that names what is missing
enum Shape {
Circle(radius: float)
Rect(width: float, height: float)
Point
}
fn area(s: Shape) -> float {
return match s {
Circle(r) => 3.14159 * r * r,
Rect(w, h) => w * h,
Point => 0.0,
}
}
fn main() {
io.print(area(Shape.Rect(width: 2.0, height: 3.0)))
}
Leave out Point and the error names it. That is what makes
adding a variant safe: the compiler shows every place that must change.
Immutable by default
let bindings and struct fields are immutable unless marked
var. That maps directly onto WasmGC's per-field mutability
flag, removes the value-versus-pointer distinction, and makes most types
shareable across tasks without their author doing anything.
Diagnostics are the product
error[E0114]: cannot assign to immutable binding `total`
┌─ cart.kite:14:5
│
9 │ let total = 0
│ ----- declared immutable here
⋮
14 │ total = total + item.price
│ ^^^^^ cannot assign
│
help: make the binding mutable
│
9 │ var total = 0
│ ~~~
Several decisions in the language — nominal traits, explicit
dyn, no implicit conversions, no overloading — were made
because they let the compiler name one cause and one fix.
Where it runs
| Target | How | State |
|---|---|---|
wasm32-gc | WasmGC, emitted directly | Every construct the language has |
kbc | Register bytecode and a VM | The dev loop, the embedding target, and the differential oracle |
native-* | Cranelift, ahead of time | Not yet — see the roadmap |
Every program in the test corpus is compiled to both backends, run on both, and the outputs compared. Two independent implementations that must agree is what makes codegen bugs findable.
The tools
kitec run file.kite compile and run
kitec check file.kite check only
kitec test file.kite run every `test_` function
kitec fmt file.kite lay it out the one way
kitec doc file.kite the reference, from the doc comments
kitec fix file.kite apply every machine-applicable suggestion
kitec build file.kite --emit wasm --out dist
kitec --explain E0301 why a rule exists
Reading order
- The specification — the language itself.
- The standard library reference, generated from its own source.
- Platform research — what Wasm can and cannot do in 2026, with sources.
- Concurrency — the async model and the
Sharemarker. - Compiler architecture — crates, IRs, and the WasmGC lowering.
- The UI layer — layout, and the dual DOM/canvas renderer.
- The roadmap — what is done, and what is honestly not.
- The brand sheet — the mark, its geometry, and where it is allowed to go.