PocketJS is a standalone cross-platform UI engine that lets you write Solid or Vue Vapor components and run them at 60 FPS on a 333 MHz Sony PSP with an 8 MB memory budget — and on every other host you use to build and test them. It has no DOM, no runtime CSS, and no layout engine in JavaScript. Layout, styling, animation, and text all live in a compactDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/pocket-stack/pocketjs/llms.txt
Use this file to discover all available pages before exploring further.
no_std Rust core. You write components the same way you always have; PocketJS handles everything from there.
What PocketJS Is
The PSP is not a hypothetical constraint. It is a real 2005 handheld with a 333 MHz MIPS CPU, 32 MB of system RAM, a hardware 3D engine (the GE), and no browser. Making a JSX UI framework run there at 60 FPS — with animated transitions, reactive signals, flexbox layout, and sub-pixel text — requires a different kind of architecture than a typical web UI library. PocketJS is that architecture. It moves everything weight-bearing out of JavaScript and into a Rust core that compiles to either bare-metal MIPS orwasm32. The JavaScript layer stays thin: it holds your component state, your component tree, and the reactive signal graph — but it never runs layout, never rasterizes text, and never drives animation frame by frame. Those jobs are done in native code at a fixed 1/60 s timestep.
The result is a framework that feels like Solid or Vue because it is Solid or Vue — you import createSignal from solid-js, ref from vue, and your standard lifecycle hooks. PocketJS supplies the host components, the styling pipeline, the input model, and the native runtime wiring.
The Three Pillars
1. Framework adapters over one native tree. Solid and Vue Vapor are first-class, equal citizens. Both use the same retained native tree and the sameHostOps surface. Solid uses babel-preset-solid in universal mode; Vue Vapor uses vue-jsx-vapor. Switching frameworks changes the JS reactivity layer and component model — not the Rust core, the styling pipeline, the input model, or the .pak format. You get all of PocketJS either way.
2. A build-time Tailwind-subset compiler with zero runtime CSS. Class strings are parsed at build time. A literal like class="px-4 py-2 rounded-xl bg-blue-600 focus:bg-blue-500" compiles to a numeric style record if and only if every whitespace-separated token is a supported utility. The compiler writes a binary style table (styles.bin) and a generated lookup; at runtime a class is just a styleId. There is no CSS engine on the device. Dynamic styling is expressed as ternaries of whole class literals, style={{…}} objects, or animate(). classList, hover:, and template-interpolated class fragments are compile errors, not silent no-ops.
3. Baked font atlases. Text uses Inter (OFL), vendored under assets/fonts/. At build time an opentype.js-based baker scans your source for the exact characters and font sizes you actually use and rasterizes only those atlas slots — horizontally supersampled, 8-bit coverage cells with proportional advances and a cmap. There is no font rasterizer on the device. Drawing text means compositing pre-baked coverage. Because only used glyphs are baked, the atlas is compact; because they are baked once, drawing is cheap.
The Four Hosts
One built app artifact runs on all four hosts without modification:| Host | JS engine | Rust build | Backend |
|---|---|---|---|
| PSP hardware | QuickJS (Bellard 2025, ~ES2023) | mipsel-sony-psp | sceGu (the GE) |
| PPSSPP | QuickJS | mipsel-sony-psp | sceGu, run headless for e2e goldens |
| Browser | The browser’s own engine | wasm32-unknown-unknown | Deterministic software rasterizer → 480×272 canvas |
| Headless Bun | Bun | wasm32-unknown-unknown | Same rasterizer → byte-exact PNG goldens |
mipsel-sony-psp) to produce the PSP EBOOT, and once to wasm32-unknown-unknown to serve both the browser dev host and the headless Bun golden tests. Because animation ticks at a fixed dt = 1/60 s and frame content is a pure function of the frame index, goldens are byte-exact rather than fuzzy — the same code path runs everywhere.
Who It Is For
PocketJS is for JavaScript and TypeScript developers who want to build real, animated UI for the PSP — launchers, menus, dashboards, small apps — without writing C or hand-rolling a layout engine. You get a familiar reactive component model, utility-class styling, and a fast build pipeline. PocketJS handles host detection, the generated style table, image uploads, and the per-frame host callback. You write components. It also works well as a constrained-hardware proving ground. If your UI runs correctly on a 333 MHz MIPS chip inside an 8 MB arena, the browser host is effortless by comparison. The headless Bun golden tests and the PPSSPP e2e tests give you byte-exact visual regression coverage for free.Hello, Counter
A complete app: a counter driven by d-pad focus and the Circle button.- Solid
- Vue Vapor
focus:bg-blue-500andactive:bg-blue-700are variants baked into the style record. Focus and active-state style changes swap style IDs natively — zero JS on each transition.transition-colors duration-150declares motion. The tween ticks in Rust at a fixeddt = 1/60 s. JS only declares it.Count: {count()}/Count: {count.value}is a mixed text run — a static string and a reactive expression laid out as one inline run by the text engine, not as two separate flex items.Show/ the conditional expression are standard framework control flow. When they toggle, the Solid reconciler’sremoveChild/insertBeforecalls cross the FFI boundary; the Rust core handles the rest.
mount from @pocketjs/framework handles host detection, feeds the generated style table, uploads pak images, and wires the frame callback:
- Solid
- Vue Vapor
What v1 Punts
PocketJS v1 is deliberately scoped. These are features not yet implemented — unsupported patterns that fall into this category surface as loud compile-time or dev errors rather than silent failures:- Kinetic / momentum scroll views — not yet implemented
hover:variants — there is no pointer on a PSProunded-fullon runtime-sized nodes — requires build-time-knownw-N h-Nin the same literal- Percentage sizes beyond
-full - CLUT / swizzled textures
- Render-to-texture opacity groups — per-vertex alpha propagation is used instead
- Kerning
- 3DS / Android hosts
Quickstart
Clone the repo, write your first component, build it, and run it in the browser dev host or on real PSP hardware.
Architecture
How one Rust core, two framework adapters, and four host backends compose into one cross-platform UI stack.
Components
View, Text, Image, control flow, and app-shell primitives like FocusGrid, Modal, and Portal.Playground
Run the demos in your browser — no local install needed. The same Rust core, compiled to WASM.