psg1-sim
v0.1.0
Published
Development simulator for the PlaySolana PSG1 handheld console
Maintainers
Readme
psg1-sim

Web-first PSG1 simulator library for React and React Native Web.
It provides one unified gamepad API for:
- real controllers via browser
navigator.getGamepads() - native Android gamepad key events
- simulated on-screen PSG1 controls
- keyboard fallback controls
Install
npm install psg1-simPeer dependencies: react >= 18, react-dom >= 18
PSG1 Button Model
PSG1 controls are exposed with PSG1 labels:
A,B,X,YDPadUp,DPadDown,DPadLeft,DPadRightL1,R1,Start,Select,L3,R3
Internally these map to PS-style naming for compatibility:
A -> Cross,B -> Circle,X -> Square,Y -> TriangleStart -> Options,Select -> Share
Wrap Your App
import { Psg1Simulator } from 'psg1-sim';
export function App() {
return (
<Psg1Simulator enabledRealGamepad showDebugHud={false}>
<MyGame />
</Psg1Simulator>
);
}Read Input in Game Code
import { Psg1Button, usePsg1Gamepad } from 'psg1-sim';
function MyGame() {
const { pressed, lastEvent, isDown } = usePsg1Gamepad();
return (
<div>
{isDown(Psg1Button.A) && <p>Jump</p>}
{pressed.has(Psg1Button.DPadLeft) && <p>Move Left</p>}
{lastEvent && <p>Last: {lastEvent.type} {lastEvent.button} ({lastEvent.source})</p>}
</div>
);
}Real Controller Testing (Web)
Psg1Simulator can auto-enable the web backend with enabledRealGamepad.
You can also manage it manually:
import { enableWebGamepadBackend, disableWebGamepadBackend } from 'psg1-sim';
const stop = enableWebGamepadBackend();
// ...later
stop();
// or disableWebGamepadBackend();Custom gamepad index mapping:
enableWebGamepadBackend({
buttonMap: {
[Psg1Button.A]: 1,
[Psg1Button.B]: 2,
},
});Default mapping used by the backend:
0 -> A,1 -> B,2 -> X,3 -> Y4 -> L1,5 -> R18 -> Select,9 -> Start10 -> L3,11 -> R312 -> DPadUp,13 -> DPadDown,14 -> DPadLeft,15 -> DPadRight
Android APK Controller Support
Android controller input is supported through a native backend that maps Android KeyEvent codes into PSG1 buttons.
Psg1InputProvider/Psg1Simulatorauto-enables the Android backend.- Default keycode mapping:
96 -> A,97 -> B,99 -> X,100 -> Y102 -> L1,103 -> R1108 -> Start,109 -> Select106 -> L3,107 -> R319/20/21/22 -> DPadUp/Down/Left/Right
If your runtime emits custom native events, bridge them with:
import { emitAndroidGamepadEvent } from 'psg1-sim';
emitAndroidGamepadEvent({
action: 'down',
keyCode: 96, // A
});Keyboard Fallback Defaults
- Arrow keys -> D-pad
J-> AK-> BU-> XI-> YQ-> L1E-> R1Enter-> StartShift-> SelectZ-> L3C-> R3
API
usePsg1Gamepad()
Returns:
{
pressed: ReadonlySet<Psg1Button>;
lastEvent?: GamepadEvent;
isDown(btn: Psg1Button): boolean;
}<Psg1Simulator />
Props:
enabledRealGamepad?: boolean(defaulttrue)showDebugHud?: boolean(defaultfalse)realGamepadOptions?: WebGamepadBackendOptionskeyMap?: Record<string, Psg1Button>store?: InputStore
Compatibility
usePsg1Input() is still exported as a deprecated compatibility hook.
Deprecated enum aliases are kept for migration:
A -> Cross,B -> Circle,X -> Square,Y -> TriangleStart -> Options,Select -> Share
License
MIT
