Porting Another World to ESP32: 50 FPS Vector Graphics, Capacitive Touch, and Bluetooth Audio on a $5 Microcontroller
In 1991, French visionary Éric Chahi released Another World (Out of This World in North America)—a cinematic tour de force that revolutionized video game storytelling. Built entirely on an avant-garde vector polygon engine and a custom 64-thread virtual machine, the game squeezed smooth cinematic cutscenes onto 7.14 MHz Amiga 500 computers with just 512 KB of RAM.
For a long time, I wondered: Could we run the complete Another World engine natively on a bare-metal ESP32 microcontroller with fluid 50 FPS rendering, capacitive touch controls on a custom PCB, and Bluetooth audio streaming?
Here is the story of how anotheresp32 was engineered, the memory and hardware obstacles overcome, and how you can run it on your own board.

The Hardware: LilyGO TTGO T-Display & “TTECH Game Pad”
The project targets the popular LilyGO TTGO T-Display:
- MCU: Espressif ESP32-D0WDQ6 (Dual-Core Xtensa LX6 @ 240 MHz, ~320 KB SRAM, 4 MB Flash)
- Display: 1.14” ST7789 IPS TFT (240x135 pixels, 16-bit RGB565 over high-speed SPI)
- Cost: ~$5–$8
To make it a true standalone portable console, I etched and milled a custom PCB gamepad: the TTECH ESP32 Game Pad.
+-------------------+
| [USB-C PORT] |
| |
| TTGO T-Display |
| [240x135 IPS] |
+-------------------+
| | |
+-----------+---+---+-----------+
| TTECH ESP32 Capacitive Pad |
| |
| (UP: GPIO 15) |
| | |
| (LEFT: 2) --+-- (RIGHT: 13) | (ACTION: 27)
| | |
| (DOWN: GPIO 32) | (JUMP: 33)
| |
| [START: GPIO 12] |
+-------------------------------+
Instead of using clicky tactile buttons, the board features 7 bare-copper capacitive touch pads connected directly to the ESP32’s internal touch sensor peripherals (Touch 2 through Touch 9).
Technical Obstacle 1: The Boot-Strapping Pin Trap
During PCB design, GPIO 12 was originally routed to DOWN. However, GPIO 12 is MTDI—a critical ESP32 strapping pin that sets the flash voltage regulator to 1.8V if pulled high at power-on! If a player touches the pad or trace resistance pulls GPIO 12 high during boot, the flash voltage drops and the chip enters an unrecoverable boot loop (flash read err, 1000).
The Solution: We remapped the default movement controls strictly to safe, non-strapping pins:
- LEFT:
GPIO 2(Touch 2) - UP:
GPIO 15(Touch 3) - RIGHT:
GPIO 13(Touch 4) - DOWN:
GPIO 32(Touch 9) — Safe! - ACTION:
GPIO 27(Touch 7) - TOUCH JUMP:
GPIO 33(Touch 8) - START / PAUSE (Optional):
GPIO 12(Touch 5)
At boot, the driver executes a 100ms baseline calibration cycle on all 7 channels, computing a resting threshold for instantaneous, jitter-free touch response.
Technical Obstacle 2: The 160 KB SRAM Puzzle & 65 KB Polygon Tables
The native Another World engine renders at 320x200 (4-bit indexed color, 16 colors). Each frame page occupies 32,000 bytes.
The original VM engine relies on 4 visual pages:
Page 0: Display page (front buffer)Page 1: Back buffer (drawing canvas)Page 2: Background persistence bufferPage 3: Composition / sprite layer
The Problem:
4 x 32,000 bytes = 128,000 bytes (128 KB).
On an ESP32 with ~160 KB of free application heap, reserving 128 KB upfront left only ~32 KB of remaining heap. When Part 16001 loaded its 65,156-byte cinematic polygon table (res 25), malloc() failed, leaving Lester standing on a pitch-black screen.
The Engineering Fix:
By analyzing the VM opcode stream, we found that Another World rarely needs 4 distinct physical buffers simultaneously; pages 2 and 3 are largely composition scratchpads. We re-architected the video subsystem to allocate 2 high-speed physical framebuffers (64 KB total) with page aliasing.
This dropped baseline video SRAM consumption from 128 KB to 64 KB, freeing up ~160 KB of contiguous heap. Now, massive 65 KB polygon tables, sound banks, and Amiga audio modules allocate instantly without needing external PSRAM!
Technical Obstacle 3: 50 FPS Vector Polygon Rasterization
Another World does not draw bitmap sprites—it renders dynamic 2D vector polygons grouped in hierarchical transformation trees.
[Raw Shape Bytecode]
│
▼
[Vertex Transform & Zoom]
│
▼
[Left & Right Edge Walkers]
│
▼
[4-Bit Nibble Packed Scanlines]
│
▼ (SPI DMA via LovyanGFX)
[ST7789 240x135 IPS TFT @ 50 Hz]
To achieve 50 FPS on the ST7789:
- Integer Fixed-Point Edge Walking: All polygon bounding box calculations and slope interpolations use fixed-point arithmetic.
- Scanline Nibble Packing: The 4-bit indexed page buffers pack two pixels per byte, allowing fast horizontal word fills.
- SPI DMA Push via LovyanGFX: Framebuffer updates are converted to 16-bit RGB565 and pushed across hardware SPI at 80 MHz using LovyanGFX’s DMA bus.
The engine supports two scaling modes in hw_config.h:
- Letterbox Mode (Default): Authentic 16:10 aspect ratio (216x135) centered with clean black pillarboxes.
- Stretched Mode: Full 240x135 non-uniform screen stretch.
Technical Obstacle 4: Audio Subsystem (8-Bit DAC & Bluetooth A2DP)
Another World’s iconic audio was composed on Amiga 4-channel MODs and digitized 8-bit PCM samples (thunder cracks, elevator whirs, alien beast growls).
We implemented a dual audio subsystem:
- Zero-Overhead 8-bit DAC Output (Default):
- Channels mixed in real-time at 22,050 Hz and streamed via DMA to
GPIO 25(DAC1) andGPIO 26(DAC2).
- Channels mixed in real-time at 22,050 Hz and streamed via DMA to
- Wireless Bluetooth A2DP Audio:
- Mixed audio is upsampled to 44.1 kHz stereo and streamed to Bluetooth speakers or headphones.
- Includes direct hardware MAC auto-pairing in
include/bt_mac.hfor instant connection without scan delays.
Technical Obstacle 5: Commercial Asset Policy & 1-Command Demo Loader
A major priority was ensuring the repository remains 100% legal, open-source, and free of DMCA risks. Commercial game banks (bank01–bank0d, memlist.bin) must never be checked into Git.
To give anyone an instant, out-of-the-box playable experience without hunting for floppy disks, we built the --demo auto-fetcher into tools/pack_assets.py:
# 1. Clone the clean C++ engine repo
git clone https://github.com/tonym128/anotheresp32.git
cd anotheresp32
# 2. Automatically download the 1991 public shareware demo archive
python3 tools/pack_assets.py --demo
# 3. Flash SPIFFS filesystem and firmware to ESP32!
pio run -t uploadfs && pio run -t upload
The engine automatically detects whether you’re running the 1991 Shareware Demo (smoothly looping after the wilderness chase) or the Full 13-Level Game (playing all the way to the fortress palace ending).
Source Code & Hardware Details
The entire codebase, circuit diagrams, test suite, and CI workflows are open-source under the MIT License on GitHub:
👉 github.com/tonym128/anotheresp32
Quick Requirements:
- Board: LilyGO TTGO T-Display ESP32
- Toolchain: PlatformIO (VS Code or CLI)
- Assets: Run
python3 tools/pack_assets.py --demoor pack your own DOS/Amiga files!
Acknowledgments & Attributions
This project was made possible thanks to the work and reverse-engineering of several pioneers in retro computing:
- Éric Chahi: Original visionary creator, designer, and programmer of Another World / Out of This World (1991, Delphine Software).
- Fabien Sanglard: For his iconic technical book and articles on the engine:
- Gregory Montoir (cyxx): Author of
rawgl(Another World engine rewrite) andanother_js, providing foundational reference for ByteKiller decompression, audio mixer routines, and bytecode dispatch. - The ScummVM Team: For pioneering retro game engine preservation and Cinematique virtual machine documentation.
- Phil Schatzmann (pschatzmann): Author of
ESP32-A2DP, enabling high-performance Bluetooth A2DP audio streaming. - Lovyan03: Author of
LovyanGFX, the ultra-fast SPI DMA graphics driver powering our 50 FPS ST7789 display.
Another World is © Éric Chahi and Delphine Software / Dotemu. This project is an independent open-source virtual machine engine created for retro computing research and preservation.