Building Halo PC Nametags: Understanding Chimera's Widescreen Renderer
I didn't set out to reverse engineer Chimera's widescreen renderer.
I just wanted to draw a nametag above a player's head.
At first glance, the task seemed simple enough. Capture the camera position, project a player's position into screen space, and call draw_text(). Halo already provides most of the information we need, and Chimera conveniently exposes a Lua interface for drawing text.
As it turned out, those were the easy parts.
The further I dug into the problem, the more I discovered that understanding Halo's coordinate system, Chimera's widescreen implementation, and the relationship between the two was far more complicated than I had expected.
By the end of it, I'd learned far more about Chimera's renderer than I ever intended to.
The original plan
The goal was straightforward enough.
For each player, I would:
Obtain the player's world position.
Capture the current camera position and orientation.
Convert the world position into screen coordinates.
Draw the player's name above their head.
The projection code itself wasn't especially complicated. Using the camera's forward, right, and up vectors, I transformed a world-space position into normalised device coordinates before converting them into screen coordinates.
Everything appeared to work correctly.
Then things started drifting.
To make matters even more interesting, one of the community references I was using described the `draw_text()` parameters slightly differently from the way Chimera itself interpreted them. I wouldn't discover that until much later.
The first clue
My initial assumption was that the projection mathematics were wrong.
The obvious calculation looked something like this:
local screen_center = screen_width / 2 That seems perfectly reasonable. After all, the center of a 1920-pixel-wide display should be 960 pixels.
Except it wasn't.
The more I tested, the stranger things became. Some resolutions behaved correctly. Others didn't. Sometimes the text appeared to drift horizontally. Sometimes it aligned perfectly at one resolution before becoming increasingly inaccurate at another.
Naturally, I blamed my trigonometry.
Unfortunately, the trigonometry wasn't the problem.
Capturing the camera
One of the first issues I encountered was a subtle one-frame delay.
Initially, the nametags were rendered during the preframe event. Camera data, however, was being captured during precamera.
This meant that each frame was being projected using the previous frame's camera position, producing a noticeable delay whenever the camera moved quickly. Eventually I moved the rendering code directly into OnPreCamera(), immediately after capturing the camera state.
The lag disappeared instantly.
Problem solved.
Or so I thought.
The mystery of the missing pixels
While investigating the issue, I discovered something extremely surprising.
Chimera wasn't drawing directly into the monitor's native coordinate space at all.
Instead, it was maintaining an internal coordinate system derived from Halo's original 640×480 interface. Even on a modern widescreen display, Chimera continued to operate within this virtual coordinate space.
The implications of this were enormous.
The center of the screen wasn't 960 pixels.
It wasn't 640 pixels.
It was still 320.
Even more surprisingly, Chimera wasn't simply stretching Halo's interface to fit a modern display. Instead, it was translating between Halo's original 4:3 coordinate system and the monitor's actual resolution.
That meant I wasn't dealing with a native 1920×1080 coordinate system at all. I was dealing with an abstraction layer built on top of one.
Internally, Chimera adjusted its calculations according to the selected widescreen mode while preserving Halo's original coordinate system [1]. The renderer supports three modes:
Full-screen widescreen rendering.
A centered 4:3 region.
A centered 16:9 region.
Once I understood this, several other mysteries immediately began to make sense.
Understanding the projection system
Eventually I settled on the following approach:
local screen_x = halfw + ndc_x * halfw
local screen_y = 240 - ndc_y * 240
The important detail here is that halfw isn't derived directly from the monitor resolution. Instead, it depends upon Chimera's internal projection state.
When widescreen rendering is enabled, the center remains fixed at 320.
The vertical field of view must also be calculated separately from the horizontal field of view, since Chimera scales the horizontal and vertical axes differently.
The head isn't where you think it is
Another challenge involved determining exactly where the nametag should appear.
My first attempt simply added a constant offset above the player's feet. This worked well enough while players were standing, but it quickly fell apart when vehicles entered the picture.
The reason turned out to be surprisingly simple.
When a player is seated inside a vehicle, the position stored at +0x5C is not the player's world position at all. Instead, it is an offset relative to the seat itself.
Eventually I discovered that the player's actual world position could be found elsewhere, while the head itself corresponded to node 12 within the biped node array.
That allowed the nametag to follow crouching, animation changes, and seated players naturally.
draw_text() wasn't doing what I thought
Another rabbit hole emerged when I began digging through the available community resources.
Chalwk, a long-time member of the Halo modding community, had written an excellent reference for Chimera [2], and I naturally assumed the following function signature was correct:
draw_text(text, left, top, right, bottom, font, align, a, r, g, b) The documentation itself was perfectly understandable, but my implementation refused to behave as expected.
Eventually I discovered that the function was actually treating those values as:
draw_text(text, left, top, width, height, font, align, a, r, g, b) In other words, the final two position values represented the dimensions of the drawing region rather than the coordinates of its opposite corner.
Once alignment entered the picture, things became even more interesting. Centering text wasn't a matter of supplying the middle of the screen as an x coordinate; instead, Chimera calculated the final position relative to the supplied region.
After tracking down the behaviour and confirming it against the source code, I sent my findings back so the reference could be updated.
At least the next person won't have to spend several hours wondering why their nametags are drifting across the screen.
The final result
After several days of experimentation, calibration overlays, memory inspection, logging, and far more trigonometry than I expected, the system finally behaved correctly.
Standing players worked correctly.
Crouching players worked correctly.
Vehicle occupants worked correctly.
More importantly, I finally understood why the original implementation had failed.
The problem wasn't my projection mathematics.
The problem wasn't the field-of-view calculation.
The problem wasn't the aspect ratio.
The problem was that I was trying to solve a 2026 problem using a 2003 coordinate system without realising that Chimera was carefully translating between the two.
Final thoughts
Reverse engineering often follows a familiar pattern.
You begin with a very specific objective.
You discover a bug.
You investigate the bug.
You uncover a system you didn't know existed.
You spend several days understanding that system.
Eventually, you solve the original problem.
In my case, I wanted to draw a nametag above a player's head.
Instead, I ended up learning how Chimera renders widescreen interfaces.
References
- [1] Chimera Source Code — Snowy Mouse / Chimera contributors
Used to verify the widescreen scaling, text anchoring, and internal 640×480 coordinate behaviour described in this article.
- [2] Scripting with Chimera - Client-Side Lua — Chalwk
An excellent community reference for Chimera’s Lua API. It was the starting point for much of this work, and the draw_text() parameter description was later updated from left, top, right, bottom to left, top, width, height based on the findings from this project.