Architecture

TinyFX architecture

TinyFX is one language with three executable target paths:

  1. Native Rust execution. The tfx CLI compiles source to a bytecode program, executes it in the Rust VM, and renders through the Rust wgpu runtime in a winit window.
  2. Browser Rust/WASM execution. The same bytecode program and Rust runtime are compiled to WebAssembly and render to a WebGPU canvas.
  3. Generated TypeScript/WebGPU execution. The compiler emits readable TypeScript plus compiler-authored WGSL and physical pass descriptions. That program runs against the JavaScript core in @tinyfx-lang/runtime; it does not load the TinyFX VM or a TinyFX WASM runner.

The target choice changes execution and host ownership, not the language front end. Parsing, checking, GPU legality, capture analysis, and WGSL generation have one Rust source of truth.

End-to-end shape

TinyFX sources + imports + embedded standard library
                     |
                     v
          checked Frontend
  (AST, definitions, types, semantic side tables, stage facts)
          /                         \
         v                           v
VM-specific lowering          generated-program lowering
bytecode + WGSL blueprints    structured CPU IR + physical GPU blueprints
         |                           |
         v                           v
  native or WASM VM            readable TypeScript/ESM
  self-contained .tfxb         JavaScript CPU/WebGPU core

WGSL is generated while lowering compile-time shader and compute sites. The runtime receives generated WGSL, layouts, binding descriptions, and packing plans; it does not recompile TinyFX ASTs during a frame.

The two lowerers deliberately remain siblings. The generated-program IR is not an intermediate representation for VM bytecode, and VM bytecode is not a serialized form of generated TypeScript. Both consume the checked Frontend and preserve the checker’s decisions rather than resolving calls, defaults, coercions, fields, captures, or resources again. See Compiler internals and The VM and artifacts.

Workspace map

AreaPrimary locationResponsibility
Compilercrates/tinyfx-compilerLexer, parser, checking, stage analysis, WGSL, generated-program IR, TypeScript emission, formatter, IDE analysis, operation catalog
Virtual machinecrates/tinyfx-vmBytecode lowering, interpreter, heap/GC, serialized-program format, host boundary
Rust renderercrates/tinyfx-runtimewgpu resource management, frame encoding, environment/camera, overlay, UI backends, assets, native media
CLIcrates/tinyfx-cliFilesystem imports, diagnostics, artifact emission, winit application and watch mode
WASM bindingscrates/tinyfx-wasmSeparate source-facing compiler/editor and bytecode-facing runner bindings
Language servercrates/tinyfx-lspLSP protocol adapter over compiler IDE analysis
Browser facadepackages/tinyfxBrowser-facing compile/check/editor API and lazy WASM runner loading
JavaScript runtimepackages/tinyfx-runtimeGenerated-program CPU/WebGPU core, shared platform contracts, browser platform, WASM bridge
Vite integrationpackages/tinyfx-viteBuild-time .tfx-to-VM modules, assets, declarations, and invalidation
Browser applicationapps/playgroundInteractive VM/WASM playground and generated-program example browser
VS Code extensioneditors/vscodeTextMate grammar and language-client process management
Standard librarystdTinyFX source compiled into every program

Rust dependency direction is:

tinyfx-cli / tinyfx-wasm  ->  tinyfx-runtime  ->  tinyfx-vm  ->  tinyfx-compiler
tinyfx-lsp                ->  tinyfx-compiler

The compiler has no wgpu dependency. This keeps type checking, editor analysis, and compiler-only WASM builds independent of graphics initialization.

The browser boundary

The browser has two independent execution cores:

  • The WASM core contains the Rust VM and Rust renderer. It accepts an already compiled bytecode artifact.
  • The generated JavaScript core interprets neither bytecode nor TinyFX source. It executes compiler-emitted CPU code and compiler-authored physical WebGPU blueprints.

Both implement the same browser-core shape and are driven by one shared BrowserPlatformSession. That session owns request-animation-frame scheduling, canvas backing-store size and device-pixel-ratio updates, focus, visibility, pointer/keyboard/text input, cleanup, browser media, and optional Canvas2D paint composition. The execution cores own only program semantics and rendering. This prevents browser lifecycle policy from drifting between the WASM and generated-program paths.

The browser facade in packages/tinyfx keeps the compiler/editor WASM separate from the runner. Calls such as check, completion, hover, and source compilation load only the compiler artifact. run() first compiles valid source to bytecode, then dynamically imports the runner, so an invalid program never downloads the larger execution artifact.

The generated TypeScript target instead imports @tinyfx-lang/runtime/core. Its optional browser embedding imports the platform boundary separately. The runtime package intentionally exposes subpaths rather than a catch-all root module so applications own only the layers they need.

Shared contracts and artifacts

TinyFX has several separately versioned contracts. They should not be treated as one global format revision:

  • .tfxb is the self-contained VM artifact. It contains bytecode, strings, functions, type/runtime metadata, WGSL blueprints, layouts, and packing plans.
  • Program metadata is a stable, compiler-owned entry/per-file view used by source tooling and compiled artifacts.
  • Generated TypeScript declares the capabilities it needs from the JavaScript core.
  • Physical render and compute descriptions are strict compiler-to-WebGPU DTOs. New capabilities are added through explicit shapes rather than silently widening old shapes.
  • Builtin wire keys have append-only order because serialized bytecode refers to those keys.

The practical rule is simple: consumers validate the contract they receive, and a source artifact should be rebuilt when its reader reports an incompatible format.

Where to go next