Onboarding
First hour on Khada, in order.
What Khada is
A 3D model viewer for League of Legends — web, iOS/Android, and Apple QuickLook. Pipeline:
CDragon / local League install
↓
extractor (C# .NET) — WAD → GLTF + MaterialExtras
↓
processor (Node, tsx) — gltfpack + texture compression + manual fixes
↓
web / mobile / ar — user-facingautomation (Cloudflare Worker) polls CDragon for new skins and fires the pipeline on Telegram command; ar serves USDZ for Apple QuickLook.
Monorepo
packages/
extractor/ C# .NET CLI
processor/ Node orchestrator
web/ Astro + Three.js viewer
ar/ Worker for USDZ / QuickLook
usdz/ GLB→USDZ library
automation/ Worker for CDragon polling + Telegram bot
settings/ YAMLs for manual fixes, aliases, news
docs/ this sitePackage manager: pnpm@10.10.0. Full command catalog is in the root CLAUDE.md.
First-hour tours
Extract one skin
cd packages/extractor
dotnet run --project LeagueConvert.CommandLine -- \
convert-wad Zoe -o output/zoe -s -a --skins 0- Auto-detects a local League install (
LeaguePathResolver.cs). - If absent, downloads the PBE WAD via CDTB — needs
pip install cdtbon one of the Python interpretersWadDownloader.csprobes (preferspython3.10..12over genericpython3). - First run downloads
hashes.game.txtandhashes.binhashes.txtfrom CommunityDragon.
Run the viewer
pnpm run webThe Three.js engine is packages/web/src/engine/index.ts (~3500 lines). Every MaterialExtras key the extractor writes has a handler here.
Full pipeline end-to-end
pnpm run processor generate-files "[1001]" --remote
# skin ID = championId * 1000 + skinIndexRuns the extractor, post-processes with gltfpack, writes to packages/processor/output/.
Debugging a specific skin
Always fetch the CDragon bin.json first — don't start by running the extractor:
https://raw.communitydragon.org/pbe/game/data/characters/{champion}/skins/skin{skinId mod 1000}.bin.json
https://raw.communitydragon.org/pbe/game/data/characters/{champion}/animations/skin{skinId mod 1000}.bin.jsonIf the JSON reproduces the bug, it's a parse/mapping bug in our code. If the JSON is clean but our output isn't, the bug is downstream of parsing. Details: CDragon bin.json endpoints.
Hex hash in a material dump? Start at hashing.
Wrong tint / dark texture / red overlay? Almost always a shader profile issue — common pitfalls, especially 1 and 4.
Where to go next
Before touching…
- Materials or shaders → read common pitfalls end to end. The ten numbered items are scar tissue, not style advice.
- Skeletons or animations → read skeletons & animations. The two-pass track mapping and the joint-snap reparenting bake are both easy to break.
- New file format → file formats + the relevant upstream wiki page.
When a model/texture/chroma looks wrong in the viewer → debugging the model pipeline — the toolkit (extractor CLI, bin.json, chrome-devtools-mcp, GLB Python inspection, MD5 triage) for isolating which stage broke it.
File map
| Want to change | Open first |
|---|---|
| CLI flags, entry point | LeagueConvert.CommandLine/Program.cs |
| WAD download / caching | LeagueConvert.CommandLine/WadDownloader.cs |
| Champion path resolution | LeagueConvert.CommandLine/LeaguePathResolver.cs |
| Hash table loading + manual overlay | LeagueConvert/IO/HashTables/HashTables.cs |
| WAD parsing | OfficialLeagueToolkit/Core/Wad/WadFile.cs + LeagueConvert/IO/WadFile/StringWad.cs |
| Shader param → material extras | LeagueConvert/IO/Skin/StaticMaterial.cs |
| GLTF export | LeagueConvert/IO/Skin/Extensions/SkinExtensions.cs |
| Skeleton + track mapping | LeagueToolkit/IO/SkeletonFile/Skeleton.cs |
| Animation parsing | LeagueToolkit/IO/AnimationFile/Animation.cs |
| Joint-snap bake | LeagueConvert/IO/Skin/Skin.cs (methods named *JointSnap*, ComputeWorldTransform, RewriteVictimTrackAsWorld) |
| Three.js engine | packages/web/src/engine/index.ts |
| Telegram bot commands | packages/automation/src/worker/index.ts |
| Per-skin manual overrides | packages/settings/model-fixes*.yml + siblings |
Poking at a WAD ad hoc without our extractor? Grab the Rust wadtools CLI.