← Back to Documentation Index

📦 WAD System

Load and render classic DOOM WAD files with full texture, sprite, and level support

📋 Overview

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.

💡 Key Features
  • Full WAD Parsing — IWAD and PWAD binary format with lump directory
  • Level Conversion — VERTEXES, LINEDEFS, SIDEDEFS, SECTORS, THINGS → Nova64 3D walls
  • Wall Textures — Composite multi-patch textures from TEXTURE1/TEXTURE2 + PNAMES
  • Floor/Ceiling Flats — 64×64 flat textures with palette lookup
  • Sprite Extraction — Monster and item sprites as billboard textures
  • Collision Data — Rasterized wall segments for physics
  • Thing Classification — Monsters, items, and decorations mapped to game types

🚀 Quick Start

Load and render a WAD file
// 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);
Load WAD with textures
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
Drag-and-drop WAD loading
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());
}

📦 WADLoader

Parses IWAD and PWAD binary files, providing access to the lump directory and map data.

new WADLoader()

Creates a new WAD loader instance.

wad.load(arrayBuffer)

Parses a WAD file from an ArrayBuffer. Validates the IWAD/PWAD header and reads the lump directory.

arrayBuffer ArrayBuffer — The raw WAD file data
Returns: WADLoader — The loader instance (for chaining)
Throws: Error — If the file is not a valid WAD
wad.getMapNames()

Returns an array of map names found in the WAD (e.g., E1M1, MAP01).

Returns: string[] — Map names matching E#M# or MAP## patterns
wad.getMap(name)

Parses all geometry lumps for a map and returns the parsed data.

name string — Map name (e.g., 'E1M1')
Returns: object | null{ name, vertexes, linedefs, sidedefs, sectors, things }
wad.getPalette()

Reads the PLAYPAL lump — 256 RGB color entries (768 bytes).

Returns: Uint8Array | null — 768-byte palette (256 × RGB)
wad.getLump(name)

Gets any raw lump by name.

name string — Lump name (case-insensitive)
Returns: object | null{ data: Uint8Array, size: number }
wad.getFlatLumps()

Extracts all floor/ceiling flat textures between F_START/F_END markers.

Returns: object{ [name]: Uint8Array(4096) } — 64×64 indexed color data
wad.getPNames()

Parses the PNAMES lump — the list of patch names used by wall textures.

Returns: string[] — Patch name list
wad.getTextureDefs(lumpName)

Parses TEXTURE1 or TEXTURE2 lump — composite wall texture definitions.

lumpName string'TEXTURE1' or 'TEXTURE2'
Returns: object{ [name]: { name, width, height, patches[] } }
wad.getSpriteLumps()

Extracts all sprite data between S_START/S_END markers.

Returns: object{ [name]: Uint8Array } — Raw sprite picture data

🗺️ convertWADMap

convertWADMap(map, scale?)

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.

map object — Map data from wad.getMap()
scale number — Scale factor (default: 1/20). DOOM units → Nova64 units
Returns: object — Level data:
PropertyTypeDescription
wallsarrayWall segments: { x, y, z, len, h, ang, light, texName, xoff, yoff, step?, upper? }
colSegsarrayCollision points: { x, z, r }
enemiesarrayEnemy spawns: { x, z, type, doomType }
itemsarrayItem pickups: { x, z, type, doomType }
playerStartobject{ x, z, angle, floorH }
sectorsarraySector data: { floorH, ceilH, floorFlat, ceilFlat, light }

Enemy Types

TypeDOOM 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)

Item Types

TypeDOOM 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)

🎨 WADTextureManager

Extracts textures, flats, and sprites from a WAD and creates Three.js DataTexture objects. All textures are cached for reuse.

new WADTextureManager(wadLoader)

Creates a texture manager bound to a loaded WADLoader.

wadLoader WADLoader — A WADLoader that has already called .load()
texMgr.init()

Initializes the texture manager — loads palette, patch names, texture definitions, flats, and sprites from the WAD. Must be called before using any get* method.

texMgr.getWallTexture(name)

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.

name string — Texture name (e.g., 'STARTAN3')
Returns: THREE.DataTexture | null
texMgr.getTextureDef(name)

Gets the texture definition (dimensions) for UV calculation.

Returns: object | null{ name, width, height, patches[] }
texMgr.getFlatTexture(name)

Returns a 64×64 floor/ceiling flat texture as a Three.js DataTexture.

name string — Flat name (e.g., 'FLOOR0_1')
Returns: THREE.DataTexture | null
texMgr.getSpriteTexture(thingType)

Returns a sprite texture for a DOOM thing type, suitable for billboard rendering.

thingType number — DOOM thing type ID (e.g., 3004 for Former Human)
Returns: object | null{ texture, width, height, leftOfs, topOfs }
texMgr.parsePicture(data)

Parses DOOM column-based picture format (patches and sprites) into RGBA pixel data.

data Uint8Array — Raw picture lump data
Returns: object | null{ width, height, leftOfs, topOfs, pixels: Uint8Array }
texMgr.dispose()

Frees all cached Three.js textures. Call when switching WADs or cleaning up.

🧱 setWallUVs

setWallUVs(meshId, wallDoomLen, wallDoomH, texWidth, texHeight, xoff, yoff)

Applies correct UV tiling and offset to a wall mesh's geometry, matching DOOM's texture alignment.

meshId string — The Nova64 mesh ID (from createCube)
wallDoomLen number — Wall length in DOOM units
wallDoomH number — Wall height in DOOM units
texWidth number — Texture width in pixels
texHeight number — Texture height in pixels
xoff number — Horizontal texture offset
yoff number — Vertical texture offset
Textured wall with UV tiling
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);
    }
  }
}

📊 Constants

THING_MONSTERS

Maps DOOM thing type IDs to enemy categories: 'grunt', 'shooter', 'tank', 'boss'.

THING_ITEMS

Maps DOOM thing type IDs to pickup categories: 'health', 'armor', 'ammo'.

THING_SPRITE_PREFIX

Maps DOOM thing type IDs to 4-character sprite name prefixes used in the WAD lump directory.

Thing TypeSprite PrefixName
3004POSSFormer Human
9SPOSFormer Sergeant
3001TROOImp
3005HEADCacodemon
3002SARGPinky Demon
3003BOSSBaron of Hell
16CYBRCyberdemon
7SPIDSpider Mastermind
2035BAR1Barrel
2011STIMStimpack
2012MEDIMedikit

📖 WAD Format Reference

File Structure

OffsetSizeDescription
04 bytesTag: IWAD (game WAD) or PWAD (patch WAD)
44 bytesNumber of lumps (int32 LE)
84 bytesOffset to lump directory (int32 LE)

Map Lumps

Each map marker (e.g., E1M1) is followed by these lumps:

LumpDescription
VERTEXES2D vertex positions (x, y as int16)
LINEDEFSLines connecting vertices with sidedef references
SIDEDEFSTexture assignments and sector references per line side
SECTORSFloor/ceiling heights, flat textures, light levels
THINGSObject placements: position, angle, type, flags

Texture Lumps

LumpDescription
PLAYPAL256-color palette (768 bytes RGB)
PNAMESPatch name list for composite textures
TEXTURE1/2Composite wall texture definitions
F_START–F_ENDFloor/ceiling flat textures (64×64, indexed)
S_START–S_ENDSprite picture data (column-based format)
💡 Compatible WAD Files

The Nova64 WAD system works with any DOOM-format WAD including:

  • FreeDoom — Free, open-source DOOM-compatible IWAD
  • DOOM Sharewaredoom1.wad
  • Custom PWADs — Community-made level packs

🎮 Example Carts

wad-demo — FreeDoom WAD Explorer

Full FPS game that loads freedoom1.wad from the server or accepts drag-and-drop. Features level browsing, textured walls, enemy sprites, shooting, and pickups.

fps-demo-3d — Neo-Doom

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.