New Symbian Wanted a Frame Function: PocketJS on a Nokia E7

Build modern appsfor impossible devices.

Components, signals and Tailwind — on consoles, e-readers and microcontrollers. A tiny JS guest where it fits; the framework compiled away where it doesn’t.

Loading Pocket Stage…
The live Pocket Launcher inside the model’s real screen material — browse the deck, launch any app, SELECT to switch. PSP model by Dibad · CC BY 4.0
Familiar code, native machinery

Write the app you already know how to write.

Components, signals, Tailwind classes. Then pick the machine underneath.

Counter.tsx
import { Text, View } from "@pocketjs/framework/components";
import { createSignal } from "solid-js";

export default function Counter() {
  const [n, setN] = createSignal(0);
  return (
    <View class="flex-col items-center gap-4 p-6 bg-slate-950">
      <Text class="text-2xl text-slate-50 font-bold">Count: {n()}</Text>
      <View class="px-4 py-2 rounded-xl bg-slate-700 focus:bg-slate-500" focusable onPress={() => setN(n() + 1)}>
        <Text class="text-base text-slate-50 font-bold">Press Circle</Text>
      </View>
    </View>
  );
}
Execution
the same Rust core, compiled to WebAssembly
Display
any viewport, density-aware
Rendering
identical framebuffers to the devices — goldens run here
Input
mouse, keyboard, gamepad
Ships as
a URL — no install, no toolchain
Run this counter in the playground →

Layout becomes native flexbox

taffy lays out in the Rust core — a familiar flex-col gap-4 lands on identical pixels on every target, provable in goldens.

Tailwind becomes style data

Class literals compile to compact numeric records. No CSS parser, no cascade, no utility runtime ships to the device.

Reactivity becomes what fits

A tiny QuickJS guest where there’s room for one; compiled dirty-bit state machinery where there isn’t.

The .pocket format

Ship the application,
not the port.

A .pocket file describes what the app is and what it needs — identity, capabilities, build plan, payloads. Each device decides how it should run: which viewport, which presentation, which execution class. If a target can’t honor the contract, it says so before launch, not after.

build --all-targets inspect verify thin
my-app.pocket
my-app.pocket
├─ identity       app id, version, entry
├─ capabilities   input.touch · display.viewport.live · …
├─ build plan     how every target gets produced
├─ payloads       guest bundle · AOT program
└─ assets         style packs · fonts · tiles

One deterministic file. Every target re-verifies the plan before launch.

System software, built into the runtime

Not just rectangles on unusual screens — the pieces apps need to become an ecosystem.

App launcher

A Cover Flow deck that cold-boots, freezes and switches whole apps — running live in the PSP at the top of this page.

Capability contracts

A registry of real, tested hosts. Apps declare needs; targets admit or refuse them at build time.

Input & OSK

D-pad focus, touch, analog, a virtual cursor — and a system keyboard any app summons with one call.

Host services

USB companions bring what the device lacks: yt-dlp on a Mac becomes YouTube on a PSP.

Time-travel DevTools

Every session records itself. Step to any frame, inspect the native tree — over USB to real hardware.

Where it runs

Built for every kind of impossible.

Impossible doesn’t mean old. It means no browser, no modern OS, no JS engine, no continuous refresh — or no vendor left to care.

Retro consoles

No OS to lean on, and 333 MHz is the fast one.

  • Sony PSP
  • PS Vita
  • Game Boy Advance
  • Game Boy
  • NES
  • Nintendo 3DS

Abandoned smartphones

The vendor moved on. The hardware didn’t.

  • Symbian Belle
  • MeeGo
  • BlackBerry
  • Windows Phone

E-ink surfaces

A screen that fights every frame — so the runtime picks partial, dynamic or full refresh per update.

  • PocketBook
  • Kindle

Microcontrollers

Kilobytes, not gigabytes. Sometimes no allocator at all.

  • ESP32 MeowBit
  • ESP32‑P4
  • M5Stack Tab5

Built on Pocket Vapor, the experimental compile-away pipeline.

Native desktop surfaces

Modern machines that deserve better than a bundled browser per app.

  • macOS widgets
  • Transparent windows
  • iOS
shippingin progressplanned

Every “shipping” chip is a real, tested host in the registry — not a roadmap slide. See the target profiles →

Shipped on real hardware

The proof is what it can carry.

Complete open-source apps, each shipped to stress a different part of the platform.

The Pocket Launcher Cover Flow deck on a Nokia E7's landscape AMOLED screen.

Pocket Launcher

Proves: multi-app lifecycle & system UI

A Cover Flow deck that boots, freezes and switches whole apps — on PSP and on a 2011 Nokia. It’s running live in the 3D PSP at the top of this page.

OpenStrike on de_dust2: sunlit BSP geometry with the Solid HUD showing 100 HP and 30/90 ammo.

OpenStrike

Proves: 3D worlds & the JS/native split

A Counter-Strike-shaped FPS on the classic GoldSrc-era maps — BSP, lightmaps, bots, recoil — at a locked 60 FPS on a 333 MHz PSP. The HUD is a Solid app.

Pocket Figma on a PSP at 59% zoom: the Paper Wireframe Kit cover with the viewer HUD along the bottom.

Pocket Figma

Proves: big documents & streamed assets

A real Figma Community file — 14,430 nodes, a canvas 26,000 pixels wide — baked into streaming tiles the device pans and zooms without ever parsing the file.

Pocket YouTube on a PSP: a paused video under the player HUD's title bar and red progress bar.

Pocket YouTube

Proves: host services, video, audio & OSK

YouTube on a PSP where the network is a USB cable: a Mac companion runs yt-dlp and ffmpeg, the handheld plays the stream — search, seek, CJK titles and all.

Pocket Character's VRM digital human in a transparent macOS widget window, next to Activity Monitor showing a single light process.

Pocket Character

Proves: it also replaces heavy desktop runtimes

A VRM digital human in a transparent always-on-top window — one native process, 118 MB and ~4% of a core, where the Electron stage needs 2.2 GB and 44%.

The same Vue todo app running side by side on Game Boy Advance, Game Boy and NES.

Pocket Vapor Todo

Proves: it reaches machines with no interpreter

One Vue module — ref, computed, list rendering — compiled into GBA, Game Boy and NES carts, verified frame-by-frame against real Vue on the same inputs.

Your turn

The browser was never the limit.

Take the app model you already know to the device everyone else gave up on.