No runtime, no VM
The output is a native executable from clang -O2, not a bundled interpreter. Zero startup warmup, small binaries, plain libc by default.
You write .ts. It writes LLVM IR, hands it to clang, and out comes a real executable. Your operating system is none the wiser. No runtime. No V8. No Electron.
One self-contained binary — for CLI tools, Docker microservices, and desktop apps.
$ git clone https://github.com/memphisx/KlainMainLang
$ cd KlainMainLang && make build # → ./klainmain
$ ./klainmain app.ts # → native binary
$ ./app
hello, native worldThese are curated feature-area checklists, “does the core case work?”, not external conformance. They mirror the project's own status matrix, caveats and all.
External conformance · full public suites Shown for the default -compat=strict lane. Low on purpose, and shown on purpose: this is a typed subset — most of Test262 is eval-based untyped JS, and Node's suite is dynamic JavaScript against the full platform, both largely out of scope by design, not silent failures. A second lane, -compat=js, trades strict's guarantees for vanilla-JS reach and scores higher on the untyped corpora. All four suites, both lanes, per OS →
The output is a native executable from clang -O2, not a bundled interpreter. Zero startup warmup, small binaries, plain libc by default.
http.createServer — the real Node shape — speaks HTTP/1.1 and HTTP/2 (h2c) on one port, with WebSocket (ws/wss) upgrades. fs, worker_threads and cluster give you real OS threads and processes. TLS on both ends.
fetch, URL, WebSocket, Web Crypto, Streams, AbortController, timers, the web APIs that make sense without a DOM, and none of the ones that don't.
klain:webview opens a real window over the system browser engine and calls straight into typed native code. Any SPA (React/Vue/Svelte/Quasar) embeds into the binary with new Webview({ serve }) — a single-file app, packaged to a .app or .desktop.
number is a JS-faithful 64-bit float by default, opt into sized machine ints (int8…uint64) with a JSDoc width when you want them. Memory is manual by default (never frees, perfect for short-lived CLIs); pass -mm=gc for a real Boehm collector. Crypto / bigint / regex backends chosen per compile.
One statically-linkable binary, no interpreter and no node_modules to copy in. A Docker microservice fits in a FROM scratch image measured in megabytes, boots instantly, and has nothing extra to patch or CVE-scan.
Every snippet below is an actual file under examples/. It compiles and runs, verified on every build. No slideware.
import { go, Channel, select, defaultCase } from 'klain:sync'
const primes = new Channel<number>(1024) // primes stream to the collector
const done = new Channel<number>(0) // a worker signals when finished
// Fan out: 8 goroutines test interleaved slices, in parallel on every core.
for (let id = 0; id < 8; id++) {
go(() => {
for (let n = 2 + id; n <= 500000; n += 8) {
if (isPrime(n)) primes.send(n)
}
done.send(id)
})
}
// Fan in: select takes whichever channel is ready — count primes as they
// arrive, tally completions, then drain the last buffered results.
let count = 0, finished = 0
while (finished < 8) {
select(
primes.recvCase((p: number) => { count += 1 }),
done.recvCase((id: number) => { finished += 1 }),
)
}
let draining = true
while (draining) {
select(
primes.recvCase((p: number) => { count += 1 }),
defaultCase(() => { draining = false }),
)
}
console.log(`primes below 500000: ${count}`) // 41538app.tslexerSource text becomes a flat token stream.
emits tokensPratt descentRecursive descent with precedence climbing builds the tree.
emits ASTResolveProgramEvery transitive import is parsed and merged into one program.
emits one AST~60 emit_*.goSmall domain files write LLVM IR text, one main().
emits .llclangThe real backend turns IR into a host-arch executable.
emits binary./app → one .ll, one clang call, one generated main(), one binary that runs unsupervised.
Not German. Not a clothing brand. Klain Main isn't “klein Main” (a cute little main()), and it isn't the Augsburg streetwear label. It's Greek.
Κλάιν Μάιν is slang for “I don't care”, the good kind: do the thing anyway, even when everyone tells you it's futile, for no better reason than because you can. That's the whole reason this compiler exists, because “how would I even build one” turned out to be a far better rabbit hole than whatever the day's actual plan was.
Read the docs