OP. I spent the holidays building a native PICO-8 emulator for iOS because the standard web player suffers from significant audio latency and input lag on mobile Safari, and there just hasn't been anyone else creating an emulator for this console that has infinite free games.
The core engineering challenge was reconciling the asynchronous nature of iOS file I/O with the synchronous requirements of the PICO-8/WASM virtual file system.
A few technical details on the implementation:
1. Bootloader Hijacking: To bypass the default "JELPI" demo cart, I intercept the Emscripten `Module.preRun` lifecycle, manually injecting user code into the WASM heap before the runtime initializes.
2. State Persistence: Instead of standard "Save Games" (SRAM), I implemented a system that dumps the simulated RAM (200+MB) into a GZIP-serialized blob (<1MB). This allows for instant "Save States" that persist exactly where you left off, even if the app is killed.
3. Audio/Input: By bridging the C++ runtime directly to Swift via a custom layer, I managed to eliminate the WebAudio latency that usually plagues PICO-8 on iOS. Games run as if the device was a native pico console itself, very cool!
The project is open source and can be sideloaded via AltStore/SideStore. Happy to answer questions about the WASM/Native bridging!
OP. I spent the holidays building a native PICO-8 emulator for iOS because the standard web player suffers from significant audio latency and input lag on mobile Safari, and there just hasn't been anyone else creating an emulator for this console that has infinite free games.
The core engineering challenge was reconciling the asynchronous nature of iOS file I/O with the synchronous requirements of the PICO-8/WASM virtual file system.
A few technical details on the implementation:
1. Bootloader Hijacking: To bypass the default "JELPI" demo cart, I intercept the Emscripten `Module.preRun` lifecycle, manually injecting user code into the WASM heap before the runtime initializes.
2. State Persistence: Instead of standard "Save Games" (SRAM), I implemented a system that dumps the simulated RAM (200+MB) into a GZIP-serialized blob (<1MB). This allows for instant "Save States" that persist exactly where you left off, even if the app is killed.
3. Audio/Input: By bridging the C++ runtime directly to Swift via a custom layer, I managed to eliminate the WebAudio latency that usually plagues PICO-8 on iOS. Games run as if the device was a native pico console itself, very cool!
The project is open source and can be sideloaded via AltStore/SideStore. Happy to answer questions about the WASM/Native bridging!