Skip to main content

Architecture overview

This section is for contributors and the deeply curious. The authoritative, detailed specifications live in the repository's spec/ directory; this page is the orientation map.

The one-paragraph version

Kog compiles standard TSX into JavaScript that targets a fine-grained signals runtime: component functions run once at mount, building LVGL widgets directly through a ~10-function host interface, and every dynamic JSX expression becomes an effect subscribed to exactly the signals it reads. The app ships as precompiled bytecode running on MQuickJS, an embedded JS engine (parser removed on device) that executes bytecode in place from read-only flash — the property that makes Kog fit on a bare, PSRAM-less ESP32. The native side is a portable C core — one task owning both LVGL and the JS engine, an event loop where LVGL's timer wheel doubles as the JS scheduler, and a hardware layer on a separate worker task. The same C core compiles into the desktop simulator (SDL) and the ESP32 firmware.

Layer diagram

Your app (TSX, hooks) packages: @kog/react, @kog/ui, @kog/hardware
──────────────────────────────
Compiler (Babel via esbuild) @kog/compiler: reactive-read rewriting,
tsc --noEmit gate JSX → host calls, Show/For lowering,
style interning, prop-ID interning
──────────────────────────────
Signals runtime (JS, tiny) @kog/runtime: $signal/$memo/$effect,
batching, ownership/disposal, show/forEach
──────────────────────────────
__kog host interface ~10 functions; u16 IDs from kog-protocol
──────────────────────────────
Portable C core handle table, prop dispatch tables,
(kog_core, kog_bindings, kog_mquickjs) style interning, unified event loop, GC policy
──────────────────────────────
Ports kog_port_sdl + kog_hal_sim (simulator)
kog_port_esp + kog_hal_esp (ESP-IDF)
──────────────────────────────
LVGL v9 · MQuickJS · FreeRTOS/SDL

Load-bearing design decisions

  • No VDOM on a retained-mode renderer. LVGL widgets already persist; diffing a virtual tree against them would duplicate state on the device with the least RAM. Fine-grained effects write straight to widget setters.
  • One shared ID vocabulary. kog-protocol generates both the C enums and the compiler's tables from one JSON file; its version is the ABI. No property-name strings ever cross the wire or live on the device.
  • One task owns the UI. JS and LVGL share a thread by construction, so application code cannot race the renderer; everything else (network, hardware buses) marshals in through queues.
  • Bytecode-only devices. Apps are compiled to bytecode on the host; devices don't ship a JS parser — smaller, faster to boot, smaller attack surface.
  • The simulator is the runtime. Same C core, same bytecode, same dev protocol — hardware included, via a virtual pin table.
  • The engine behind one seam. All JS-engine API use is isolated in a single adapter, so the engine — MQuickJS, which runs bytecode in place from flash — sits behind a stable seam with everything above the __kog host interface engine-agnostic. (An earlier build used that seam to keep a second engine, quickjs-ng, swappable behind KOG_JS_ENGINE; Kog has since unified on MQuickJS and retired quickjs-ng.)

Spec index (repository spec/)

DocContents
00-overview.mdDecision log and repo map
01-language-semantics.mdRun-once semantics, transform rules, divergences
02-compiler-pipeline.mdBuild stages, ABI stamping
03-host-interface.mdThe __kog surface and C signatures
04-native-runtime.mdTasks, event loop, memory budgets, GC
05-ota-versioning.mdPartitions, bundle format, signing, rollback
06-boards.mdboard.yaml schema, three-tier build
07-hardware-io.mdHAL, pin registry, per-chip capability tables
08-dev-server.mdDev protocol, hot reload, discovery, flashing
09-components-styling.mdComponent surfaces, StyleSheet, navigation, FlatList contract
10-roadmap.mdPost-v1: AOT tier, Fast Refresh, expanders
11-web-simulator.mdThe WASM simulator + in-browser docs playground
12-web-flash-devlink.mdBrowser flashing + the framed serial dev-link
13-networking.mdZero-import fetch/WebSocket/sockets/Wi-Fi across all targets
16-rom-bytecode-inplace.mdMQuickJS: run bytecode in place from flash, deploy/relocation layout (draft)