
Discover frontend products found across trusted public sources.

<h3> PocketJS</h3> <p>High-performance component UI outside the browser, with native rendering, standard Vue Vapor and Solid support, a Tailwind design system, and 60 FPS animation under an 8 MB memory budget. Write Solid JSX, Vue Vapor JSX, or Vue single-file components, run them on QuickJS, and let PocketJS move layout, styling, text and animation into a tiny <code>no_std</code> Rust core.</p> <p>It runs on real PSP and PS Vita hardware, PPSSPP, Vita3K, the browser (WASM), native macOS windows (wgpu) and headless Bun. Full design + contracts: <a>docs/DESIGN.md</a>. PocketJS is growing into a family of specialized runtimes — Rust cores, spec-pinned surfaces, one QuickJS guest — documented in <a>docs/RUNTIMES.md</a>; the 3D base lives in <a>engine/pocket3d/</a>, and its first game runtime is <a href="https://github.com/pocket-stack/open-strike" target="_blank" rel="noopener">OpenStrike</a>.</p> <h3>Screenshots</h3> pocketjs-hardware-demo.mp4 <p>These PocketJS UIs run smoothly at 60 FPS on a Sony PSP within an 8 MB memory budget, including animated transitions and input feedback.</p> <table> <thead> <tr> <th>Gallery</th> <th>Settings</th> </tr> </thead> <tbody> <tr> <td></td> <td></td> </tr> <tr> <td><strong>Motion Lab — baked keyframe timelines</strong></td> <td><strong>Motion Lab — 3D pipeline</strong></td> </tr> <tr> <td></td> <td></td> </tr> </tbody> </table></markdown-accessiblity-table> <h3>Quickstart</h3> <pre>bun install bun run bootstrap # one-time pinned PSP toolchain setup bun pocket check --target psp # schema + capabilities + ordinary app TypeScript bun pocket compile --target psp # check + emit JS/pak from the resolved plan bun pocket build --target psp -- --release # Low-level compiler commands used by framework apps/tests: bun tools/build.ts hero # -> dist/hero.js + dist/hero.pak bun tools/build.ts hero-vue-vapor-main --framework=vue-vapor bun tools/build.ts hero-vue-sfc-main --framework=vue-vapor</pre> <p>Or drive everything through the <a href="https://www.npmjs.com/package/@pocketjs/cli" target="_blank" rel="noopener"><code>pocket</code> CLI</a>: <code>npm i -g @pocketjs/cli</code>, then <code>pocket doctor</code> checks the Bun / Rust / PSP toolchain (<code>pocket setup</code> runs the same pinned bootstrap), <code>pocket create <name></code> scaffolds a format-2 manifest, and <code>pocket check|compile|build --target psp|vita</code> delegate to the canonical resolver. Low-level host-development commands such as <code>pocket dev</code>, <code>pocket psp</code>, <code>pocket vita</code>, and <code>pocket play</code> remain available.</p> <p>The build is two-pass: pass 1 transforms every module reachable from the entry (framework-specific JSX + TypeScript, or Vue SFC compiled directly to Vapor; content-hash cached in <code>.cache/</code>) while collecting class strings + text codepoints from the AST. The Tailwind compiler then writes <code>styles.bin</code> + <code>framework/src/styles.generated.ts</code>, the font baker rasterizes Inter atlas slots for exactly the characters your app uses, and everything is packed into <code>dist/<app>.pak</code>. Pass 2 bundles the cached transforms with Bun (iife, unminified).</p> <pre>import { createSignal } from "solid-js"; import { Text, View } from "@pocketjs/framework/components"; export default function Counter() { const [count, setCount] = createSignal(0); return ( <View class="flex-col items-center gap-4 p-4 bg-slate-50"> <Text class="text-xl text-slate-950">Count: {count()}</Text> <View class="p-2 rounded-md bg-blue-600 focus:bg-blue-500 transition-colors duration-150" focusable onPress={() => setCount(count() + 1)} /> </View> ); }</pre> <p>Mounting entries should look like ordinary app bootstrap code; the framework handles host detection, the generated style table, pak image uploads and the host frame callback:</p> <pre>import { mount } from "@pocketjs/framework/solid"; import App from "./app.tsx"; mount(() => <App />);</pre> <p>Styling rules (compile-time, no runtime CSS): a class literal compiles iff <em>every</em> token is a supported utility (see docs/DESIGN.md "Tailwind subset (v1)"); dynamic styling is ternaries of full literals, <code>style={{...}}</code>, or <code>animate()</code>. <code>classList</code>, <code>hover:</code> and template-interpolated classes are compile errors. <code>rounded-full</code> requires <code>w-N h-N</code> in the same literal.</p> <p>Framework selection is explicit: product builds set <code>app.framework</code> to <code>"solid"</code> or <code>"vue-vapor"</code> in <code>pocket.json</code>. Low-level framework/compiler/host work can still use <code>pocket.config.ts</code> or pass <code>--framework=...</code> to the individual scripts. App state and component lifecycle come from the native framework package (<code>solid-js</code> or <code>vue</code>); PocketJS supplies host components, input, animation, assets and native runtime wiring.</p> <h3>Vue single-file components</h3> <p>Set <code>"framework": "vue-vapor"</code> in <code>pocket.json</code>, then import <code>.vue</code> files from the app entry. PocketJS compiles them in Vapor mode, so ordinary <code><script setup></code> is enough; no <code>vapor</code> attribute is required in the SFC.</p> <pre>import { mount } from "@pocketjs/framework/vue-vapor"; import App from "./App.vue"; mount(App);</pre> <p>The supported shape is <code><script setup></code> plus <code><template></code>. Import reactivity from <code>vue</code> and host components from <code>@pocketjs/framework/vue-vapor/components</code>. Runtime <code><style></code> blocks, template preprocessors, external blocks, and Options-API-only components are not supported.</p> <p><a><code>apps/hero-vue-sfc</code></a> renders the same screen as the JSX Hero demos. See <a><code>apps/vue-sfc-lab</code></a> for <code>v-model</code>, conditionals, lists, props, events, and slots.</p> <p><code>@pocketjs/framework/components</code> also exposes small app-shell primitives: <code>Screen</code>, <code>Focusable</code>, <code>FocusScope</code>, <code>ActionHandler</code>, <code>FocusGrid</code>, <code>Portal</code>, <code>Modal</code>, and <code>ActionBar</code>. <code>FocusGrid</code> gives a subtree explicit row/column d-pad traversal today; a virtualized grid can sit behind the same focus contract later. <code>Portal</code> mounts into the runtime overlay root, so modal/action-bar UI never participates in the active screen's flex layout. <code>Modal</code> owns a focus scope and blocks background button handlers while leaving frame animation lifecycle callbacks running; route switching is still ordinary app state, not a required router package.</p> <h3>Commands</h3> <pre>bun run bootstrap # idempotent PSP toolchain setup bun play vita hero # build, install and launch in Vita3K bun play vita gallery --fullscreen # stretch to the host's full screen bun play --help # list every runnable demo bun run test # spec contract + tailwind parser tests bun pocket check --target psp # validate pocket.json + resolved target contract bun pocket compile --target psp # typecheck and compile, for custom native hosts bun pocket build --target psp # typecheck, compile, and package the target pocket build --target vita -- --release pocket play vita hero # build, install and launch in Vita3K bun tools/build.ts <app> [--framework=solid|vue-vapor] [--extra-chars=…] bun run psp <app> # low-level PSP demo build bun run vita <app> # low-level Vita demo build bun run dev [app] # browser dev host bun run wasm # rebuild the wasm core bun run e2e:vita # Vita3K, native-density 960x544 golden E2E bun run e2e:launcher:vita # Vita3K, multi-app launcher/swap E2E bun psplink # interactive real PSP switcher over PSPLINK bun run hw hero --trace # real PSP via PSPLINK + host0 trace bun symbian doctor --device # inspect the Nokia E7 toolchain and USB MTP path bun symbian doctor --coda-usb # verify CODA 4.x directly over USB interface 4 bun symbian build probe # build a visible self-signed Qt/Symbian SIS bun tools/symbian.ts build app --manifest apps/hero/pocket.json --sis-version 1.0.0 bun symbian deploy dist/symbian/pocketjs-e7-runtime.sis bunx tsc --noEmit # typecheck (babel owns the JSX transform)</pre> <p>The Nokia E7 bootstrap and its current PocketJS port boundary are documented in <a>docs/SYMBIAN_E7.md</a>. It establishes a pinned, repeatable GCCE/Qt/QuickJS/Rust/E32/SIS build and physical-device staging path. The private <code>symbian-e7-dev</code> profile can package PocketJS applications for hardware testing with a native viewport that follows the E7's landscape and portrait geometry, but it is intentionally absent from the production target registry until device acceptance and golden testing are complete.</p> <p>The PSP bootstrap owns every production input: Rust nightly + <code>rust-src</code>, the <code>cargo-psp</code> tools built at an exact <code>pocket-stack/rust-psp</code> revision, and a SHA-256-verified <code>pocket-stack/pspdev</code> SDK release. It installs them under the shared <code>${XDG_CACHE_HOME:-~/.cache}/pocket-stack</code> cache, so independent PocketJS and downstream-app checkouts can reuse one installation. Set <code>POCKET_STACK_CACHE_DIR</code> to move that cache. An explicit <code>PSP_SDK</code> takes precedence over <code>PSPDEV</code>, which takes precedence over the pinned cache; an invalid explicit path fails rather than silently selecting another SDK. PSP builds export both variables to the resolved path. PSPLINK is only needed by the real-hardware <code>hw</code>/<code>psplink</code> loop, not to compile an EBOOT.</p> <p>Manifest-driven builds resolve <code>pocket.json</code> once into a small <code>ResolvedBuildPlan</code>. The JS/font/pak compiler and native backend consume that same serialized plan; <code>planHash</code> is only its build-time checksum. At startup, the bundle checks the native host's target and HostOps ABI. The app entry and its reachable imports use the app's ordinary TypeScript configuration.</p> <p>The selected target profile also resolves <code>viewport.rasterDensity</code>. Layout and DrawList coordinates stay in the app's logical viewport; the compiler bakes font coverage and SVGs at that density, prefers same-directory <code>@2x</code> PNG, sprite and raw-PAK siblings when present, and asks the core to bake its own masks at the same density. Dynamic texture producers read the identical value from <code>platform.pixelRatio</code>—they never branch on a target name:</p> <pre>import { platform } from "@pocketjs/framework/platform"; const canvas = makeTexture(logicalWidth * platform.pixelRatio);</pre> <p>Missing raster siblings deliberately fall back to the 1x source; malformed siblings fail the build if their dimensions do not preserve logical size.</p> <p>Capabilities are plain framework API identifiers. <code>requires</code> must exist on the selected host; <code>enhances</code> resolves to booleans available from <code>@pocketjs/framework/platform</code>:</p> <pre>import { hasFeature } from "@pocketjs/framework/platform"; if (hasFeature("input.analog.left")) installAnalogNavigation(); else installButtonNavigation();</pre> <p>Literal <code>hasFeature()</code> calls are folded to booleans during a manifest build, so the unavailable branch is absent from the target bundle. <code>platform.features</code> remains available for computed queries and introspection. Capabilities describe fixed host API support, not permissions or live device state. Custom native hosts should use <code>extractHostBuildInputs()</code> and <code>hostBuildEnvironment()</code> from <code>@pocketjs/framework/manifest</code>; the complete Plan remains an internal build IR.</p> <p>The complete design, including authority boundaries, compatibility rules, typed backend dispatch, target/ABI runtime checks, extension points, and current limitations, is documented in <a>Platform contracts</a>.</p> <p>The Vita host is documented in <a>hosts/vita/README.md</a>. It preserves PocketJS's 480x272 logical layout while rasterizing geometry, fonts, vectors and core masks at Vita's native 960x544 density. Physical controls, left-analog input, and front-panel multi-touch snapshots are supported; PSP builds retain their controller-only fallback.</p> <h3>The Pocket Launcher (on-device app switching)</h3> <p>One PSP EBOOT or Vita VPK can embed every app admitted by that target plus a Cover Flow launcher (<a>docs/LAUNCHER.md</a>): pick an app with the deck, press SELECT inside any app to summon the deck back over a frozen shot of where you were, then resume or pick another. Switching is a whole-guest swap (fresh QuickJS realm + core + guest GPU resources per app); three append-only surface ops (<code>appTable</code>/<code>appLaunch</code>/<code>appShot</code>) carry the protocol, and ordinary single-app packages retain the original path.</p> <pre>bun run launcher scan --target vita # target admission -> registry bun run launcher covers --target vita # + deterministic sim covers bun run launcher build --target psp -- --release # -> multi-app PSP EBOOT bun run launcher build --target vita -- --release # -> dist/vita/launcher-main.vpk bun run e2e:launcher # PPSSPPHeadless 3-swap journey bun run e2e:launcher:vita # Vita3K 7-swap/resource-reuse journey</pre> <h3>DevTools + time travel</h3> <p>Pocket DevTools (<a>docs/DEVTOOLS.md</a>) is built into every bundle: a component tree with semantic names (<code>debugName</code> / <code><Named></code>), hover-to- highlight <strong>on the device screen</strong> (real PSP included, over the PSPLINK USB cable), pause/step, a REPL, <code>console.log</code> from hardware, and an always-on input-tape flight recorder — sessions replay byte-exactly because the whole runtime is fixed-dt deterministic.</p> <pre>bun run devtools # panel + hub + USB bridge, one process bun run devtools cards # + build, link and launch cards on a real PSP bun run tape replay <app> <tape.json> --png 60 # render any frame headlessly bun run tape:check # session-golden replay regression</pre> <p>On-demand screenshots (📷 in the panel) work in the browser host and through the PSP mailbox. On a real PSP the raw VRAM rides the usbhostfs mount and the bridge encodes the PNG desktop-side. Vita exposes the core inspection ops, but its DevTools transport and screenshot capture are not wired yet.</p> <h3>Determinism + the sim host</h3> <p>Time is a frame counter, not the wall clock (<a>docs/DETERMINISM.md</a>): the virtual clock (<code>@pocketjs/framework/clock</code>) makes the simulation rate a host policy (<code>?hz=2</code> on the web host runs the 2 FPS world on a real screen), the effect shell (<code>@pocketjs/framework/effects</code>) quantizes async results onto frame boundaries, and the headless sim host (<code>hosts/sim/</code>) replays scripted user journeys as byte-exact per-frame pixel traces — <code>tests/sim.test.ts</code> is the proof, <code>tools/flake-lab.ts</code> the wall-clock control experiment.</p> <p>Fonts: Inter (OFL), vendored in <code>assets/fonts/</code>.</p>
Our AI finds products from public sources, then rotates exposure so useful newcomers have a fair chance to be seen.
Learn how discovery works