bitofcode()
bitofcode · case studyKennedy Michel / solution architect

World of Warcraft addon · Lua

TransPose

A photographer's studio, hidden inside a video game.

World of Warcraft renders its characters in enormous detail — then glues the camera behind your back, moves it when you move, and seals the interesting machinery away behind protected calls. TransPose is the missing front end: a real studio built on the game's own hidden scene-rendering engine — the same one that powers its dressing room — with the bookkeeping to make it behave like a camera instead of a debug view.

30figures posed in one shared 3D scene
13,000+creatures & props, added by display ID
4supported game versions: Retail, Vanilla, TBC and Pandaria
0network calls, zero dependencies
rolesole design & engineering
written inLua

01 The premise

Modern games render characters in a level of detail players make real art out of — portraits, comics, machinima, guild posters. The tools they're handed are crude.

The camera is bolted behind your character and moves when you move, and the rendering machinery that could hold a scene still is sealed away behind calls an addon isn't allowed to make. Yet inside the client sits a small scene-rendering system — the one behind the dressing room and inspect windows — capable of holding several models, each independently placed and animated, in front of a camera you position by hand. It has been there the whole time; it was just never surfaced to players as something you can compose with.

02 The keystone idea

The answer was never a cleverer way to fight the gameplay camera, it was a front end for the studio the game already contains.

TransPose is a studio UI on top of that scene system, plus the bookkeeping needed to make it behave like a camera rather than a debug view: real relative placement and depth for up to thirty figures, a lens you aim by hand, and framing that fits the whole cast.

It's written entirely in Lua against a deliberately restricted sandbox — no filesystem, no network, no threads, and much of the engine hidden behind protected calls an addon isn't allowed to make. Everything it needs is baked into the folder, and it makeszero network calls.

03 The hard parts

A handful of problems turned out to be far more interesting than they first looked, and most of them came down to the engine knowing something it had no way to tell me.

the camera

Aiming at empty space

You place the scene's camera by position and Euler angles — but the sign convention of those angles is undocumented. TransPose was positioning the camera correctly and then aiming it about 20° past the subject, dropping it onto the bottom edge of frame. I recovered the convention by reading another addon'sshipped camera keyframes — real angles already known to look at their models — and solving for the sign. A test now reconstructs the camera basis from the angles handed to the engine, so getting the convention wrong fails the suite instead of shipping.

streaming

Models with no “ready” signal

Measure the cast the instant you add someone and you measure a figure that hasn't loaded — framing puts the newcomer outside the shot. Worse, the first non-zero size you can read is sometimes a tiny placeholder that dives the camera in close and low.Framing now waits until every figure reports a height, and keeps re-confirming for a few seconds, adopting the real size once it arrives.

textures

Creatures that load as white ghosts

A world creature only hands you a model file — the bare geometry, no texture. The texture lives on a display ID that has no runtime lookup at all.TransPose resolves it through a cascade: a per-creature cache it learns once and remembers forever, the pet journal for companions, an off-screen scratch model that reads the ID straight off the live unit, and finally a guided manual lookup — so the common cases are one click and the rare one is a single copy-paste.

names

Animation names the client threw away

The game removed the name column from its animation database years ago and now ships bare numbers, with no way to enumerate or validate them. So I transcribed the human-readable names offline from an open data source and baked them in, and you can search kneel instead of guessing 873.

the library

13,000 creatures, and no API to list them

There is no runtime route from the client to “every creature.” So I mined the facts from the community datamining tables, joined them to their display IDs,validated every one, deduped by appearance, and shipped the result as a raw delimited string that parses lazily into the catalog the first time you open it — thousands of creatures, NPCs and world props, searchable by name and type, costing nothing until you need them.

04 Tested without the game running

The whole addon API is mocked in plain Lua — frames, timers, model scenes, the unit and journal calls — so the entire thing loads and runs under a standalone interpreter.

A smoke suite walks the real user journey. A stresssuite drives 432 combinations of cast size, spacing, scale, zoom and camera angle, and asserts that nobody gets clipped or pushed out of frame. Revert either camera fix above and the suite fails loudly — the convention isn't just correct, it's pinned. Every commit runs green before it lands.

05 What you can do with it

For the player, none of that shows. You open a studio, assemble a cast, and shoot.

30×A multi-figure stage

Your character, groupmates, mounts, pets, dungeon bosses — placed with real depth, not flat layers.

A camera you control

Field of view, distance, orbit, and one-click auto-framing that fits the whole cast — each with a freeze lock.

Posing

A searchable library of named animations, plus a timeline you can scrub, frame-step, speed up, or freeze.

Backdrops

Studio solids, green- and blue-screen for keying, in-game locations, or the live world rendered behind the cast.

Forms & wardrobe

Druid, worgen and dracthyr forms; sheathe or hide any gear slot; put a weapon in a figure's hands; copy a target's outfit.

On-screen shortcuts

A minimap button, a floating shutter that snaps your target into the studio, and studio-only keybinds.

06 The stack

runtime
Lua 5.1WoW Addon APIModelScene / ModelSceneActorXML templates
tooling
LuaJIT test harnesshand-rolled API mockPowerShell build
assets & data
ffmpeg (PNG→TGA)Python data pipelinewago.tools datamining
discipline
smoke + stress suiteszero-networkno dependenciesAll Rights Reserved

07 Why it's genuinely cool

It takes a debug view — a sealed, undocumented corner of a twenty-year-old engine — and turns it into a camera you'd actually want to hold.

No network, no dependencies, tested without the game even running, and shipped as plain readable Lua. The interesting work was never the UI, it was the reverse engineering underneath it — recovering a sign convention from someone else's keyframes, waiting out a load with no ready signal, and building a texture resolver for data the client swears it doesn't have. The studio was in there the whole time; the work was making it hold still long enough to take the picture.