WOW MODEL VIEWER

Render 1.12.x character models on the web.

Open-source Three.js library with skeletal animation, equipment compositing, and real-time playback. Framework-agnostic. CDN-agnostic. 20 race/gender combinations. Built for Turtle WoW, compatible with any 1.12.x Classic WoW server.

$ npm i classic-wow-model-viewer three

QUICK START

import { useRef, useEffect } from 'react'
import { ModelViewer, createCdnResolver } from 'classic-wow-model-viewer'

function CharacterViewer({ race = 'human', gender = 'male' }) {
  const el = useRef(null)

  useEffect(() => {
    const viewer = new ModelViewer({
      container: el.current,
      assets: createCdnResolver('https://your-cdn.com'),
    })
    viewer.loadCharacter(race, gender)
    return () => viewer.dispose()
  }, [race, gender])

  return <div ref={el} style={{ width: '100%', height: 500 }} />
}
<script>
  import { onMount, onDestroy } from 'svelte'
  import { ModelViewer, createCdnResolver } from 'classic-wow-model-viewer'

  let el, viewer

  onMount(() => {
    viewer = new ModelViewer({
      container: el,
      assets: createCdnResolver('https://your-cdn.com'),
    })
    viewer.loadCharacter('human', 'male')
  })

  onDestroy(() => viewer?.dispose())
</script>

<div bind:this={el} style="width:100%;height:500px" />
<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import { ModelViewer, createCdnResolver } from 'classic-wow-model-viewer'

const el = ref(null)
let viewer

onMounted(() => {
  viewer = new ModelViewer({
    container: el.value,
    assets: createCdnResolver('https://your-cdn.com'),
  })
  viewer.loadCharacter('human', 'male')
})

onUnmounted(() => viewer?.dispose())
</script>

<template>
  <div ref="el" style="width:100%;height:500px" />
</template>
import { ModelViewer, createCdnResolver } from 'classic-wow-model-viewer'

const viewer = new ModelViewer({
  container: document.getElementById('viewer'),
  assets: createCdnResolver('https://your-cdn.com'),
})

// Load any of 20 race-gender combos
viewer.loadCharacter('orc', 'male')

// Animations: Stand, Walk, Run, Attack, Dance, ...
viewer.playAnimationByName('Dance')

SELF-HOST ASSETS

The viewer loads assets via a resolver — any static file host works. Use the included extraction scripts to convert a WoW 1.12.x client into web-ready model files, then upload the output folder to your CDN. See the full pipeline docs for details.

# Clone and install the tools package
git clone https://github.com/JollyGrin/turtle-wow-model-viewer.git
cd turtle-wow-model-viewer/packages/tools
bun install

# Point at your WoW 1.12.x client (TurtleWoW, vanilla, etc.)
bun run setup -- ~/Games/TurtleWoW

# Extract and convert all assets (~10 min, outputs to public/)
bun run extract

# Result: public/ is ready to upload as-is
ls public/   # models/  items/  item-textures/

CDN Folder Structure

Point createCdnResolver() at the root of this folder. The viewer resolves all paths from there.

public/
├── models/
│   └── {race}-{gender}/        ← 20 combos (human-male, orc-female, ...)
│       ├── model.json           ← mesh manifest (bones, geosets, attachments)
│       ├── model.bin            ← vertex + index buffers (40 B/vertex)
│       ├── anims.bin            ← skeletal animation keyframes
│       └── textures/
│           ├── skin.tex         ← character body (uint16 w + uint16 h + RGBA)
│           └── hair.tex         ← hair texture
│
├── items/                       ← equipment models (optional)
│   ├── weapon/{slug}/
│   │   ├── model.json / model.bin
│   │   └── textures/{variant}.tex
│   ├── head/{helm}/{race}-{gender}/
│   └── shoulder/{slug}/left/ & right/
│
└── item-textures/               ← armor compositing layers (optional)
    └── {Region}/{name}_{M|F|U}.tex