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.
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.
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.
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.
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.
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.
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
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.