mjsx
packages/core/src/mjsx.js is the whole portable engine — h(), layout
(width-in, height-out, no VDOM), hit testing, the pointer state machine,
text input with virtual keyboards, and modals — written in the ES5 subset
MicroQuickJS accepts, so the same file runs unmodified on a chip, in a
terminal, in a browser tab and in a native window. The contract is ten
gfx calls plus sys.millis() — clear, rect, frect, circle,
line, text, clip, unclip, width, height — and a backend is
whatever realizes those ten; nothing above that line changes per target.
New here, go to getting-started.md. Writing a backend, go to contract.md.
What it looks like
Section titled “What it looks like”
<box bg={UI.theme.panel} radius={8} border={UI.theme.accent} borderW={2} pad={em(1.5)}> <text text="Hello mjsx!" size={2} color={UI.theme.text} align="center" /></box>
<Button label="+1" size={2} onTap={function () { UI.set({ count: count + 1 }); }} />
<box flex={1} scroll="main" pad={em(0.75)} gap={em(0.5)}> {kids}</box>
h(Keyboard, { layout: kb, position: pos, height: Math.floor(gfx.height() / 2.6) })
{h(ArcFooter, { items: items, spread: 150, inset: 10 })}Documentation, by what you are trying to do
Section titled “Documentation, by what you are trying to do”Start here
Section titled “Start here”| Page | The question it answers |
|---|---|
| getting-started.md | Install, run an example in a window and in the terminal, write a first app, push it to a board. |
../examples/README.md (examples/README.md in the repo) | The fourteen shipped examples and what each one demonstrates. Every one is a single flat app.jsx with no imports and no build step. |
Building a UI
Section titled “Building a UI”| Page | The question it answers |
|---|---|
| ui.md | The app-author API: h() and JSX, every element and its props, UI.state/UI.set/UI.memo, the pointer model, keys and focus, timers, configStorage, safe insets. |
| layout.md | How box, row, flex weights, abs, clip/offX and scroll zones actually place things — width-in, height-out, and where the pixels end up. |
| fonts.md | Which face draws at which size, what em() snaps to, and why text measurement is a shared responsibility between core and backend. |

row: children side by side — an even split, a fixed w with the rest
sharing the remainder, and a pinned-height row where boxes stretch and
text centres.

A scroll zone 220px down its content. Drawing and hit areas both move,
and the offset persists across renders because it lives under the zone’s
name in UI._scroll.
<text text={'1EM = ' + em(1) + 'PX'} size={1} align="center" color={UI.theme.muted} />Components
Section titled “Components”| Page | The question it answers |
|---|---|
| components.md | The ready-made components — Button, input, Keyboard, ArcFooter — every prop, and the fact that all of them are built from the same box/row/text/abs any app has. |
| keyboards.md | The four layouts plus auto: how a layout is chosen from the width the keys actually get, shift and the symbol pages, T9 multi-tap, docking versus taking the whole display. |
| input.md | The text field: focus and caret, password, maxLen, overflow, where the keyboard goes, and how the mirrored field in exclusive mode is the same field. |
<input id={p.id} size={p.size || 2} placeholder={p.placeholder} password={p.password} maxLen={p.maxLen} label={p.label} exclusive={p.exclusive} onSubmit={function (v) { UI.set({ last: p.label + ': ' + v }); }} />Designing for a device
Section titled “Designing for a device”| Page | The question it answers |
|---|---|
| devices.md | The four-board ESP32-S3 fleet: which flag builds which board, first flash over chunked USB, every flash after that over OTA, WiFi provisioning, and the push → screenshot → tap loop. |
| Figures and screenshots | How every picture here was made, and how to reproduce or re-render one. |
| round.md | Round glass: safe insets, the chord a row can actually use, footers that follow the rim, and the extra scroll range at the end of every zone. |
| shapes.md | One screen on every shape in the fleet, side by side: the same focused examples/input on round, portrait, narrow-portrait, landscape and large-landscape glass, with what each shape changed. |
var round = UI.isRound();Hardware
Section titled “Hardware”| Page | The question it answers |
|---|---|
| hardware-api.md | sys.gpio(pin, op, value) and sys.i2c(addr, reg, value) on the ESP32 bridge firmware — what each op does, which pins the firmware refuses, and why there is no sys.uart. |
| sensors.md | Reading motion and the rest of the board from a script: what the host offers, how an app checks before calling, and what it shows when the hardware is not there. |
var HAVE = typeof sys !== 'undefined' && typeof sys.gpio === 'function';
Reference
Section titled “Reference”| Page | The question it answers |
|---|---|
| contract.md | The ten gfx calls and sys.millis() in full, the optional natives (poly, blit, store/fetch, font metrics), the host-declared round key, and how a backend drives the loop. |
| consistency.md | What each backend in this tree actually implements, measured against the contract — call by call, plus fourteen ranked divergences, each read out of the named source file. |

UI.theme — the colours every built-in component reads: eight named
swatches, and bg, the ninth, is the black they are drawn on. Colours
are 24-bit 0xRRGGBB throughout the API and each backend converts to its
own depth, so an app must not compare exact colours across backends.
theme: { bg: 0x000000, panel: 0x1b1e24, text: 0xffffff, muted: 0x98a1ae, accent: 0x4b8bf5, ok: 0x4ade80, warn: 0xfbbf24, err: 0xf87171, key: 0x212530},Contributing
Section titled “Contributing”| Page | The question it answers |
|---|---|
../CONTRIBUTING.md (CONTRIBUTING.md in the repo) | The one hard rule (the core file stays in the MicroQuickJS ES5 subset), how to add an example, and how to add a backend. |
bun backends/pure-js/src/run.js examples/yours/app.jsx out/yours.ppmbun backends/terminal/src/run.js examples/yours/app.jsxBy device
Section titled “By device”The fleet is four Waveshare ESP32-S3 touch-LCD boards, selected at build
time (docs/devices.md). Below is examples/layers — the same source,
no per-board branches — on each of them, plus a desktop window.
| Flag | Board | Panel | Touch |
|---|---|---|---|
| (default) | ESP32-S3-Touch-LCD-1.69 | 240x280 ST7789V2 | CST816T |
--b35 | ESP32-S3-Touch-LCD-3.5 | 320x480 ST7796 | FT6336 |
--b147 | ESP32-S3-Touch-LCD-1.47 | 172x320 JD9853 | AXS5106L |
--b128 | ESP32-S3-Touch-LCD-1.28 (round) | 240x240 GC9A01 | CST816S |
The round board is also the constrained one — 2MB quad PSRAM, a 1MB JS heap cap, and a CH343 UART bridge that needs chunked serial writes. Each of those cost real debugging, and devices.md records why.