Skip to content

Repository files navigation

PocketJS

Create on every screen you love. Build apps and games for your favorite devices, from a PSP to your desktop, with familiar JavaScript components and a compact native runtime.

@pocketjs/framework @pocketjs/cli license: MIT Discord

Website · Playground · Documentation · Blog · Changelog · Discord · X

Applications are written in TypeScript with Solid, Vue Vapor or Octane components, which all render to one native tree. A QuickJS guest runs the application, and a Rust core performs flexbox layout and draws every pixel in one thread inside one process. There is no DOM, no CSS engine and no WebView. PocketJS is a Pocket Nexus project.

A wall of PocketJS software: music, deep-zoom graphics, messaging, a digital character, galleries, DevTools, dashboards, media, and a café app, alongside OpenStrike, Pocket Voxel and Pocket Figma on PSP, including Motion Lab studies credited to yui540

In this repository

Beside the PocketJS runtime, this repository holds the technology that Pocket projects build on:

What it is Entry point
PocketJS The application runtime: TypeScript components, a QuickJS guest and a Rust core that lays out and draws every pixel This README · pocketjs.pocket.nexus
Pocket3D A hardware-native 3D stack: device kernels for GXM, GE and PICA200, and the mechanisms that purpose-built 3D engines share pocket3d/ · 3d.pocket.nexus
MicroTS An ahead-of-time compiler from TypeScript views and models to Rust, in development microts/

Contents

Familiar tools

Three TypeScript framework adapters target the same native tree and run on the same QuickJS guest. The choice changes application code and nothing below it.

Framework State primitives Source forms
Solid solid-js TSX
Vue Vapor vue TSX and <script setup lang="ts"> single-file components
Octane octane Compiled hooks and TSX, with no virtual DOM

Framework primitives are imported from solid-js, vue or octane. PocketJS owns the runtime, host components, lifecycle wiring, input, animation, assets and the native boundary.

import { createSignal, Show } from "solid-js";
import { mount } from "@pocketjs/framework/solid";
import { Text, View } from "@pocketjs/framework/solid/components";

function Counter() {
  const [count, setCount] = createSignal(0);

  return (
    <View class="w-full h-full flex-col items-center gap-4 p-4 bg-slate-50">
      <Text class="text-xl text-slate-950 font-bold">Count: {count()}</Text>
      <View
        class="px-4 py-2 rounded-xl shadow-md bg-blue-600 focus:bg-blue-500"
        focusable
        onPress={() => setCount(count() + 1)}
      >
        <Text class="text-base text-white font-bold">Press Circle</Text>
      </View>
      <Show when={count() > 3}>
        <Text class="text-sm text-emerald-600">Reactive on real hardware.</Text>
      </Show>
    </View>
  );
}

mount(() => <Counter />);

Styling

Class literals are compiled into a baked style table at build time. The runtime resolves a class attribute by lookup, so there is no CSS parser, cascade, specificity resolution or reflow on the device. The accepted vocabulary is a fixed Tailwind subset, enumerated in Styling.

One bundle, many screens

One bundle serves machines of different densities. An application declares its viewport and required APIs in pocket.json, and a target profile must satisfy that declaration before compilation and packaging proceed. Run from the application directory:

pocket create my-app
pocket check --target psp     # ok  480x272 · text.glyphs.baked · input.buttons
pocket check --target vita    # ok  same bundle, density 2, no component edited

From your component to a pixel

The guest emits tree mutations; the core owns layout, the style table and the draw list; a per-target backend submits that draw list through GE, GXM, Metal, wgpu, software rasterization, e-ink updates or another declared host.

PocketJS · 1 thread, 1 process
  your component                        guest
  renderer adapter                      guest
  native tree                           core
  flexbox layout, baked style table     core
  drawlist                              core
  backend draw                          core
  → pixels

Browser or WebView · 4 threads, 2 processes
  your component                        main
  framework runtime, vdom diff          main
  dom mutation                          main
  cssom, cascade, specificity           main
  style recalculation                   main
  layout, reflow                        main
  paint records                         main
  commit the layer tree across threads
  layer tree, tiling                    compositor
  queue raster tasks, invalidations     compositor
  rasterization                         raster pool
  ipc to the gpu process, sync fences
  draw quads                            gpu
  present                               gpu
  → pixels

See also: Architecture · Frameworks

Smooth motion

Keyframe timelines and spring curves are baked into the same style table at build time and advanced by the Rust core on its own clock, so a screen animates with no per-frame JavaScript. Motion Lab runs the yui540 studies in WebAssembly on the website, inside an interactive PSP model, and on the handheld they were written for.

Baked keyframe timelines · (yui540) 3D motion pipeline · (yui540)
Motion studies by yui540: menu, d-pad, share, hover, reload and keypad animations 3D motion studies by yui540: door, cubes, page flips and room transition

Small footprint

With no browser engine in the pipeline, the cost of a screen stays close to what the hardware can do. A complete application drawing an animated interface occupies 8 MB on a single 333 MHz core: a quarter of the PSP's 32 MB, one part in 1536 of a 12 GB iPhone 17 Pro Max, on a core clocked 13 times slower than an A19 Pro performance core at 4.26 GHz.

On a Sony PSP

One MIPS core at 333 MHz, 32 MB of RAM, measured against the 16.67 ms budget for 60 fps:

Measurement Result
OpenStrike frame budget 2.2 ms of JavaScript, 8.4 ms of total CPU work, worst observed frame 9.7 ms
Hero demo, cost of a virtual DOM Solid 15.15 ms · Vue Vapor 16.74 ms · Vue with a virtual DOM 90.75 ms
Hero demo, the three shipped frameworks Solid 3.66 ms · Vue Vapor 3.61 ms · Octane 6.53 ms

Seven samples per application. The two hero-demo rows come from separate runs with different toolchain versions, so each row is comparable internally but not against the other.

On a desktop

Historical August 2026 results for the previous gpui host: the same markdown editor built three ways on an Apple M3 Max (full report, reproduced by bun tools/bench-desktop.ts):

pocket Tauri v2 Electron
Processes 1 4 5
Cold start to first painted frame 149 ms 380 ms 301 ms
Idle resident memory 83 MB 193 MB 382 MB
On disk 10 MB 9 MB 242 MB

With a document open and no input, the pocket build redraws about twice a second, for the blinking caret. The report also records where the pocket build loses: its storm CPU rises with document length, because the editor re-wraps the whole document through the QuickJS interpreter on every keystroke.

See also: Shipping OpenStrike · Pocket Character · Twice the pixels, zero forks · The first iPhone

Replay every frame

PocketJS advances an application one frame(buttons) call at a time, and time is the frame counter. A frame is a transaction that nothing outside it can interrupt, and nothing waits on a wall clock, so tests replay the same sequence as fast as the CPU allows without changing the timing they measure: a journey that takes six seconds in front of a user is a few dozen frames in CI.

state n+1 = F(state n, input n)
pixels n  = G(state n)
  • Effects land on frame boundaries. A network reply that arrives partway through frame +3 is queued, not applied. It is delivered at the start of frame +4, in FIFO order, before any application hook runs. There are no microtask races and no mid-frame callbacks, and after() replaces setTimeout with a deadline measured in frames.
  • An async task lands on the same frame every run. Driven by requestAnimationFrame against a wall clock, one awaited confirmation lands on 22 different frames across 60 runs, and its timing assertion passes 9 times out of 60. On the frame clock it lands on frame 144 in every run, 60 out of 60.
  • History is a data structure. Tapes replay byte for byte, a session subsampled to 2 Hz is byte-identical to its 60 Hz counterpart, and forking a tape at frame 9 to splice in a different press produces the other outcome in 22 ms.
  • Chaos mode checks the guarantee, injecting sleeps, allocation churn and forced GC between frames without moving the trace by one bit.

See also: The runtime that can't flake · Time-travel DevTools · Determinism

Apps and games

Apps and games share the same runtime. Cores are independent native modules, loaded the way a kernel loads drivers: an application takes the ones its content needs, such as networking, audio or 3D, and the rest never enters the build. The JavaScript uses them beside the same UI and input APIs.

TypeScript application → guest bundle, one frame at a time
  ui       tree, layout, draw, input, focus
  net      poll batches
  audio    pcm mixer
  strike   bsp, bots, hits
  voxel    chunks, meshing

See also: Architecture · The runtime family · Pocket3D

Choose your screen

PocketJS has booted on every operating system below, on the real machine. What changes between them is one native host, never the application, and each row links to the post or pull request that brought it up. Pocket Museum repairs and maintains the older machines used to develop and test PocketJS.

Operating system Native host Receipt
PSP system software MIPS, 32 MB Introducing PocketJS
PS Vita system software ARM, GXM Twice the pixels, zero forks
iPhone OS 3.1.3 ARMv6, GL ES 1.1 The first iPhone
iOS 6.1.3 ARMv7 hosts/iphone4s
iOS 12.5.8 arm64 #278
iOS, current NativeScript host #256
macOS Metal window and widget #293
Symbian Belle Qt, GLES2 Symbian wanted a frame function
Windows CE 6 GDI framebuffer From message pump to multitouch
BlackBerry 10.3 QNX, native ELF A Square Screen and a Dead Signing Server
PocketBook e-ink inkview, partial refresh #172
ESP-IDF 6.0/6.1 P4 PPA or S3 software RGB565 ESP-IDF components
The browser WebAssembly core Playground

Devices it has booted on: Sony PSP (2004), PS Vita (2011), iPhone (2007), iPhone 4S (2011), iPod touch 6 (2015), Nokia E7 (2011), Meizu M8 (2009), BlackBerry Classic (2014), PocketBook reader (e-ink), ESP32-P4 devkit (microcontroller) and Mac (Apple silicon). The Nintendo 3DS runs several of the applications in the next section.

The authoritative host and target inventory is contracts/spec/platforms.ts; each entry records what has been verified and how. See Platform contracts and the Native contract.

Made with PocketJS

A music player for a PSP, a workspace on a 3DS, a game to take along. Each row links to the project, and most to the story of how it was built.

Project Devices What it is
OpenStrike PSP, PS Vita A Counter-Strike-shaped shooter on 2004 hardware: BSP maps, bots and a HUD written in Solid JSX, at 60 fps with 2.2 ms of JavaScript per frame. Story
Pocket Voxel PSP, PS Vita, web A creature-RPG town rebuilt as a walking voxel diorama. Game state lives in the JS guest; logic runs at 60 Hz while presentation holds a locked 30 fps beat. Story
Pocket Figma PSP, PS Vita A 14,430-node design file, cooked into streamed tile pyramids and panned with the analog nub at 60 fps on a handheld with 32 MB of RAM. Story
Pocket YouTube 3DS, PSP, PS Vita Watch on the upper screen while searching and browsing on the touch screen. A Mac companion streams video to the 3DS over Wi-Fi. Story
Pocket Shell 3DS A tiling interface: windows on the upper screen, a workspace and control deck below
Pocket Doc 3DS A Markdown library on two screens: read above, edit and navigate below
Pocket Map 3DS, PSP OpenStreetMap or Hyrule on a handheld: pan with the touchpad, zoom, search and save places through a paired Mac
Pocket Term 3DS Mac shell sessions above a touch keyboard
PSPMAN PSP A Walkman-inspired music player by ObsoleteSony: local FLAC and MP3, album art and cassette mode
Pocket Character Mac A rigged VRM companion in a transparent always-on-top window, rendering skinned 3D at 60 fps in one process and 118 MB, against 8 processes and 2184 MB for an Electron build of the same idea. Story
Pocket DevTools every backend Time-travel debugging over a USB cable at 2 bytes per frame. The inspector highlight is emitted by the core into the draw list, so it renders on the device
Pocket Launcher PSP, PS Vita Whole-application lifecycle, target admission, frozen shots and guest switching
Pocket Pi QuickJS guest A coding agent running inside the QuickJS guest environment, with no Node underneath

Pocket Voxel on a real PSP: Pallet Town as a voxel diorama with gabled roofs, carved bushes, flowers, an NPC and the player on the path, in per-tile color.

Pocket Voxel, captured on a PSP-2000: the flat Game Boy world standing up as geometry. The making-of story.

Get started

The zero-install path is the online Playground. Local browser development requires Bun and Rust via rustup:

git clone https://github.com/pocket-nexus/pocketjs
cd pocketjs
bun install
rustup target add wasm32-unknown-unknown
bun run dev                    # build WASM + the Hero app, then serve the browser host

The CLI operates inside a PocketJS checkout:

npm install -g @pocketjs/cli
pocket doctor                  # report missing host and target tooling
pocket setup                   # install the pinned web + PSP toolchain
pocket create my-app
pocket check --target psp --manifest apps/my-app/pocket.json
pocket build --target psp --manifest apps/my-app/pocket.json -- --release

Vita packaging additionally requires VitaSDK and the pinned Rust toolchain documented in hosts/vita/README.md. Guest builds can be packaged as inspectable, target-thinnable .pocket files instead of per-port directories. The Getting started guide walks through a first application.

Ahead-of-time compilation

The TypeScript support reference compares ordinary application code, AOT views and compiled model bodies, including their restrictions and current implementation limits.

MicroTS compiles Solid TSX and Vue SFC views to Rust through a shared typed View IR. app.model: "compiled" also compiles the supported TypeScript model subset to Rust. The default "rust" mode uses an application-provided Rust model implementing the same generated trait. Native AOT calls the retained UI core without a guest engine; the host still owns input, services and presentation.

bun microts/compiler/cli.ts build solid-aot-lab --strict
cargo check --locked --manifest-path apps/solid-aot-lab/Cargo.toml

The guest build lowers that compiled model's TypeScript source to a bundle with the same frame-synchronous reaction and task semantics. JavaScript is an execution format; the application source contract remains TypeScript. Rust AOT uses alloc, with capacity tags for bounded strings and arrays. Demo gen/ directories are ignored and must be regenerated before Cargo builds.

The earlier C/cartridge experiment lives in a separate repository with its own TypeScript subset, target profiles, examples and toolchains. Those sources and scripts are not part of this repository or the Rust AOT build.

Working on PocketJS

Repository layout

Path Responsibility
framework/ Public framework APIs, renderers, components, input, lifecycle and build-time styling
engine/ no_std UI core, render backends, native modules, Pocket3D and platform-native crates
devices/ Pocket3D device kernels for GXM, GE and PICA200
pocket3d/ The Pocket3D entry point: its design and a map of where its code lives
microts/ The MicroTS ahead-of-time compiler
contracts/ Generated wire specs, capability registry, manifests, build plans and package formats
hosts/ PSP, Vita, 3DS, web, desktop, e-reader, phone and MCU host integrations
hosts/esp-idf/ Composable package, QuickJS, UI, RGB565, PPA and runner components for P4/S3 firmware
apps/ Framework demos and system applications used by the launcher and acceptance suites
tools/ Build, package, launcher, device, DevTools, benchmark and release commands
tests/ Contract, compiler, simulation, emulator, package and golden verification
docs/ Platform, runtime, determinism, DevTools, backend and benchmark records
site/ This website (site/nexus/ holds the pocket.nexus homepage, site/pocket3d/ the 3d.pocket.nexus homepage)

Building and testing

Emulator journeys require their external toolchains:

bun run test                  # contracts, compiler, packages, sims, and host suites
bun run golden                # deterministic WASM/web frame goldens
bun run e2e                   # PPSSPP journey
bun run e2e:vita              # Vita3K native-density journey
bun run site:build            # docs, playground, Stage, and landing build
bun run site:preview          # build, then serve the site at http://127.0.0.1:4173/

Documentation

Topic Reference
First application Getting started
Frameworks, components, styling Frameworks · Components · Styling
Runtime internals Architecture · Native contract
Targets and packaging Platform contracts · The .pocket platform
Debugging and verification DevTools · Determinism
Runtimes beyond 2D UI The runtime family · Pocket3D
Ahead-of-time compilation MicroTS · TypeScript support
Complete examples apps/ · Blog

Sponsors and Pocket Nexus

PocketJS is developed full-time with the support of its sponsors, who are listed on the website.

PocketJS is built by Pocket Nexus, an independent, non-VC-backed lab that starts from this runtime to explore new possibilities in computing, interaction and creation, so that the joy of creating belongs to everyone. Its other projects are listed on the GitHub organization.

Attribution

The original motion studies are by yui540. PocketJS accepts yui540's two stated conditions for continued use: Motion Lab carries the requested (yui540) on-screen credit, and any other yui540 animation requires separate permission before it is ported. The accepted scope and capture-maintenance rules are recorded in apps/motions/ATTRIBUTION.md.

License

PocketJS and MicroTS are MIT licensed. Inter is vendored under the OFL in assets/fonts/.

Pocket3D, the files under pocket3d/, devices/ and engine/pocket3d/, is under the Pocket3D License. It grants what the MIT License grants, with one more condition: a distributed product that draws 3D scenes with Pocket3D shows the Pocket3D title card when it starts. PocketJS's own hosts and the applications built on them are exempt. A separate written license removes the condition: write to support@pocket.nexus.

About

PocketJS is a portable application runtime that turns modern component code into native pixels across radically different hardware. This repository is also the home of Pocket3D, a hardware-native 3D stack, and MicroTS, an ahead-of-time TypeScript compiler.

Topics

Resources

Stars

1.7k stars

Watchers

7 watching

Forks

Releases

Packages

Contributors

Languages