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 quickjs-ng (parser removed on device); 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_quickjs) 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 · quickjs-ng · 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-protocolgenerates 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.
Spec index (repository spec/)
| Doc | Contents |
|---|---|
00-overview.md | Decision log and repo map |
01-language-semantics.md | Run-once semantics, transform rules, divergences |
02-compiler-pipeline.md | Build stages, ABI stamping |
03-host-interface.md | The __kog surface and C signatures |
04-native-runtime.md | Tasks, event loop, memory budgets, GC |
05-ota-versioning.md | Partitions, bundle format, signing, rollback |
06-boards.md | board.yaml schema, three-tier build |
07-hardware-io.md | HAL, pin registry, per-chip capability tables |
08-dev-server.md | Dev protocol, hot reload, discovery, flashing |
09-components-styling.md | Component surfaces, StyleSheet, navigation, FlatList contract |
10-roadmap.md | Post-v1: AOT tier, Fast Refresh, expanders |