What it is
The original game is a J2ME MIDlet: Java bytecode written for the MIDP 2.0 phone APIs. Browsers don't have those APIs, so this project:
Compiles, not emulates
The game's unmodified bytecode is translated to JavaScript, either in your browser when you open your file, or ahead of time with TeaVM. Java's exact integer maths, bounds checks and exceptions are preserved.
Reimplements the phone
The ~50 MIDP methods the game uses are rebuilt on web APIs: a pixel-exact software renderer, PNG decoding, saves, audio and input.
Plays like a web game
Keyboard controls, a touch joystick and buttons for phones, whole-pixel scaling, and optional smoothing plus LCD/CRT screen effects.
Play in the browser
Open the player and choose your copy of SoulOfDarkness_HTC_P3450_EN_IGPL_EU_101_S8000.jar (v1.0.1, 240×320 touch build). The page unzips it and compiles the game's bytecode to JavaScript right there, then starts it. Nothing is sent to a server; the site only hosts the runtime. You can optionally keep the file in your browser's storage so you don't have to choose it again.
Other builds show a warning and may not work. Saves stay in that browser.
Build it yourself
You need JDK 11+, Maven 3.9+, Node.js 18+, and your own copy of SoulOfDarkness_HTC_P3450_EN_IGPL_EU_101_S8000.jar (v1.0.1, 240×320 touch build). The build checks the file's SHA-256 and rejects other versions.
git clone https://github.com/Jubayer-cmd/sod-web-port.git cd sod-web-port cp /path/to/your/SoulOfDarkness.jar game/SoulOfDarkness.jar mvn -q package node serve.mjs # open http://localhost:8240/
On your phone
node serve.mjs --lan # prints http://<your-LAN-IP>:8240/
Open the printed address on a phone connected to the same Wi-Fi. On iPhone, use Share → Add to Home Screen to play full screen.
Your build contains the game, so keep it to yourself: don't host, upload or share dist/.
Controls
| Action | Keyboard | Touch |
|---|---|---|
| Move | A D / ← → | drag the joystick sideways |
| Jump | W / Space / ↑ | joystick up, or Jump |
| Diagonal jump | Q / E | joystick up-left / up-right |
| Crouch | S / ↓ | joystick down |
| Attack | J (or Z) | Attack |
| Magic | K (or X) | Magic |
| Switch weapon | L (or C) | Switch |
| OK / Next (hold to fill the bar on the upgrade screen) | Enter | OK |
| Pause | P | OK during play |
| Back / weapon upgrade | Esc / U | Back |
How it works
In the browser (play/):
your game.jar ─▶ unzip ─▶ class files ─▶ jvm/compiler.js ─▶ JavaScript functions
jvm/cldc.js + jvm/midp.js (Java & phone APIs)
Ahead of time (local build):
your game.jar ─▶ original classes + data ─┐
├─ TeaVM ─▶ dist/sod.js (plain JavaScript)
MIDP 2.0 layer (this repo) ──────────────┘ │
▼
host.js canvas · input · text · WebAudio · localStorage
display.js WebGL2: Scale2x → sharp-bilinear → LCD / CRT
controls.js drag joystick + buttons midi.js MIDI synth
Pixel-exact graphics
A software renderer handles MIDP clipping, all 8 sprite transforms and alpha, then the GPU scales the result.
Same input timing as a phone
Input is delivered on the game thread before each frame, and every press lasts at least one frame, because the game checks the keys once per frame.
Context-aware OK
Enter is the phone's OK soft key, except on the upgrade screen, which only fills its bar while Fire is held.
Smooth 60 fps motion
The game updates 20 times a second. The player records each frame as draw calls, tracks sprites, tiles and the camera between frames, and redraws in-between positions on every screen refresh. Game speed is unchanged; switch to Original 20 fps for the lowest input delay.
Pause and resume
Hiding and showing the page arrive on a separate thread, so the game pauses when you switch tabs and picks up again when you come back.
Status
The in-browser player is tested in Chrome on macOS through boot, menus, story, level 1 gameplay and the upgrade screen, at the game's full 20 frames per second. The local TeaVM build is tested in Chrome on macOS: boot, menus, story, level 1 gameplay, keyboard, mouse and touch, saves, sound, tab switching, all display modes, and the phone layouts in emulated screen sizes. Typical frame cost is 2–7 ms of the game's 50 ms frame budget.
Not yet tested on physical phones. The music is a simple synth. The game's own tutorial screens still show phone controls. Only build v1.0.1 is supported.
Legal
This project's own source code is released under the MIT License. That license covers only the code in this repository. Soul of Darkness, its code, graphics, music, text and trademarks belong to Gameloft SA. None of it is included here, and nothing here grants any right to it. This is an independent, non-commercial compatibility project for people who already own the game, and it does not bypass any copy protection. Rights holders with a concern can open an issue on the repository.