Load and render classic DOOM WAD files with full texture, sprite, and level support
The WAD system lets you load DOOM-format WAD files (IWAD and PWAD) and convert their levels, textures, and sprites into Nova64 3D geometry. Build your own DOOM-style FPS or explore classic levels from WADs like FreeDoom.
// Fetch a WAD file
const response = await fetch('freedoom1.wad');
const buffer = await response.arrayBuffer();
// Parse the WAD
const wad = new WADLoader();
wad.load(buffer);
// List available maps
const maps = wad.getMapNames(); // ['E1M1', 'E1M2', ...]
// Parse a map
const mapData = wad.getMap('E1M1');
// Convert to Nova64 3D geometry
const level = convertWADMap(mapData);
// Build walls from converted data
for (const w of level.walls) {
createCube(w.len, 0xaaaaaa, [w.x, w.y, w.z], {
scaleY: w.h, scaleZ: 0.1,
material: 'standard', roughness: 0.8
});
}
// Set player start position
setCameraPosition(level.playerStart.x, 1.5, level.playerStart.z);
const wad = new WADLoader();
wad.load(buffer);
// Create texture manager
const texMgr = new WADTextureManager(wad);
texMgr.init();
// Get a wall texture as Three.js DataTexture
const tex = texMgr.getWallTexture('STARTAN3');
// Get a floor flat texture
const flat = texMgr.getFlatTexture('FLOOR0_1');
// Get a sprite for a monster type
const sprite = texMgr.getSpriteTexture(3004); // Former Human
let wadLoaded = false;
export function init() {
// Listen for file drop on the canvas
document.addEventListener('drop', async (e) => {
e.preventDefault();
const file = e.dataTransfer.files[0];
if (!file || !file.name.endsWith('.wad')) return;
const buffer = await file.arrayBuffer();
const wad = new WADLoader();
wad.load(buffer);
const maps = wad.getMapNames();
if (maps.length > 0) {
buildLevel(wad, maps[0]);
wadLoaded = true;
}
});
document.addEventListener('dragover', (e) => e.preventDefault());
}
Parses IWAD and PWAD binary files, providing access to the lump directory and map data.
Creates a new WAD loader instance.
Parses a WAD file from an ArrayBuffer. Validates the IWAD/PWAD header and reads the lump directory.
Returns an array of map names found in the WAD (e.g., E1M1, MAP01).
E#M# or MAP## patterns
Parses all geometry lumps for a map and returns the parsed data.
'E1M1')
{ name, vertexes, linedefs, sidedefs, sectors, things }
Reads the PLAYPAL lump — 256 RGB color entries (768 bytes).
Gets any raw lump by name.
{ data: Uint8Array, size: number }
Extracts all floor/ceiling flat textures between F_START/F_END markers.
{ [name]: Uint8Array(4096) } — 64×64 indexed color data
Parses the PNAMES lump — the list of patch names used by wall textures.
Parses TEXTURE1 or TEXTURE2 lump — composite wall texture definitions.
'TEXTURE1' or 'TEXTURE2'
{ [name]: { name, width, height, patches[] } }
Extracts all sprite data between S_START/S_END markers.
{ [name]: Uint8Array } — Raw sprite picture data
Converts parsed DOOM map data into Nova64-ready 3D geometry. Handles one-sided walls, two-sided walls (steps, ceiling drops), collision segments, enemy/item placement, and player start.
wad.getMap()
1/20). DOOM units → Nova64 units
| Property | Type | Description |
|---|---|---|
walls | array | Wall segments: { x, y, z, len, h, ang, light, texName, xoff, yoff, step?, upper? } |
colSegs | array | Collision points: { x, z, r } |
enemies | array | Enemy spawns: { x, z, type, doomType } |
items | array | Item pickups: { x, z, type, doomType } |
playerStart | object | { x, z, angle, floorH } |
sectors | array | Sector data: { floorH, ceilH, floorFlat, ceilFlat, light } |
| Type | DOOM Things |
|---|---|
'grunt' | Former Human (3004), Former Sergeant (9), Imp (3001), Lost Soul (3006) |
'shooter' | Chaingunner (65), Cacodemon (3005), Revenant (66), Arachnotron (68), Pain Elemental (71) |
'tank' | Pinky Demon (3002), Spectre (58), Mancubus (67), Hell Knight (69) |
'boss' | Baron of Hell (3003), Arch-Vile (64), Cyberdemon (16), Spider Mastermind (7) |
| Type | DOOM Things |
|---|---|
'health' | Stimpack (2011), Medikit (2012), Health Bonus (2014) |
'armor' | Armor Bonus (2015), Green Armor (2018), Blue Armor (2019) |
'ammo' | Clip (2007), Shells (2008), Rocket (2010), Cell (2047), weapons (2001–2006) |
Extracts textures, flats, and sprites from a WAD and creates Three.js DataTexture objects. All textures are cached for reuse.
Creates a texture manager bound to a loaded WADLoader.
.load()
Initializes the texture manager — loads palette, patch names, texture definitions, flats, and sprites from the WAD. Must be called before using any get* method.
Returns a wall texture as a Three.js DataTexture with nearest-neighbor filtering and repeat wrapping. Composites multi-patch textures and flips vertically for Three.js.
'STARTAN3')
Gets the texture definition (dimensions) for UV calculation.
{ name, width, height, patches[] }
Returns a 64×64 floor/ceiling flat texture as a Three.js DataTexture.
'FLOOR0_1')
Returns a sprite texture for a DOOM thing type, suitable for billboard rendering.
3004 for Former Human)
{ texture, width, height, leftOfs, topOfs }
Parses DOOM column-based picture format (patches and sprites) into RGBA pixel data.
{ width, height, leftOfs, topOfs, pixels: Uint8Array }
Frees all cached Three.js textures. Call when switching WADs or cleaning up.
Applies correct UV tiling and offset to a wall mesh's geometry, matching DOOM's texture alignment.
const level = convertWADMap(mapData);
const texMgr = new WADTextureManager(wad);
texMgr.init();
for (const w of level.walls) {
const wallId = createCube(w.len, 0xaaaaaa, [w.x, w.y, w.z], {
scaleY: w.h, scaleZ: 0.1
});
rotateMesh(wallId, 0, w.ang, 0);
// Apply WAD texture
if (w.texName) {
const tex = texMgr.getWallTexture(w.texName);
const def = texMgr.getTextureDef(w.texName);
if (tex && def) {
const mesh = getMesh(wallId);
mesh.material.map = tex;
mesh.material.needsUpdate = true;
setWallUVs(wallId, w.len * 20, w.h * 20,
def.width, def.height, w.xoff, w.yoff);
}
}
}
Maps DOOM thing type IDs to enemy categories: 'grunt', 'shooter', 'tank', 'boss'.
Maps DOOM thing type IDs to pickup categories: 'health', 'armor', 'ammo'.
Maps DOOM thing type IDs to 4-character sprite name prefixes used in the WAD lump directory.
| Thing Type | Sprite Prefix | Name |
|---|---|---|
| 3004 | POSS | Former Human |
| 9 | SPOS | Former Sergeant |
| 3001 | TROO | Imp |
| 3005 | HEAD | Cacodemon |
| 3002 | SARG | Pinky Demon |
| 3003 | BOSS | Baron of Hell |
| 16 | CYBR | Cyberdemon |
| 7 | SPID | Spider Mastermind |
| 2035 | BAR1 | Barrel |
| 2011 | STIM | Stimpack |
| 2012 | MEDI | Medikit |
| Offset | Size | Description |
|---|---|---|
| 0 | 4 bytes | Tag: IWAD (game WAD) or PWAD (patch WAD) |
| 4 | 4 bytes | Number of lumps (int32 LE) |
| 8 | 4 bytes | Offset to lump directory (int32 LE) |
Each map marker (e.g., E1M1) is followed by these lumps:
| Lump | Description |
|---|---|
| VERTEXES | 2D vertex positions (x, y as int16) |
| LINEDEFS | Lines connecting vertices with sidedef references |
| SIDEDEFS | Texture assignments and sector references per line side |
| SECTORS | Floor/ceiling heights, flat textures, light levels |
| THINGS | Object placements: position, angle, type, flags |
| Lump | Description |
|---|---|
| PLAYPAL | 256-color palette (768 bytes RGB) |
| PNAMES | Patch name list for composite textures |
| TEXTURE1/2 | Composite wall texture definitions |
| F_START–F_END | Floor/ceiling flat textures (64×64, indexed) |
| S_START–S_END | Sprite picture data (column-based format) |
The Nova64 WAD system works with any DOOM-format WAD including:
doom1.wadFull FPS game that loads freedoom1.wad from the server or accepts drag-and-drop. Features level browsing, textured walls, enemy sprites, shooting, and pickups.
Arena shooter with 3 built-in levels and optional WAD loading. Drag-and-drop a .wad file or press L to load via file picker.