In 2011, Markus Persson (Notch), the creator of Minecraft, participated in Ludum Dare 21 with a small retro dungeon-crawler: Prelude of the Chambered. A raycasting engine à la Wolfenstein 3D, pixelated graphics, puzzles and bosses. A technical gem compressed into 48 hours of game jam.
The problem? It was Java. And in 2025, who still launches a JVM to play a small indie game?
The Project: Porting Java to TypeScript
Prelude of the Chambered Reborn is my complete rewrite of the original code in TypeScript, built with Vite, playable directly in the browser. No plugins, no downloads, just a URL.
👉 Play now
Why This Project?
Three reasons:
- Nostalgia: I discovered this game back then and always wanted to revisit it
- Technical challenge: Translating object-oriented Java to modern TypeScript without losing the essence of the original code
- Accessibility: Making this piece of indie gaming history accessible to everyone, on any device
Technical Architecture
Pure Software Rendering
The most important choice: no WebGL. The game exclusively uses the Canvas 2D API with software raycasting, exactly like the original.
// Pixel by pixel rendering on Canvas
class Bitmap {
pixels: number[];
width: number;
height: number;
draw(bitmap: Bitmap, x: number, y: number): void {
// Direct pixel copy, no GPU
}
}
This approach guarantees maximum compatibility: the game runs on any browser, even older ones.
Modular Organization
The code is split into specialized modules:
src/
├── rendering/ # Bitmap, Bitmap3D, Screen, Sprite
├── entities/ # Player, EnemyEntity, bosses
├── levels/ # Level base, blocks, 6 levels
├── menus/ # TitleMenu, WinMenu, GameOverMenu
└── core/ # Game loop, input handling
Each module is independent and testable. The structure allows for easy understanding and modification of any part of the game.
Modern Stack
- TypeScript: Strict typing to avoid runtime bugs
- Vite: Ultra-fast build with HMR for development
- GitHub Actions: Automatic deployment on every push
- Canvas API: Software rendering without WebGL dependency
The Bitmap Map System: Notch’s Brilliant Idea
One of the most elegant architectures from the original game, which I preserved, is the bitmap-based level design system. Each level is literally a PNG image where every pixel defines a game element.
// Loading level from an image
const response = await fetch(`res/level/${name}.png`);
const pixels = getPixelsFromImage(image);
// Each color = a block type
switch (color) {
case 0xffffff: return new SolidBlock(); // White = wall
case 0x0000ff: return new WaterBlock(); // Blue = water
case 0xff66ff: return new LadderBlock(); // Magenta = ladder
case 0xffff64: return new ChestBlock(); // Yellow = chest
}
Why is this brilliant?
- Visual level design: Open Paint, draw some pixels, you have a playable level
- No proprietary format: A standard PNG, editable by any tool
- Version control friendly: Git diffs show changes visually
- Trivial extensibility: Adding a new block type = a new color
The system goes further with the alpha channel to encode mechanism IDs:
// Alpha encodes the ID to link switches and doors
const id = 255 - ((pixels[x + y * w] >>> 24) & 0xff);
// A switch with alpha=250 triggers the door with alpha=250
And entities? Same principle:
| Color | Entity |
|---|---|
#ff0000 (red) |
Bat |
#ff0001 |
Bat Boss |
#ff0002 |
Ogre |
#ff0003 |
Ogre Boss |
#ffff00 (bright yellow) |
Player spawn |
This “data-driven” approach allows anyone to create levels without touching the code. A game designer can iterate by just modifying images.
The Game in Detail
Six Interconnected Levels
- Prison: The starting point, implicit tutorial
- Dungeons: First enemies and mechanics
- Overworld: Open area with exploration
- Crypt: Dark atmosphere, new challenges
- Temple: More complex puzzles
- Ice Cave: The final level
Combat System
Various enemies with their patterns:
- Bats: Fast but fragile
- Ogres: Slow but powerful
- Eyes: Ranged attacks
- Ghosts: Go through walls
Plus boss variants for each type.
Collectibles and Progression
- Power Glove: Break blocks
- Pistol: Ranged combat
- Flippers: Swimming
- Cutters: Cut obstacles
- Skates: Slide on ice
- Potions: Healing
The goal: collect the four keys to escape.
Conversion Challenges
Type Management
Java and TypeScript have different type systems. Java classes with complex inheritance required refactoring:
// Entity pattern with composition
abstract class Entity {
x: number;
y: number;
level: Level;
abstract tick(): void;
abstract render(screen: Screen): void;
}
Game Loop
Moving from Java’s Thread.sleep() to requestAnimationFrame required rethinking the timing:
class Game {
private lastTime = 0;
gameLoop(currentTime: number): void {
const deltaTime = currentTime - this.lastTime;
this.update(deltaTime);
this.render();
this.lastTime = currentTime;
requestAnimationFrame(this.gameLoop.bind(this));
}
}
Input Handling
From Java AWT to native DOM events, with keyboard and planned gamepad support.
Contributing
The project is open source and contributions are welcome:
- 🐛 Bug fixes: Behaviors different from the original
- 🎨 Assets: Sprite optimization
- 📱 Mobile: Touch support
- 🎮 Gamepad: Controller support
# Local installation
git clone https://github.com/Lingelo/prelude-of-the-chambered-reborn
cd prelude-of-the-chambered-reborn
npm install
npm run dev
The development server starts at http://localhost:5173.
Conclusion
Prelude of the Chambered Reborn is more than a nostalgia exercise. It’s a demonstration that “old school” rendering techniques remain relevant and that TypeScript can handle performance-oriented code.
The project also proves that with modern tools (Vite, GitHub Actions), deploying a web game becomes trivial. A simple git push command and the game is online.
If you grew up with Notch’s games or are curious to see how a raycasting engine works, try the game. And if you find bugs or have improvement ideas, the GitHub repo awaits.
Original Prelude of the Chambered: Copyright (c) 2011 Mojang. This rewrite is an educational and preservation project.