Concept
Compile pipeline.
What happens between .xgis source
and a rendered frame: how the language turns into WGSL the GPU can run.
Stages
Lexer + Parser
The lexer in
@xgis/compiler recognizes the
usual tokens (identifiers, numbers, strings, punctuation) plus
keywords reserved by the language:
source,
layer,
background,
preset,
symbol,
keyframes,
fn,
and the modifier prefix z<N>:.
The parser produces a typed AST (see
compiler/src/parser/ast.ts):
every top-level statement becomes one of
SourceStatement,
LayerStatement,
BackgroundStatement,
PresetStatement, etc.
AST → IR (Scene)
lower() walks the AST and
produces the Scene IR — a flat list of
RenderNode records, one per
layer, with utility classes already resolved into structured
fields (fill,
stroke,
opacity,
size, etc.). Presets get
expanded inline. Keyframes get attached to their referencing
layers.
The IR is what the optimizer reads and what the codegen writes from. It deliberately knows nothing about WGSL or GPU buffers — that keeps the language semantics testable in pure TypeScript.
Optimizer: constant fold + classify
Every expression is classified into one of four buckets:
| Expression | Class | Handling |
|---|---|---|
| 360 / 12 | constant | Folded to 30 at compile time |
| opacity-[interpolate(zoom, 8, 40, 16, 100)] | zoom-dependent | CPU-interpolated per frame |
| speed / 50 | clamp(4,24) | per-feature GPU | WGSL codegen, evaluated in vertex shader |
| threat_size(5) | constant (user fn) | User function inlined and folded |
Classification decides which pipeline the layer needs and which uniforms get uploaded each frame.
Codegen: WGSL shader variants
Layers with no per-feature GPU expressions share a common
tile_default shader pipeline.
Layers that DO use per-feature expressions (e.g.,
fill match(.continent)) get
a custom WGSL variant emitted at compile time — the
match table becomes a
chained if/elif in the fragment
shader, with branch values constant-baked from the IR.
Variants are deduplicated by content hash so a 50-layer scene with identical styling produces one pipeline, not 50.
Runtime
@xgis/runtime consumes the
emitted SceneCommands and ShaderVariants, owns the GPU device,
manages tile catalogs (PMTiles MVT / GeoJSON), and
dispatches per-frame draws. WebGPU is the primary backend; Canvas
2D engages as fallback when no adapter is available.
The runtime is the only component that touches the GPU. Compiler unit tests can fully exercise the language without a graphics context.