PS5 React
React interfaces. Controller navigation. Standalone PS5 homebrew.
Write JSX, preview natively on macOS, and build a fullscreen application for PS5.
Quick start ·
First app ·
Styling ·
APIs ·
Documentation
PS5 React is an open-source framework for standalone console homebrew. React runs
inside **QuickJS** through **Embedded React**. The software renderer draws the
interface into a framebuffer; **OpenGL presents that framebuffer** on screen.
The application runs in its own native host, without a browser or Sony RNPS hooks.
**Status: experimental v0.1.0.** The original proof of concept and a specific
System Explorer filesystem integration were user-confirmed on firmware 13.60
with kstuff and ShadowMount. Those results apply to the exact artifacts recorded
in [Hardware evidence](docs/HARDWARE.md). New apps, networking and other native
features need their own console validation.
## What you can build
| Capability | What the framework provides |
| --- | --- |
| React interfaces | JSX, hooks, state, reusable components and fullscreen application hosts |
| Controller navigation | Spatial D-pad focus, Cross activation, Circle back navigation and focus scopes |
| Styling | A build-time subset of Tailwind CSS v3, custom themes and explicit style objects |
| Animation and media | Motion-style components, local assets, native remote images and short WAV feedback sounds |
| Native services | Filesystem, HTTP, large-file downloads, controller state, notifications, users and power control |
This is a focused console renderer, not the complete official React Native
runtime. Use the supported primitives and native modules from `@ps5-react/core`.
## Quick start
### Prerequisites
The supported development environment is **macOS on Apple Silicon**.
| Dependency | Purpose |
| --- | --- |
| Python 3.12+ and Pillow | CLI, asset baking and packaging |
| Node.js and npm | React dependencies and JavaScript bundling |
| Git, CMake, SDL2 and a C++ compiler | Dependency bootstrap and native desktop preview |
| LLVM 18 | PS5 cross-compilation |
| libcurl 7.85+ development files | Native desktop HTTP and image transport |
| libarchive 3.7+ development files | Streaming ZIP, TAR, 7z and RAR extraction |
1. Install native tools:
```sh
brew install python node git cmake sdl2 llvm@18 curl libarchive openssl@3
```
2. Clone the project and install dependencies:
```sh
git clone https://github.com/half144/ps5-react.git
cd ps5-react
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install -r requirements.txt
npm ci
```
3. Check the environment and open the starter:
```sh
npm run doctor
npm run dev
```
Edit `apps/starter/index.jsx`. Saving JSX, assets or app configuration rebuilds
and restarts the native preview. **State resets on restart; this is not Fast
Refresh.** The first run fetches and builds pinned dependencies when needed.
### Controls
| Action | Desktop preview | PS5 |
| --- | --- | --- |
| Move focus | Arrow keys | D-pad |
| Activate focused element | Enter | Cross |
| Navigate back | Backspace | Circle |
| Options (the `options` action) | O | Options |
| Exit application | Escape | PS button |
| Stop the development watcher | Ctrl+C in the terminal | — |
The starter exits automatically 30 seconds after its first displayed frame.
Set `timeoutSeconds` to `0` in its `app.json` to disable that timeout. Scaffolded
apps already use `0`.
## Your first app
Choose a title ID unused by your local apps and your console:
```sh
npm run create -- --name my-app --title-id PPSA99100 --title "My App"
npm run dev -- --app my-app
```
Replace `apps/my-app/index.jsx` with this complete example. The scaffold includes
its font asset and the `font-inter` theme mapping.
```jsx
import {useState} from 'react';
import {AppRegistry, FocusScope, Text, View} from '@ps5-react/core';
function App() {
const [count, setCount] = useState(0);
return (
My first PS5 app
Count: {count}
setCount(value => value + 1)}
className="w-72 items-center rounded-xl border-2 border-transparent
bg-slate-800 px-6 py-4 focused:border-cyan-400 focused:bg-slate-700">
Add one
setCount(0)}
className="w-72 items-center rounded-xl border-2 border-transparent
bg-slate-800 px-6 py-4 focused:border-cyan-400 focused:bg-slate-700">
Reset
);
}
AppRegistry.registerComponent('my-app', () => App);
```
The scaffold pins the starter's baked font sizes. To let this example and future
literal sizes be discovered automatically, replace `apps/my-app/assets.config.js`
with:
```js
export default {
fonts: {'Inter-Regular': {extraGlyphs: '—–'}},
};
```
`onPress` makes each `View` focusable. Navigation chooses the next element from
its laid-out position; the app does not manage manual focus indices.
`FocusScope` groups navigation and supports focus memory, wrapping and trapping.
### Apps in another repository
```sh
npm run create -- --name my-store --title-id PPSA99101 --dir ../my-projects
npm run dev -- --app-dir ../my-projects/my-store
npm run build -- --app-dir ../my-projects/my-store
```
The bundler resolves React and `@ps5-react/core` from this checkout; an external
app does not need its own `node_modules`. Its directory name must not collide
with a folder in `apps/`. See [external-app editor setup](docs/EXTERNAL-APPS.md).
PNG files in the app's `sce_sys/` go into the title's `sce_sys/`: `icon0.png`
(512×512) is the home-screen icon, `pic0.png` (1920×1080) its background and
`pic1.png` (1920×1080) the splash shown until the first frame. Without an
`icon0.png` the build draws a placeholder icon.
### Application configuration
`app.json` controls the title identity, display name, version, display size,
refresh rate and automatic exit. A configuration for the example app is:
```json
{
"$schema": "../../app.schema.json",
"titleId": "PPSA99100",
"contentId": "UP9000-PPSA99100_00-MYAPP00000000000",
"name": "My App",
"version": "01.000.000",
"render": {"width": 1920, "height": 1080},
"surface": {"width": 1920, "height": 1080},
"timeoutSeconds": 0
}
```
The render size determines software rasterization cost and styling scale; the
surface size is the displayed framebuffer size. Keep them equal: different
sizes go through a bilinear texture scale that softens text. 3840×2160 gives
sharp text on 4K TVs; 1920×1080 costs a quarter of the rasterization and
uploads changed rows through the faster buffer-texture path, which matters for
apps that redraw large areas every frame.
`"resolution": "auto"` replaces `render` and `surface`, and is what the starter
uses. The build compiles the
app twice, at 1920×1080 and 3840×2160, and the title picks the layout matching
the console's video output when it starts: 4K and 1440p output get 3840×2160,
everything else 1920×1080. Classes and baked font sizes are per layout, so the
design looks the same at either size; raw numeric `style` values are render
pixels and do not scale. Leave `sizes` out of `assets.config.js` so each layout
bakes the font sizes its classes use. The eboot carries both bundles and font
sets (about 0.4 MB more for the starter). Preview the small layout with
`npm run dev -- --resolution 1080p`.
`"refreshRate": 120` asks for 120 Hz output (default 60). The display must
accept 120 Hz at the surface size, which for 3840×2160 means HDMI 2.1;
otherwise the console stays at 60 Hz. Each frame then has 8.3 ms, so frames
that fit 60 Hz can miss at 120, and switching the TV into the mode delays the
first frame by about 1.5 s. Motion keeps its speed at either rate.
`"refreshRates": [60, 120]` lets the app switch between the listed rates while
it runs with `Display.setRefreshRate` (docs/NATIVE-API.md); it must include
`refreshRate`, and listing 120 sets the same `param.json` bits.
`"video": true` links FFmpeg and the PS5's hardware decoder for `