Goeteia: A pure Scheme web programming tool compiled to WebAssembly

Goeteia is a self-hosting Scheme compiler that compiles directly to WebAssembly in the browser, leveraging Wasm GC and tail calls for high performance without requiring a backend server.
Everything beside this is rendered by Goeteia.
The Scheme below is compiled to WebAssembly in your browser and mounted live.
No server compiles this — the page carries the whole compiler (goeteia.wasm
, ~70 KB gzipped, cached after first load), and each Run recompiles the source above in ~15 ms.
The compiler is written in the Scheme subset it compiles. The self-hosted build recompiles itself and the output is byte-identical — the fixpoint is checked in CI fashion on every change, and every test runs through both stages.
Fixnums are unboxed i31ref
s, pairs and records are GC structs, eq?
is one ref.eq
. No shadow heap in JavaScript: the host supplies two byte-stream imports and nothing else.
syntax-rules
and procedural syntax-case
with fenders, nested ellipses and datum->syntax
, running in a compile-time interpreter with hygiene by renaming.
Typed function references with a fast per-arity entry and a generic entry per closure — variadic procedures and apply
are cheap, and every tail call is a return_call
. A 100M-iteration loop runs in constant stack, in ~150ms.
Escape continuations ride the Wasm exception-handling proposal: capture is O(1), the normal path costs one try block, and winders unwind inner-to-outer on the way out.
(web sx)
templates over fine-grained (web reactive)
signals, an (web html)
renderer, and a (web js)
FFI that reaches straight into the host — this page is built with it.
(web three)
builds reactive Three.js scenes the way sx
builds DOM; (web gl)
drives raw WebGL through a command buffer — one bridge call per frame — with shaders written as s-expressions in (web glsl)
. The title above is exactly this: dot-matrix glyphs, a vertex shader, one draw call.
When the backend is also Scheme (Igropyr
), requests and replies are s-expressions — (rpc "/rpc" '(add 1 2 1/2))
comes back (ok 7/2)
, the exact ratio intact. (web fetch)
makes it direct-style over Wasm JSPI; (web ws)
/ (web sse)
push datum streams; (web json)
handles everyone else.
R6RS-style (library ...)
files with (import (math utils))
resolution, dependencies first; exports are advisory because unused code is pruned anyway.
$ git clone https://github.com/guenchi/Goeteia
$ cd Goeteia
$ ./run-tests.sh # every test, both compiler stages
$ ./build-self.sh # rebuild the compiler with itself
$ echo '(define (fact n) (if (zero? n) 1 (* n (fact (- n 1)))))
(fact 20)' > fact.ss
$ node rt/compile.mjs goeteia.wasm fact.ss fact.wasm
$ node rt/run.mjs fact.wasm
2432902008176640000
Compiled modules run on any engine with Wasm GC and tail calls: Node 22+, current Chrome / Firefox / Safari, wasmtime. Bootstrapping from source needs Chez Scheme; the checked-in compiler wasm works without it.
Source: Hacker News













