Building a 3D World with Playable Characters
Contents
- --Overview3
- 00About this journal6
- IEngine, gameplay and first characters8
- 01Three.js setup: jump input and pointer lock9
- 02Generated mesh to rigged game character11
- 03Weapon grip in hand and a fake Mixamo rig13
- 04Mixamo clips: fingers, feet, sword flips16
- 05Lag fixes, and NPCs built on player code19
- IIArt tools in the pipeline22
- 06Blender hand sockets, Git LFS, review rules23
- 07Clone abilities, aim offset, hand-keyed draw25
- 08Reaching 120 fps and measuring frames right29
- 09Map pipeline: greybox, then a modelled kit31
- 10High-to-low baking and local AI on 6 GB34
- IIIThe character quality problem36
- 11Unarmed character: decimation, pose, drift37
- 12Fused-shell hair: why it tears when it moves39
- 13Character quality gap: cause and four routes43
- IVMaking a generated character game-ready46
- 14Agent E rebuild: lineup, timeline and cost47
- 15Faceting: weld before decimating49
- 16Seam gaps and a capped collar56
- 17Master first, then reduce61
- 18Fixing AI paint errors by hand65
- 19Texture atlas, first rig, source holes69
- 20Voxel remesh fills: the monster neck74
- 21Full-res body: hood, shoulders, normals78
- 22Welding a new head onto a body84
- 23Game budget, KTX2 and skinning spikes89
- 24Build cost and the rules it left95
- VReference98
- 25Architecture reference99
- 26Engineering rules and their incidents101
- 27Roadmap103
Building a 3D World with Playable Characters
September to October 2026 · The first step toward 3D worlds people can walk through: how we built a playable third-person
game on Three.js and Electron, and what it took to make its characters and its world hold up.
This is the engineering journal of TPS Arena. It covers what we built, what broke, which routes we weighed, what we
picked and what it cost. Each chapter is written as problems, causes and solutions. Where our first diagnosis was
wrong, the chapter says so and gives the real cause.
The short version is that getting a shooter to run is not the hard part. The first playable build came quickly. Everything
after that is the distance between a build that runs and a game that feels right. That distance is made of problems like
a jump that only worked one frame in three, hair bound to a character's hands, a sword that took twenty iterations to
sit in a fist, and a frame-rate meter that reported the wrong number.
Why a game first
A game is the hardest everyday test of a 3D world. Its characters have to move, collide, be lit and hold a frame rate
while real people use them. Everything in this journal, from making a generated model fit for real time to keeping a
five-a-side match above 100 fps, is groundwork for the worlds we want to build next: digital buildings that mirror real
landmarks, places to walk through and shop in, and previews that let a property developer show a project before it is
built, in time in VR and AR as well as on a screen.
TPS Arena is the case study. This journal is the record of what it took, problem by problem.
The tools, in the order they joined
| Order | Tool | The bottleneck it removed | What it did not fix |
|---|---|---|---|
| 1 | Three.js + Electron | A playable game in the browser and as a desktop build | Characters made of boxes |
| 2 | Meshy (image to 3D) | Sculpted, textured characters without a character artist | One fused shell: no separate hair, no clean topology |
| 3 | Mixamo (skeleton + animation clips) | Real motion in place of poses written as numbers | Clips are generic. A character's own moves still need authoring |
| 4 | Blender, scripted | Placing a sword by dragging it once, not by twenty numeric iterations | The result still has to be judged by eye |
| 5 | Higgsfield (pictures and 3D inside Blender) | Concept art and map pieces in one window | Credits. And generation was the wrong tool for architecture |
| 6 | Local models on the GPU (ComfyUI, Hunyuan3D) | Picture edits and rough 3D at no credit cost | A 6 GB card is the ceiling |
| 7 | Cascadeur | Planned: hand-authored combat animation | Installed, not used yet |
| 8 | KTX2 / Basis Universal (compressed textures) | Character textures stay compressed on the graphics card: four times less video memory at the same map sizes | Draw calls. Memory was no longer the limit; the number of things drawn per frame is |
Chapters
The chapters are in build order, grouped into five parts. Page 00 sets out what we built and the tools behind it. Part V
holds the two reference pages, and the roadmap is always the last chapter.
| # | Chapter | What happened | Milestone |
|---|---|---|---|
| Part I · Engine, gameplay and first characters | |||
| 01 | Three.js setup: jump input and pointer lock | We chose Three.js and Electron, fixed a jump that only registered one frame in three, added a post- processing stack and fixed a mouse capture that failed silently. | v0.01 to v0.05. A playable build and a portable exe. |
| 02 | Generated mesh to rigged game character | We replaced code-built characters with a Meshy model on a skeleton. We reduced a 2.3 million triangle mesh without smearing its texture and re-bound hair that the automatic rig had tied to the hands. | v0.06 to v0.14. Agent E runs in game as a rigged GLB (v0.10). |
| 03 | Weapon grip in hand and a fake Mixamo rig | Agent S's grip took about twenty numeric iterations. We diagnosed the real problem: art was being authored as numbers in code. We also found that a "Mixamo rig" from Meshy was not Mixamo's skeleton. | v0.15 to v0.16. Waist twist down from 177 degrees to at most 18. written. |
| 04 | Mixamo clips: fingers, feet, sword flips | Agent E and Agent Zero moved onto Mixamo's skeleton. We fixed open fingers caused by a bone write cache, a sword that flipped between clips, and feet that sat at the wrong angle. | v0.17 to v0.18. A 13-rule asset contract ( tools/herocheck.mjs) and . |
| 05 | Lag fixes, and NPCs built on player code | We traced freezes to dynamic lights and to bullets tested against skinned bodies. We found 48 colliders that were never drawn. We rebuilt NPCs so they run the character's own code. | v0.19 to v0.30. Bot extends (v0.23). A pickup freeze of up to 4,689 ms became 17 to 25 ms. |
| Part II · Art tools in the pipeline | |||
| 06 | Blender hand sockets, Git LFS, review rules | The grip was solved by placing a socket by hand in Blender. We moved assets to Git LFS, built Agent L and his kit, and set the rule that a diagnosis is reported before any fix. | v0.32. The game matches the Blender socket to three decimals. . |
| 07 | Clone abilities, aim offset, hand-keyed draw | We rebuilt Agent L's clone as an ability, not a character. We turned the camera so strikes meet the crosshair. We authored the sword draw by hand, frame by frame. | v0.32.45 to v0.33.12. Camera turn fixed at 12 degrees. The draw's cut lands 0.23 s after the click. |
| 08 | Reaching 120 fps and measuring frames right | We measured first, then pooled clones, added bounding-volume trees and baked shadows. We found the frame meter was counting loop ticks, not finished frames. | v0.33.4. Low went from 29 fps to 116 to 137 fps in 5 v 5. npm run . |
| 09 | Map pipeline: greybox, then a modelled kit | Olympus shipped as a greybox first. We then spent about 250 credits generating pieces that should have been modelled in Blender for nothing. | v0.33 to v0.35.3. Olympus V1 and V2. docs/LESSONS.md started. |
| 10 | High-to-low baking and local AI on 6 GB | Straight decimation shattered a statue's blade. We replaced it with a high-to-low bake. We set up ComfyUI and Hunyuan3D on a 6 GB card. | v0.35.5 to v0.36. All eight gods stand on Olympus V2. . |
| Part III · The character quality problem | |||
| 11 | Unarmed character: decimation, pose, drift | The first character with no weapon. We reduced his mesh from 655,622 to 59,996 triangles with the face protected, fixed a rest-pose mismatch in the Blender preview and a 12-degree sideways drift. | v0.37 and v0.37.1. One movement rule for every character. . |
| # | Chapter | What happened | Milestone |
|---|---|---|---|
| 12 | Fused-shell hair: why it tears when it moves | The first healer. Her kit was quick. Shortening her hair exposed that the generated model has no back under the hair. | v0.37.2 on her own branch. Kit checker: 49 abilities, 0 mismatches. |
| 13 | Character quality gap: cause and four routes | We compared how our characters are constructed with how Fortnite and Overwatch characters are constructed, and weighed four routes to close the gap. | A five-step plan: hair bake-off, per- clump hair chains, character rework, netcode, stay on Three.js. |
| Part IV · Making a generated character game-ready | |||
| 14 | Agent E rebuild: lineup, timeline and cost | Four days and more than a hundred named builds rebuilding our first character, Agent E. This chapter gives the lineup and the cost; the ten that follow take one problem each: why her face looked worse in the game, seams and a collar that moved, the master we skipped, colour only an eye can fix, a shoulder hole that was in the source, the monster neck of a one- shell route, the full-resolution body, the head weld (W1 to W7), and the road to game size. 35 to 48 hours of active work, much of it by hand in Blender. | v0.38. W7, the new benchmark for Agent E: 47,557 triangles in game, textures compressed from 213 MB to 53 MB on the GPU. Rule One: the eye marks, the script measures. |
| 15 | Faceting: weld before decimating | Why the in-game face looked worse than its sculpt: the wedges were our own unwelded reduction. Twelve free repaints and three remeshes ranked by eye. | Weld before reducing, bake after. Face from T8, hair paint from T3. |
| 16 | Seam gaps and a capped collar | The first part-by-part rebuilds, holes at every seam, and a collar that took eleven builds. | R8fix v6, on par with the old model (56,875 triangles). |
| 17 | Master first, then reduce | The full-resolution master we had skipped, and the R8x family reduced from it. | R8x at 39,344 triangles. Master first, then reduce. |
| 18 | Fixing AI paint errors by hand | Red paint on her collarbones, three automatic fixes that looked like burn marks, and the brush that replaced them. | R8x hand-painted: the pick. |
| 19 | Texture atlas, first rig, source holes | A 63 MB atlas, the first rig, and a shoulder hole that had been in the source all along. | Streetwear X in the game (46,709 triangles). |
| 20 | Voxel remesh fills: the monster neck | Collar welds, two routes built and rejected in an afternoon, the monster neck of a one-shell route, and Meshy's remesh made into a finished figure. | B7 kept as the benchmark body. M8x in the game. Rule One. |
| 21 | Full-res body: hood, shoulders, normals | The T8 sculpt at full detail as the body, a hood top nobody had seen, and a shoulder that took eleven attempts, much of it by hand in Blender. | Option 2, the body every later build stands on. |
| 22 | Welding a new head onto a body | A new head welded to the body so face and neck read as one person (W1 to W7). | W7, the new benchmark for Agent E. |
| 23 | Game budget, KTX2 and skinning spikes | From the 475,573-triangle benchmark to game size by hand, rigged, with textures compressed for the graphics card. | v0.38. 47,557 triangles, 213 MB down to 53 MB on the GPU. |
| 24 | Build cost and the rules it left | The machine, the process, and the 35 lessons the rebuild wrote into our documents. | The next build ran in 56 seconds. |
| Part V · Reference | |||
| 25 | Architecture reference | Reference. The decisions that shape the game, how a character is made, how it is tested, and the performance budget. | About 15,000 lines of plain JavaScript. Characters, abilities, weapons and maps are data. |
| # | Chapter | What happened | Milestone |
|---|---|---|---|
| 26 | Engineering rules and their incidents | Reference. Every rule we follow, with the incident that produced it. | Measure before changing. Decide looks by eye, in the game. |
| 27 | Roadmap | Always the last chapter. What is next and why: carrying Agent E's recipe to the rest of the roster, performance on a mid-range card, netcode for LAN play and the remaining map work. | Target: six characters, one finished map, playable on LAN. |
About this journal
What this journal is, what we built, and the tools behind it.
What it is
This is the engineering journal of TPS Arena, a third-person shooter built on Three.js and Electron. It covers the build
from the first playable version to v0.38. It is a record of problem solving. Each chapter takes a stretch of the build and
explains what we made, what went wrong, why it went wrong and how we fixed it.
Every figure in it was measured. Frame times, triangle counts, joint angles and damage numbers come from our own
test harness, our benchmark and the patch notes.
What we built
| Area | Where it stands |
|---|---|
| The game | A third-person shooter that runs in a browser or as a desktop app. |
| Versions | v0.01 to v0.38. About 170 entries in docs/PATCH_NOTES.md, each with what changed and why. |
| Code history | 478 commits and 42 tags. |
| Characters | Agent E, Agent S, Agent Zero, Agent L, Agent B, Agent J and Agent C. |
| Maps | Olympus, in two playable versions. |
| Project documents | 13 files: developer guide, handoff, characters, maps, lessons, character kits, clips, the Agent L draw, Blender setup and lore. |
The busiest series by far is v0.32, with 63 entries. It covers Agent S's grip, Agent L, the clones and the camera.
The kind of work changed part way through. The early chapters are code, with many small commits. The later chapters
are art in Blender, with few commits and long stretches of modelling.
The stack and the tools
| Tool | The bottleneck it removed | What it did not fix |
|---|---|---|
| Three.js + Electron | A playable game in the browser and as a desktop build | Characters made of boxes |
| Tool | The bottleneck it removed | What it did not fix |
|---|---|---|
| Meshy (image to 3D) | Sculpted, textured characters without a character artist | One fused shell: no separate hair, no clean topology |
| Mixamo (skeleton + animation clips) | Real motion in place of poses written as numbers | Clips are generic. A character's own moves still need authoring |
| Blender, scripted | Placing a sword by dragging it once, not by twenty numeric iterations | The result still has to be judged by eye |
| Higgsfield (pictures and 3D inside Blender) | Concept art and map pieces in one window | Credits. And generation was the wrong tool for architecture |
| Local models on the GPU (ComfyUI, Hunyuan3D) | Picture edits and rough 3D at no credit cost | A 6 GB card is the ceiling |
| Cascadeur | Planned: hand-authored combat animation | Installed, not used yet |
| KTX2 / Basis Universal (compressed textures) | Character textures stay compressed on the graphics card: four times less video memory at the same map sizes | Draw calls. Memory was no longer the limit |
Engine, gameplay and first characters
- 01Three.js setup: jump input and pointer lock
- 02Generated mesh to rigged game character
- 03Weapon grip in hand and a fake Mixamo rig
- 04Mixamo clips: fingers, feet, sword flips
- 05Lag fixes, and NPCs built on player code
Three.js setup: jump input and pointer lock
September 2026 · v0.01 to v0.05 · A playable shooter, the engine decision, and the first bugs.
| What we built | A playable third-person shooter in the browser, with a portable desktop exe, code-built characters, bots, a post-processing stack and graphics presets. |
| The challenge | A jump that ignored two key presses out of three. A mouse capture that failed silently. And the question of whether the engine was the limit on how the game looked. |
| Key decisions | Three.js in the browser, wrapped in Electron. Stay on it and do not move to Unreal. Log every change as a patch note from the start. |
| The result | A build that plays correctly, an engine decision recorded as final, and a clear finding: the gap was character assets, not the renderer. |
1. The brief and the stack
The brief was a third-person shooter with customisable characters. Agents have skills and abilities, as in Valorant. The
camera is third person, as in Marvel Rivals. New agent models would be added later from reference pictures.
The brief did not name an engine. We chose Three.js in the browser, wrapped in Electron for a desktop build.
Characters were built in code and the enemies were bots. That gave us a playable build and a portable exe.
2. The jump
Problem. A player standing on a box could not reliably jump off it. Jumping and bunny hopping are core movement in
this genre, so this had to be right.
Cause. We found it by reading the physics, not by guessing. On a box, the ground check only registered once every
three frames. Gravity had to sink the capsule into the box before it counted as grounded. Space was ignored two
times out of three.
Solution. A real floor probe every frame, plus coyote time, jump buffering and bunny hopping.
The same round of play set our quality bar for everything after it.
| Area | Our bar |
|---|---|
| Sound | Gunfire that sounds real and is pleasant to listen to. |
| Animation | The first animation read as blocky. It has to look like a character, not a block figure. |
| Maps | A map must look like a place, not an aim trainer. |
3. Is the gap Unreal Engine?
Problem. Our goal is animation in the class of Overwatch. An Overwatch-lite or Fortnite-lite result is good enough for
a beta. The build was far from that. We had to decide whether the engine was the gap, and whether the existing code
would carry over or need a rewrite.
| Option | What it gives | Trade-off | Verdict |
|---|---|---|---|
| Switch to Unreal | Native AAA tooling | The existing code is thrown away. The work moves into a graphical editor that cannot be read, changed and tested as plain code, so most character work becomes manual. | Rejected |
| Stay on Three.js | PBR, shadows, skinned animation and post-processing. Plain code in one repo that can be read, changed and tested. | We hand-build cheaper versions of what Unreal ships with. | Chosen |
Cause. The engine is not the gap. The gap was character assets first, because every character was built from boxes
in code. Then animation. Then lighting and effects.
Solution. We stayed on Three.js. Our project rules record the engine decision as final. Chapter 13 puts that answer
under test again.
4. The first named character, and graphics before models
Agent E was the first named character. She carries a giant blade and has no teleports, in the style of Magik from
Marvel Rivals or Vendetta from Overwatch. She has a red ponytail.
We chose to raise the rendering quality before taking on 3D models. The game gained a post-processing stack:
ambient occlusion, bloom, tone mapping and anti-aliasing. It also gained High, Medium and Low presets.
5. The cursor that would not lock
Problem. The pause menu was dead and the crosshair would not move.
Cause. An ordering problem. On Deploy, the game generated all its procedural textures before asking the browser to
capture the mouse. That took seconds. The browser's click permission expired and the capture failed silently.
Solution. Lock first, and pre-warm the textures earlier.
Problem. The browser was stealing keys. Ctrl+D bookmarked the page when the player crouched.
Solution. Every key is swallowed while a match runs.
At this point the repo moved to GitHub, so the work could continue across machines.
6. Patch notes from the start
We set one habit early. Every change gets an entry in docs/PATCH_NOTES.md, the way a shipped game publishes
patch updates. Those entries are meant to become the public patch notes at release. The character select screen
renders that file in-game.
7. The limit of code-built characters
Problem. The figures looked like toy block figures. Our bar is a character in the style of Fortnite.
First attempt. We painted faces onto the heads. It made them worse. The faces looked unsettling, and no face was
better than that face.
Solution for now. The other characters went back to blank faces. Agent E kept hers for one more round. That
round is where chapter 02 starts.
What this gave us
A game that runs and plays correctly, a fixed engine choice, and a ranked list of what actually stood between us and
the look we wanted: character assets, then animation, then lighting and effects.
Generated mesh to rigged game character
September 2026 · v0.06 to v0.14 · Meshy, a rig, and hair bound to her hands.
| What we built | A loader for rigged GLB characters, the first generated character (Agent E) running in game, a height rule for every character, two-bone inverse kinematics for arms and legs, and a hair re- binding tool ( tools/hairbones.mjs). |
| The challenge | A generated model arrives at 2.3 million triangles, as one fused shell. Its clips are padded. Its hair is bound to whatever bone was nearest. |
| Key decisions | Image-to-3D plus a skeleton, not more tuning of procedural faces. Reduce the mesh locally to keep the texture mapping. Hair is a stiff mass at gameplay distance. Free-flowing motion is kept for a cape. |
| The result | Agent E at 245k triangles and 17 MB, rigged and running in the game (v0.10). Real clips take over wherever one exists (v0.12). |
1. The turning point
Problem. After another round on Agent E's painted face, it still did not read as a character.
Cause. It was not a tuning problem. Eyes painted on a sphere with capsule hair will never read as a sculpted
character.
The pipeline we weighed against it was: concept art, then an image-to-3D generator, then Mixamo for the skeleton,
then a GLB file into Three.js.
| Option | What it gives | Trade-off | Verdict |
|---|---|---|---|
| Keep tuning the procedural face | Free | Cannot reach a sculpted look, however long it is tuned. | Rejected |
| Image-to-3D (Meshy) + a Mixamo skeleton | Sculpted, textured, rigged meshes | The generation steps are manual, in an external tool. | Chosen |
Solution. We made the engine load a rigged GLB. We tested it on a free placeholder model, which is still in the
roster as the character "Proto". This is the first tool added to the workflow, and the first of the jumps in speed
described in chapter 13.
2. Getting a usable model out of Meshy
The first model came back white, with no texture. It was in an action pose with the sword fused to the body. We
regenerated it in an A-pose with empty hands.
Problem. The model was 2.3 million triangles. Meshy's limit for rigging is 300k. A higher count is not automatically
better. It has to be reduced without damaging the texture.
| Option | What it gives | Result |
|---|---|---|
| Meshy's own remesh to 30k | Under Meshy's 300k limit for rigging | Smeared the hair texture across her face and chest. |
| Reduce locally (weld + simplify) | Keeps the texture mapping | A first pass at 72k left red spots on the face where hair had collapsed onto skin. A stricter pass at 245k kept the face clean. |
Why our 72k model looked worse than a 30k Overwatch model. An Overwatch model is built by hand,
with every triangle placed on purpose. A generated one is not. Chapter 13 covers this in full.
Result. Agent E was rigged and running in the game at 245k triangles and 17 MB. That build is v0.10, the first with a
3D model.
3. Heights
Problem. Agent E looked tiny next to the other characters.
Our rule. Characters range from 1.55 m to 1.95 m. Each has a true stature that drives the rig, the hitbox and the
camera.
Our first fix was wrong. The old code-built characters drew at about 1.19 times their stated height. We enlarged
Agent E to match them, which put her at 1.96 m in world height.
Solution. Agent E is 1.65 m, as specified. Everyone else was made proportionally smaller.
4. Animation: 5 out of 100
Problem. With a real model on screen, the hand-written animation looked worse, not better. On our own scale, where
80 out of 100 is smooth and realistic animation, the ability animations sat at about 5. They needed a major rework.
Cause. Joint rotations had been guessed axis by axis. Knees and elbows bent the wrong way.
Solution. We rebuilt the arms and legs on proper two-bone inverse kinematics. We wired the loader so that real
animation clips take over wherever one exists. That was v0.12. It was still nowhere near 80.
The walk that reset
| Problem | Cause | Solution |
|---|---|---|
| Agent E took a few natural steps, then the animation reset. | Our first guess was wrong. Measuring found it. Meshy's clips start their keys two frames late and pad the end. Each cycle froze for about five frames and popped. | Fixed once the clip was measured. |
| Every few steps her leg twitched for one frame. | Different bones' tracks ended on different frames. | Fixed on the second attempt. |
5. The hair, first time round
Problem. In Mixamo's preview, Agent E's long hair was stuck to her hands.
Cause. The automatic rig had bound the hair to whatever bone was nearest. We measured the weights.
| Hair weighted to | Share |
|---|---|
| Head | 41% |
| Arms and shoulders | 30% |
| Hips and spine | 21% |
| Hair weighted to | Share |
|---|---|
| Legs | 6% |
What we tried.
| Attempt | Result |
|---|---|
| 1. Find hair by its red pixels, re-bind it to head and spine | A brown strand stayed stuck to her. Not fixed. |
| 2. Hue test plus fill, blended seam | Shards poking into the body at the waist and wrists. |
| 3. Hard cut | Two dots left at the hips. |
| 4. Split mixed triangles, drop scraps, add hair bones with physics | Dots gone, but the hair hung like a drape. |
Stepping back. We stopped iterating and looked at how Fortnite handles hair. We also saw that we had been fixing
something the game did not show. The hair stuck to the arms in Mixamo's preview, and that is not our game
environment.
Solution. Hair is a stiff, bunched mass that barely moves, the way Fortnite does it at gameplay distance. Free-
flowing motion is kept for a cape.
That decision held until Agent C (chapter 12). It is the compromise that chapter 13 takes apart. The hair is stiff because
it is welded to the body, and it cannot move further without tearing.
What this gave us
A real character in the game, a loader that every later character uses, a height rule, and a first understanding of what a
generated model is: one fused shell that has to be repaired before it can move.
Weapon grip in hand and a fake Mixamo rig
September 2026 · v0.15 to v0.16 · Agent S, a grip tuned by numbers, and a rig that was not what its bone names said.
| What we built | Agent S, the second character. A physics cloak from its own model. A greatsword that splits into twin blades. His first combo on real Mixamo clips. DEVELOPER_GUIDE.md. One version scheme for the patch notes. |
| The challenge | Placing a prop in a hand by typing numbers and reviewing screenshots. And a skeleton from Meshy that carried Mixamo's bone names but not Mixamo's bone directions. |
| Key decisions | Art must be authored in Blender, not as numbers in code. Characters are rigged on Mixamo's own skeleton. Props are generated as their own models. |
| The result | Waist twist down from 177 degrees to at most 18. A four-hit combo on real clips. A ranked list of where the visual gap really was. |
1. A second character, and what the generator copies
Agent S's specification: humanity's strongest gladiator, 185 cm, an athletic mesomorph build but not a bodybuilder.
Problem. Meshy's first results were slim and flat-shaded. We are building characters in the style of Overwatch and
Fortnite, not 2D anime characters.
Cause. Meshy copies what it sees. The lesson was about the input picture, not the text description.
Solution. The concept picture itself has to look like a 3D render.
2. The cape: use the right tool
Problem. Meshy kept modelling his cloak folded open. We built the cape in code. It came out as a flat 2D piece that
looked like a cardboard cutout.
Cause. Code was the wrong tool for a sculpted garment.
Solution. We generated the cloak as its own model. The engine gave it a chain of bones and physics.
It still took corrections.
- The clasp side had been read as the front. It is the back.
- The top flared out like wings.
- The collar height took three tries. It sat above the collar and must sit below it.
His kit is rage mode, an air dash, a wall run, and a greatsword that splits into twin blades. The mechanics were the
easy part.
3. The grip
Problem. The hard part was his hand. The wrist twisted the wrong way and the elbow distorted.
What we tried. Two rebuilds of the hand logic. Both were reverted. What was left was one number, the roll of the
right hand, tuned by eye.
| Iteration | Right hand roll |
|---|---|
| 1 | 30 degrees |
| 2 | 60 degrees |
| 3 | The other way |
| 4 | 45 degrees |
| 5 | 55 degrees |
| 6 | 52 degrees |
| 7 | Lower the sword 5 cm |
| 8 | Back to 45 degrees |
It took about twelve iterations in all. Our own measurement of "palm toward body" turned out to be inverted. After
that, the direction was set by eye in the game, not by the measurement.
The twin blades
Problem. In dual-sword mode Agent S held two full greatswords. Each hand should hold one half of the greatsword,
cut down the centre.
Our first cut was wrong. It went through the blade the flat way. That halved its thickness and looked identical
from the camera.
Solution. The second cut went across the profile (tools/splitsword.mjs).
4. A method that does not scale
We stepped back and assessed where we stood.
- Against a proper Overwatch-lite goal, we were about 5% there in appearance.
- It was taking twenty iterations to seat one sword in one character's hand. That does not scale.
The suggested missing step was a bridge between our code tooling and Blender, through Higgsfield.
| Option | What it gives | Trade-off |
|---|---|---|
| Higgsfield + Blender bridge | An official Blender add-on, and a bridge that lets scripts act in the open Blender scene. | Half right. Higgsfield's 3D is largely a wrapper over the tools we already use. It is Meshy underneath. Generations burn plan credits. |
| Stay on Meshy + Mixamo only | Already working. | Every prop keeps being fitted by numbers and screenshots. |
Cause. The missing step was not any one product. Art was being authored as numbers in code and reviewed by
screenshot. That is the wrong tool for art.
Solution. Author in Blender, where a sword is placed by dragging it once. We wrote this into DEVELOPER_GUIDE.md.
Blender joined the workflow in chapter 06.
The guide also ranked where the visual gap really was.
| # | Gap | Ours then | Share of the gap |
|---|---|---|---|
| 1 | Environment art | Untextured boxes, one ramp, flat ground | ~45% |
| 2 | Lighting | One directional light, flat ambient, plain sky | ~20% |
| 3 | VFX | Coloured geometry and simple sprites | ~15% |
| 4 | Animation | Mixamo locomotion plus poses written in code | ~15% |
| 5 | Materials / post | Default PBR, flat colours, bloom only | ~5% |
5. A "Mixamo rig" that was not Mixamo's
Problem. The first real Mixamo animation clip went onto Agent S. The clip played, but his legs looked broken.
| # | Cause | Where | Solution |
|---|---|---|---|
| 1 | A finished clip was never faded out. | Engine | Fixed in the engine. |
| 2 | The old code poses were running on top of the clip. | Engine | Fixed in the engine. |
| 3 | His waist twisted by up to 177 degrees. | Asset | Re-rig on Mixamo's own skeleton. See below. |
What we measured. Agent S's skeleton from Meshy had 28 bones against Mixamo's 65. The bones carried
Mixamo's names, but they pointed in different directions. The arms were 95 and 103 degrees off. A clip recorded on
Mixamo's skeleton cannot play correctly on that.
| Option | What it gives | Trade-off |
|---|---|---|
| Retarget in Blender | Keeps the Meshy rig. | A tool we did not have yet. |
| Option | What it gives | Trade-off |
|---|---|---|
| Rig him on Mixamo itself | The native skeleton, so every Mixamo clip fits. | Mixamo refused the file: "unable to map your skeleton". The file already had one. |
Solution. We wrote a small tool that strips the model down to a bare mesh that Mixamo will accept
(tools/glb2objzip.mjs). We rigged him there.
| Before | After | |
|---|---|---|
| Skeleton | Meshy, 28 bones | Mixamo, 65 bones |
| Worst waist twist | 177 degrees | 18 degrees |
From then on his combo was real clips: three slashes at 42 / 42 / 57 damage and a fourth at 84.
One route we tried and removed. We converted those clips onto Agent E's old Meshy rig. It did not hold up in
game and we removed it. She would have to be re-rigged on Mixamo too (chapter 04).
6. Version numbers
Problem. The patch notes had grown as "v0.1, v0.2, Patch 0.2.1". We wanted one uniform scheme with no confusion.
What we tried. Splitting the notes into several files. We reverted it, because the point was to reduce clutter.
Solution. One file, one scheme. We also checked the early history for padding. The prototype had been listed as nine
versions. We collapsed it to six honest ones, v0.01 to v0.06.
What this gave us
A second character on a skeleton that every Mixamo clip fits, and the diagnosis that shaped the rest of the project:
place art by hand in an art tool, and let the game play it back.
Mixamo clips: fingers, feet, sword flips
September 2026 · v0.17 to v0.18 · Agent E, Agent Zero and Agent B move onto real clips, and the asset library gets a contract.
| What we built | Agent E on Mixamo's skeleton with her own greatsword clips. Agent Zero imported and corrected. Agent B with her own move set. A character asset contract ( tools/herocheck.mjs, 13 rules) and one preparation tool ( tools/heroprep.mjs). |
| The challenge | Fists that would not close, a sword that flipped, a comment that silently disabled a combo, and a run of import defects on every new model. |
| Key decisions | Use the original animation and fix the engine, not the fingers. A character's quirk is an asset or pipeline problem, so fix the file or the tool. Never add a per-character flag in the engine. Organise for fifty characters, not four. |
| The result | 0 sword flips in a test run. Fists sit 0.3 to 4.7 cm from the handle. Every character file passes the same 13 rules. |
1. Open fingers and an upside-down sword
Once Agent S's clips worked, we moved Agent E onto Mixamo's skeleton too, with her own greatsword clips. Her hair
had to be rebuilt again. It took two tries. The first mask swept in tanned skin, which made the top read as short hair
and split the lower half of the long hair away from it.
Problem. Her fingers stayed open around the hilt. The sword sat off her back hand. Sometimes it flipped upside
down.
First approach, wrong. We tried to curl the fingers in code. It took about six rounds and never looked like a
closed hand. The better instinct was to use the original animation as recorded, and ask why it was not arriving.
Cause. The fault was in our own engine code, not the animation. Three.js only writes a bone when its value has
changed. Our rig reset the fingers to their rest pose every frame. The idle clip holds the fists perfectly still. So from the
library's point of view nothing had changed, and the fists were never written.
Solution. We invalidate that cache each frame.
| Problem | Cause | Solution |
|---|---|---|
| Fingers stay open on the hilt | Rig resets fingers to rest each frame. The clip's still fists look unchanged to Three.js, so they are never written. | Invalidate the cache each frame. |
| Sword flips upside down | The blade's direction was decided once and carried between clips. | Vote on the direction every frame, from both fists. |
A test run showed 0 flips. The fists sit 0.3 to 4.7 cm from the handle.
2. One comment, two broken characters
Problem. Agent E's primary fire dropped back to its fallback mode.
Cause. A // comment had been added in the middle of her settings line. Everything after it on that line was silently
switched off: her clip combo, its speed-up, its hit timings. The same mistake later broke Agent Zero.
Solution. Comments go at the end of a line and never in the middle. We wrote it down as a rule because it happened
twice.
3. Twelve rounds on one ability
Critical X is a leap, a spin and a beam. Agent E and Agent Zero share the same clip. His is mirrored. We tuned its feel by
play. Jump height went 1.5 m, then 1.0, 0.5, 0.8 and finally 1.2. Hang time and fall speed changed six times or more.
| Option | What it gives | Verdict |
|---|---|---|
| Fall at 8 m/s² | A gentle landing at 4.0 m/s | Too floaty once the hang was extended |
| Fall at 18 m/s² | Landing at 5.7 m/s | Rejected. The final fall should use the game's own gravity. |
| Normal gravity (24) plus a weightless hover up to the apex | The beam leaves at 1.03 s at the top of a 1.20 m jump | Chosen |
Two measurement errors we corrected. We first measured the blade's sweep through our own grip code and
not the skeleton. That led to a wrong 9% speed-up, which we removed. We also recorded the slash as running to 1.65
s. Watching it showed the slash ends in the air, almost at the moment the beam leaves. 1.65 s was the time slot, not
the motion.
An accident that became a feature. Pressing Space and then E sent the character to 1.88 m, against 1.20 m for
the cast alone. It happens because the cast raises upward speed to a minimum and does not replace it. It was already
useful in play, so we kept it and logged it as an official mechanic. It is in the v0.17 notes as leap stacking.
4. Agent Zero: twelve generations, then the feet
Agent Zero's look took twelve generations on Meshy. The versions with the correct look came out with short legs. We
tried stretching his legs in the file. At 48% the legs were too long in preview, and we reverted it. Then the import
problems came one after another.
| Problem | Cause | Solution |
|---|---|---|
| He hovered 0.9 m above the floor on the character select screen | Meshy exports have their origin at the centre of the body. Agent Zero had skipped the step that moves it to the feet. | Our first fix was a per-character flag. It was wrong twice. We replaced it with a tool that fixes the file. |
| His hands had no pinky fingers (24 finger bones, not 30) | Our first two diagnoses were wrong. The real cause: the glove was sculpted as fused fingers. | Diagnosed as a property of the source model, not the rig. |
| His heels were up | A clip carries the foot angle of the skeleton it was recorded on. His was 12.9 degrees off. | Our first fix only dropped his feet lower. It did not flatten the toes. The second corrects the ankle angle, measured per character and applied by the engine with no per- character data. |
5. A clean library, built for fifty characters
After Agent Zero we reorganised the project for fifty characters and not four. Our bar was to patch in the cleanest way
possible. We planned it in phases and approved them one at a time.
| Phase | What it does | Cost / risk |
|---|---|---|
| 1. Clip names. 2. An asset contract and tools | Only adds things | Low |
| 3. Per-character folders | Moves 46 files into assets/heroes/<id>/ | Touches working paths |
| 4. Trim the docs | The handoff from 336 to 203 lines | Needs review |
| 5. Orphan files | 14 files, 299 MB | Parked, not deleted |
The result is a contract a character file must pass (tools/herocheck.mjs, 13 rules) and one tool that prepares a
model for Mixamo and brings it back (tools/heroprep.mjs).
The standing rule. A character's quirk is an asset or pipeline problem. Fix the file or the tool. Never add a per-
character flag in the engine plus a paragraph explaining it.
6. Agent B is not Agent E
Problem. Agent B was a placeholder on Agent E's body. We copied too much and gave her Agent E's whole move
set, where only the body was meant to be shared.
Solution. She is a female swordsman, but she is her own character. We restored her own walk, jump and abilities.
Only the three-hit combo is shared.
Rule. Character changes stay on the character they were made for.
What this gave us
Three characters running on real clips with closed fists and a stable sword. A measured, engine-level foot correction
that needs no per-character data. And a library where a new character is checked by a tool against 13 rules, not
explained by a paragraph.
Lag fixes, and NPCs built on player code
September 2026 · v0.19 to v0.30 · Lag, invisible walls, knockback, and the decision that an NPC is the character code.
| What we built | Versus mode with up to five NPCs a side. A pooled light system. Capsule hitboxes. Bot extends . A post-match scoreboard with assists. One knockback path, Player.shove. |
| The challenge | Multi-second freezes, 48 colliders with nothing drawn, NPCs that played by their own rules, and a shove that behaved differently on the ground and in the air. |
| Key decisions | Measure slow frames before guessing. Bullets test capsules, not rigged bodies. If a player cannot do it, an NPC cannot either. Difficulty changes reaction, never damage. |
| The result | A health-pack pickup went from up to 4,689 ms to 17 to 25 ms. NPCs read the same tables as the player. Ground and air knockback match 1:1. |
1. One default speed for everyone
We wanted melee characters to outpace gunners. The default walk dropped from 6.2 to 4.0 m/s, and melee characters
kept 6.2 by name. Bots had their own speeds per difficulty. We removed them. Bots scale down to the defaults and
have no special speeds. That change is the seed of the biggest architectural decision in the project.
2. Lag, cause one: lights
Problem. Versus mode arrived in v0.19, with up to five NPCs a side. With it came freezes, worst when shooters were
in the match.
What we measured. We added a slow-frame logger to find where the time went. It found two separate causes.
Cause. Every muzzle flash, health-pack glow and projectile added its own light to the scene. In Three.js the number
of lights is part of every material's shader. Each time the count changed, every material was rebuilt.
Solution. A fixed pool of lights that effects borrow from. The light count never changes.
| Picking up a health pack | Frame time |
|---|---|
| Before, three measurements | 2,637 ms, 2,154 ms, 4,689 ms |
| After, with pooled lights | 17 to 25 ms |
3. Lag, cause two: bullets against bodies
What we measured. Of 260 slow frames, 256 were game logic, not drawing. The cost was in the hit test.
| One bullet ray tested against | Cost |
|---|---|
| The map | 0.02 ms |
| One bullet ray tested against | Cost |
|---|---|
| A capsule | 0.09 ms |
| Four rigged character bodies | 3.6 ms |
Cause. Bullets were raycast against each character's rigged body. No shipping game does that.
Solution. Every character now carries an invisible capsule and a head sphere. Bullets test those.
4. Forty-eight walls made of air
Problem. Characters ran into obstacles that were not there. The map had invisible walls.
Cause. It had been in the game since the first build. The decoration groups were being added to themselves instead
of to the world. Every pillar, barrel, lamp, tree and sign was built, given a collider, and never drawn. Forty-eight colliders
with nothing to see.
Solution. We attached the groups to the world. That also let us merge 650 small meshes into a handful (v0.20).
With the props finally visible, we judged the map honestly. The target was a map like Overwatch. What we had looked
like Minecraft. The Practice map went bare again, and Central Park was rebuilt as a plaza.
5. The decision: an NPC is the character code
Problem. The NPCs kept cheating in small ways. They moved at their own speeds and swung at their own rate. NPC
Agent E had grown a leap ability no player had. And Agent Zero, with 500 HP including armour, died in a few bullets.
Cause. An audit confirmed it. NPC bullets left the eye horizontally at about 1.6 m. Against a 1.80 m character, every
shot landed in the head zone. Four bullets at 39 each was 156 damage.
Solution. We went further than a patch and made the rule structural.
| Before (v0.22) | After (v0.23) |
|---|---|
| A bot is its own class with its own mover, swing timer and damage table | Bot extends Player: the NPC is the character's body. Its brain only fills in a fake keyboard and mouse. |
| Difficulty changes damage, speed and hit points | Difficulty is only reaction time, burst length and pause. Damage, attack rate, cooldowns and speed come from the same tables the player reads. |
The rule in our engineering rules reads: if a player cannot do it, an NPC cannot either. We made it for fairness, since
this is meant to be a PvP game one day. It also turned out to matter for networking (chapter 13). A remote player is the
same body driven by a third kind of keyboard.
The same work gave the project its vocabulary rule. A "jump attack" is Space plus left click, whatever animation plays.
We had been naming moves after their clips. Moves are now named by their input.
6. Scoreboard and assists
v0.24 added the post-match table: kills, deaths, assists, damage, healing, mitigation. We wrote our own assist rules.
| An assist is credited for | Window |
|---|---|
| 50 damage | Within 8 seconds |
| Crowd control that ended | Within 3 seconds |
| A shield or heal | Within 5 seconds |
We also took a cost lesson here. A simple question, how other games count assists, deserves a simple search and not
a deep research pass.
7. Immortal Fury: sixteen passes on one ability
Problem. Agent S's spin was supposed to drag enemies in and finish with a cut. Between v0.26.2 and v0.28.4 it was
a shove, then a vacuum, then a vortex, then a radial pull with a sideways swirl. The finisher's timing alone took eight
releases (v0.27.7 to v0.27.14). It played choppy, and it felt like two abilities and not one connected move set.
Solution. Two playback rates. Slow into the hit and fast out of it.
In the middle of the tuning, a change to his dash and pull strength went in without sign-off. We laid out the options
afterwards.
| Option | What it gives | Trade-off / verdict |
|---|---|---|
| Revert to the approved state (dash 9 m/s through the spin, pull 5) | The signed-off behaviour | The pull stays weak |
| Keep the unapproved change (dash stops early, pull 7) | A stronger pull | Takes away his movement |
| Dash untouched, pull 8, hits limited to a 2.2 m height band | Both the pull and the movement | Chosen. Shipped as v0.27.4 and frozen. |
The same failure later, with a clip instead of a number, produced Rule Zero (chapter 06).
8. Knockback on the ground and in the air
Problem. A shove that sent an airborne enemy 2 to 3 m moved a grounded one about 0.35 m.
Cause. Ground friction (16 per second) ate the push in roughly 0.05 s.
Requirement. Ground and air should match.
| Option | What it gives | Trade-off / verdict |
|---|---|---|
| A knockback state: for 0.15 s a shoved character ignores friction and their own keys | A grounded shove travels | Shipped as v0.28. The air still carried further. |
| One global juggle number | One line of code, ground untouched | Our first recommendation. Not chosen. |
| Air drag that only applies while knocked, and every push re-tuned | Ground and air match 1:1, dashes untouched | Chosen. Many numbers to retune (the basic push went from 6 to 13). |
There is one door for all knockback now, Player.shove. Immortal Fury was deliberately left out of the drag. A more
rugged feel suits it better.
What this gave us
Freezes traced to two measured causes and removed, a map where every collider can be seen, and NPCs that are the
character code with a different input source. That last decision later carried straight into networking.
Art tools in the pipeline
- 06Blender hand sockets, Git LFS, review rules
- 07Clone abilities, aim offset, hand-keyed draw
- 08Reaching 120 fps and measuring frames right
- 09Map pipeline: greybox, then a modelled kit
- 10High-to-low baking and local AI on 6 GB
Blender hand sockets, Git LFS, review rules
September 2026 · v0.32 to v0.32.26 · A grip placed by hand, Blender in the pipeline, asset storage, a new character, and our
first rule.
| What we built | A Blender socket workflow for weapon grips. Blender, scripted, plus Higgsfield for generation. Git LFS for game assets. tools/kitcheck.mjs. Agent L, a striker with a sheathable nodachi, and his full kit. |
| The challenge | A grip that numbers could not solve. Features that grew past their scope. A working Mixamo clip that was replaced when it only needed slowing down. |
| Key decisions | Art is decided in a viewport, not in code. Keep the repo and turn LFS on from here. Descriptions are checked against ability data by a tool. Rule Zero: diagnose, stop and report before any fix. |
| The result | The game matches the Blender socket to three decimals in idle, walk and run. Agent L shipped at 41,851 triangles with a complete kit and a timing reference in docs/ABILITY_TIMING.md. |
1. The grip, solved by hand
Problem. After twenty iterations to seat one sword in a hand, Agent S's grip was still not right. The fix our developer
guide had named, a socket placed in Blender, had never been built.
Solution. We built it, starting with Agent S. A script sets up the scene: Agent S at his real height, the sword attached
to the hand bone, and a panel with a Save button. The sword is then dragged into the hand directly.
The first attempts were rough. An undo removed the whole scene and left a box in its place. One placement was lost
when the window was reopened before saving. Then the sword was placed by hand and saved.
Result. The game read the saved socket and matched Blender to three decimals in idle, walk and run. That was
v0.32. Direct manipulation did what placing a prop by numbers had not. It is the second big jump in speed for the
project: art decided in a viewport, by eye.
2. Two bridges into Blender
Next we wanted Blender itself scripted live, and generation available inside it.
| Option | What it gives | Cost / risk |
|---|---|---|
| BlenderMCP (free) | Scripted work, live, in the open Blender scene | A server and an add-on, both needed |
| Higgsfield MCP + its Blender add-on | Generation of pictures and 3D inside Blender | Needs credits. The trial charges automatically after three days. |
We installed both. They wanted the same network port, so we moved one. Higgsfield's sign-in took three attempts.
With no credits yet it could do nothing, so it waited until chapter 09.
3. Where assets live
Problem. Models and textures were in plain git. Standard practice is to keep game assets, textures, 3D models,
audio, video and datasets in Git LFS. We had not set it up from the start.
| Option | What it gives | Trade-off / verdict |
|---|---|---|
| A fresh repo now | A clean start and a smaller clone (about 285 MB against 545 MB) | Loses every commit and the work log |
| Rewrite history to move old files into LFS | A smaller repo with history kept | Costly, and could break things |
| Keep the repo, turn LFS on from here, start a fresh repo at v1.0.0 | Nothing lost. The old repo becomes the archive. | Chosen |
The rule since. Code and docs in git. Game-ready models and textures through LFS. Raw downloads and Blender
project files never in git. A work log tool written alongside counted 248 commits.
4. Things we built and then removed
Not everything stayed. Three things were undone. Each is a lesson about scope.
| What | What went wrong | What we did |
|---|---|---|
| Agent S's free arm | His left arm hung like a lazy arm. About six rounds of tuning in code produced a stiff swing and a bug that made his sword flail at a sprint. | A line-for-line revert, done twice. The files were verified identical to the old commit. Motion like that belongs in an animation tool, not in code. |
| A decimation tool for Agent L | We needed a lighter model and started building a head-aware reducer. It was too much time on a small feature. | Cleaned up and undone. A remesh on Meshy did the job in two minutes. |
| Leftover lifesteal | Agent S was healing about 2,000 a fight from a line written in v0.03, when Agent E was the only melee character. Lifesteal is a rare ability and not meant to be on many characters. | Removed. An audit found nine character-select descriptions that did not match the code. We made it a hard rule: tools/kitcheck.mjs now compares every description with the ability's data. |
5. Agent L
Agent L is a striker with a long nodachi. Our requirement from the start: the blade must be sheathable, and his default
position is the blade sheathed.
| Option | What it gives | Trade-off / verdict |
|---|---|---|
| Three models: sword, empty sheath, sheathed sword | A simple visual swap | Three generations that may not match each other |
| Two models: the nodachi and an empty scabbard. "Sheathed" is the sword placed in the scabbard in Blender. | One sword reused, fittings always match | Needs Blender placement. Chosen. |
Generating his body taught us to change only the named part of a generation description. One reworded version made
him look like a sailor. Another rewrite dropped the blue coat, which was part of the design. We now edit only the part
that needs to change and leave the rest of the wording alone.
6. The incident behind Rule Zero
Agent L came back from Mixamo rigged, at 41,851 triangles, and went into the game on a temporary kit. His sword arm
looked locked and his jump looked wrong.
Problem one, the arm. He was a placeholder on Agent S's settings, which lock the arm. That was the cause, and it
was the one we suspected.
Problem two, the jump. Our first diagnosis was wrong. We judged the Jumping Up clip from Mixamo unusable,
replaced it with a jump made in code, and committed both changes together. Measured again, the clip was complete.
The feet tuck through its whole length. It only needed to play slower.
Solution. We restored the clip and slowed it down. Then we stopped and wrote the rule that now sits at the top of
our engineering rules.
Rule Zero. Do not fix anything until the fix has been decided. Diagnose, then stop and report. The decision on what
happens next comes after the report.
A question is a question, not a work order. The cause of a bug is reported with options, and the fix waits for a yes.
7. His kit
With that settled the kit went quickly.
| Ability | What it does |
|---|---|
| Air Leap | Twice the force of a jump (17.6 m/s) |
| Dash | Double-tap |
| Lightning Pierce | A travelling strike, capped by distance |
| Giga Slash | Three cuts that send blue waves |
The tuning was the slow part. Giga Slash's hang time took eight rounds. A version that halved it was too exact. He
dropped too fast and the animation looked rushed, so we reverted it. We kept the timing tables, which is why exists.
docs/ABILITY_TIMING.md
Problem. From a high jump, Pierce carried Agent L across a small map.
Solution. The fix caps the travel by distance and brakes on exit. It behaves the same on the ground and in the air.
We did it properly because the design has to survive an eventual migration to Unity or Unreal.
What this gave us
A grip workflow that takes minutes in a viewport. A storage rule that keeps the repository lean. A tool that stops
descriptions drifting from data. A fourth character. And Rule Zero, which has shaped every fix since.
Clone abilities, aim offset, hand-keyed draw
September 2026 · v0.32.45 to v0.33.12 · Agent L's clones, the third-person camera, and a sword draw authored frame by
frame.
| What we built | Two clone abilities for Agent L: Shadow Mirage on Q and Phantom Mirror on F. A camera turned toward the character so strikes meet the crosshair. A scabbard and nodachi made on Higgsfield. The project's first hand-authored animation, the sword draw. |
| The challenge | A clone that dealt no damage. Swings that landed beside the crosshair. Clips recorded in a T-pose on a body that rests in an A-pose. A sheath that broke abilities on Olympus. |
| Key decisions | A clone is an ability, not a character. One camera angle for all characters, capped at 12 degrees. Price a generation job before submitting it. Move the prop, not the body. |
| The result | Every checked bone agrees to 0.0 degrees after rest-pose matching. The draw cut lands 0.23 s after the click, and the draw itself is his first hit. |
1. A clone that did no damage
Problem. Agent L's Q made a copy of him that repeats his moves. In the Practice Range, Giga Slash dealt 240
damage with the clone and 240 without it, even though six waves flew instead of three. The mirage was there but it
was not dealing the doubled damage.
We followed Rule Zero. No code changed until the design was approved.
| Option | What it gives | Cost / risk |
|---|---|---|
| A. A clone that stays at Agent L's right and attacks whatever he aims at | Double damage on everything. Enemies see two of him. | It must reposition when he aims across it |
| B. A clone that behaves like an easy NPC | Simple | Does not hit what he aims at |
| Mirror clone (opposite left/right, same forward/back) | Placement becomes a skill | Obstacles and distance limits can strand it |
Decision. We chose A, and corrected the design underneath it. Our first build made the clone a separate character
with its own ability charges. That was wrong. Clones are not characters. They are an ability, derived from Agent L. A
clone is a mimic with no ID, not a twin.
We could already see where the idea ran out, so we did not argue the mirror version on paper. We built it as a second
ability on F so both could be tested side by side. Q is Shadow Mirage. F is Phantom Mirror, a lighter body that can be
hit but has no brain of its own.
2. Swings that land beside the crosshair
Problem. We put a Marvel Rivals screenshot next to ours. Our character stood far to the left of the crosshair: 351
pixels, 18% of the screen width. Many sword hits looked like they did not land where they should. It felt mildly off.
| Option | What it gives | Trade-off / verdict |
|---|---|---|
| Turn the camera a few degrees toward the character (one angle for all) | The character's strikes meet the crosshair | Chosen. Past 12 degrees the walk looks like a crab-walk. |
| Rotate each character model | Per-character control | Characters share animations. Advised against. |
| Set the camera height from each character's real head height | Every character framed alike | Built, then rejected. It made Agent L look tall and Agent S tiny. |
It took eight versions (v0.32.51 to v0.32.58) to settle.
| Setting | Values tried | Final |
|---|---|---|
| Shoulder offset | 0.9 m, 0.55 m, 0.7 m | 0.7 m |
| Camera turn | 15.6 degrees, 12 degrees | 12 degrees |
Two rules we kept. The turn never goes past 12 degrees, or the walk reads as a crab-walk. And the lower the head
sits from the crosshair, the smaller the character feels. We treat that as law for framing.
A side effect we had not tested for. Both clones aimed from Agent L's eye, which no longer pointed where the
camera did. They stood at the old angle while Agent L stood at the new one. Their aim now starts at the camera.
3. Credits and approvals
We bought 1,000 Higgsfield credits to make Agent L's scabbard and nodachi.
Problem. The scabbard job was priced at 25 credits and submitted in the same step.
Rule. Show the job and the cost, and wait for a yes. Pricing and submitting are separate steps.
The nodachi needed two generations. The first came out with a scimitar's curve. We then narrowed the blade and
guard when the right first move was to scale the whole sword down without touching its proportions. For the second
generation we priced the options first.
| Option | What it gives | Cost |
|---|---|---|
| 1. Meshy 7 image-to-3D, Ultra | Best geometry | 44 credits |
| 2. Meshy 7 standard | Same model, slightly less detail | 38 credits. Chosen. |
| 3. Hunyuan 3D v3 | From the image only, no text description | 15 credits |
| 4. Tripo H3.1 | From the image only, no text description | 9 credits |
Fitting the hilt into the sheath took many small corrections in Blender. The pattern of them is the lesson. Unrequested
changes made it worse: too much cut off the collar, a ring flattened until it looked broken and wrinkled, parts scaled
that were not in scope, such as the grip. In art tuning, one person leads by eye. Each step is one named change, then a
check.
4. T-pose clips on an A-pose body
Problem. A clip copied straight across put Agent L's hands inside his body.
First diagnosis, wrong. We first assumed the wrong Blender window was being viewed. It was the right window.
The transfer was what was wrong.
Cause. Mixamo records its clips on a skeleton standing with arms straight out. Agent L rests with his arms down at an
angle. The difference at the arms is 53 degrees.
Solution. Match the rest poses before transferring. Once matched, every checked bone agreed to 0.0 degrees. The
same correction went into the game's loader. The same trap caught Agent J's clips later (chapter 11).
5. Drawing the sword
The draw is the first animation in the project authored by hand, frame by frame. We measured three Mixamo clips for
how close the palm comes to the grip.
Our principles. Move the prop, not the body. The first thing to try in any instance is to reposition the sheath. And
the sheath should swing, because it does in real life: it goes up violently as a result of momentum.
| Option | What it gives | Cost / risk |
|---|---|---|
| Move the hands to the sheath | Simple | The arm motion was already right for a nodachi |
| Move the sheath to the hand: swing it from the hip to behind the back by frame 11 | Physically plausible | Many keyframes to tune |
| Option | What it gives | Cost / risk |
|---|---|---|
| Use the second clip, which grabs lower | A closer grab by the numbers | Needs a longer swing |
Decision. Use V1, the first clip. Then came the per-frame work.
- The wrist bend at frame 11 went from the clip's 22 degrees up to 62.8 and back to 58. A later note took a wrist
bend from 15 degrees back to 10. - The sheath's resting spot was raised twice.
- Three frames were pushed out by an inch or half an inch.
The intent for each frame is written in our draw notes file, so later work does not have to guess.
| Draw playback in game | Verdict |
|---|---|
| Full length, 1.3 seconds | First version |
| Three times speed | Too fast |
| Twice speed | Chosen. The cut lands 0.23 s after the click. |
The draw itself is his first hit.
6. Bugs the sheath caused
On Olympus, Agent L's abilities stopped working after the sheath shipped. We suspected the new code. That was right
for one of two causes.
| Problem | Cause | Solution |
|---|---|---|
| The Q clone could not be placed while sheathed | While sheathed, the code blocked the whole left-click path. That threw away the click that places the clone. | Fixed in the sheath code, where the click was being dropped. |
| The Q clone could be placed over empty air | Olympus is a map of floating islands. In the test the clone sat 5.6 m below his floor. | The cast is refused with a red ring and no cooldown when there is no floor. |
| Swapping places with the F clone killed Agent L | The clone could walk off a cliff. It had no fall-death check and kept falling until its timer ran out. | One line added the check (v0.33.12). |
What this gave us
A clear model for clones as abilities. A camera rule that makes melee read correctly for every character. A rest-pose
correction built into the loader. And a working method for hand-authored animation, with the intent of each frame
written down.
Reaching 120 fps and measuring frames right
September 2026 · v0.33.4 to v0.35.2 · A frame-rate target, a frame counter that over-reported, and one repeatable benchmark.
| What we built | Pooled clones, bounding-volume trees on every world mesh, baked shadows on Medium and Low, an FPS overlay that measures finished GPU frames, a warm-up loading screen, and npm run . |
| The challenge | Five-a-side matches lagged on a greybox map with no art in it. Real 3D art was still to come. |
| Key decisions | Measure before changing anything. Build nothing mid-match. Count finished frames, not loop ticks. Compare every art step against the last release with one benchmark. |
| The result | Low went from 29 fps to 116–137 fps. High went from 14 fps to about 55–60 fps. The worst frame during a Agent L cast went from up to 10 s to 16–74 ms. |
1. The requirement
Problem. Five-a-side matches lagged, even on a greybox map. If an empty map could not hold a frame rate, a fully
modelled and textured one had no chance.
Our bar. 120 fps as the target and 80 to 100 fps as the floor, on a mid-range PC (Ryzen 7 3700X, RTX 2060), at Low
and Medium. Below that, a shooter stops feeling like a shooter.
2. Measure first
We changed nothing until it was measured. The game had no frame counter, so we added one before anything else.
| What we measured | Finding |
|---|---|
| Memory | Not the problem. |
| CPU | Most of the cost sat on one CPU core. |
| GPU | Also over budget. |
| Agent L | A real outlier. His clone was rebuilt from scratch on every cast, with single frames as long as 10 seconds. |
Solution. v0.33.4 shipped four changes.
- Clones are built once during loading and pooled.
- Every world mesh got a bounding-volume tree, so ray tests never walk triangles.
- On Medium and Low the map's shadows are baked once at load and characters get blob shadows. Only High
redraws shadows live. - An FPS overlay and a frame limit setting.
| 5 v 5 on Olympus V1, 1920×1080, a Agent L on each side | Before | After v0.33.4 |
|---|---|---|
| Low | 29 fps | 116–137 fps |
| Medium | n/a | about 100 fps |
| High | 14 fps | about 55–60 fps |
| 5 v 5 on Olympus V1, 1920×1080, a Agent L on each side | Before | After v0.33.4 |
|---|---|---|
| Worst frame while Agent L casts | up to 10 s | 16–74 ms |
That is roughly a 5x improvement in play.
3. Shader stutter at match start
Problem. The first seconds of a match still stuttered.
Cause. Shaders were compiling the first time each effect appeared on screen.
Solution. The loading screen now stays up for at least four seconds and runs a short hidden fight to warm
everything. Our rule: up to ten seconds of loading is acceptable if nothing is built mid-match.
4. The meter that lied
Problem. On High, the in-game meter showed 80 to 100+ fps. The experience on screen was closer to 10 fps.
Cause. The meter was counting how often the game loop asked for a frame. With vsync and the frame cap removed
in v0.33.4, the loop kept ticking while the graphics card fell behind. Timed directly, the meter read 60 while the real
rate was 42. High had also been rendering at up to twice the screen's pixels on high-DPI displays.
Solution. The meter now measures finished frames on the GPU. We then listed the costs on High as numbered items
and re-cut the presets from that list. Items 1, 2 and 4 became a new Ultra preset. Items 1 to 5 became High. Medium
and Low were left alone, since they are the presets our target is set on.
5. One honest benchmark
Problem. The first set of real surface textures on Olympus cost about 20% of the frame rate at once. Nothing had
compared that step against the previous build.
Solution. One command, npm run bench (tools/bench.cjs). It runs the same scene from the same camera at
2560×1440, times the GPU, then runs a ten-second bot fight. Its first numbers were High 72 fps (limited by the GPU)
and Medium 137 fps. Every art step is now compared against the last release with it.
The texture cost was brought back from about 20% to about 6% by merging pieces and dropping normal maps where
nobody would see them.
6. The performance budget
| What costs frames here | The rule we follow |
|---|---|
| Draw calls: every visible object is drawn once per pass | Static map art is merged per material at load. Repeated props are instanced. |
| Ray tests against detailed meshes | Bullets test capsules. The world uses bounding-volume trees. NPC sight uses boxes. |
| Live shadows | Baked once on Medium and Low. |
| Lights: every point light is paid for by every lit pixel, and changing the count recompiles every material | Effects borrow from a fixed pool. Nothing owns a light. |
| Anything built or compiled during a match | The loading screen builds, compiles and draws everything once. |
What this gave us
The game's own code runs on one CPU core. So the budget is draw calls and script time per frame, not triangle counts
or memory. That is the opposite of what one would guess, and it is why a dense map is acceptable as long as it is
merged. We also have a meter we can trust and a benchmark that catches a regression at the step that caused it.
Map pipeline: greybox, then a modelled kit
September 2026 · v0.33 to v0.35.3 · The first map built to look like a place, and what its art pipeline taught us.
| What we built | Olympus V1 and V2 as playable maps, an art strategy in docs/MAPS.md, a seamless painted sky, and docs/LESSONS.md. |
| The challenge | Generation credits are money. We spent about 250 of them on pieces that Blender could have modelled for 0. |
| Key decisions | Layout before art. Lock the style before any 3D. Build piece by piece, not one arena model. Meshy 7 for anything seen close. Model architecture in Blender and generate only organic and showcase pieces. |
| The result | A corrected pipeline, a 0-credit plinth as proof, and a written list of mistakes so each one is made once. |
1. Layout first, because credits are money
Problem. Olympus was the first map meant to look like a place and not a test room. The first estimate for its art was
about 390 credits for a 22-piece kit. An art budget of 390 to 450 credits is roughly half of a $50 plan.
Solution. We built the layout in code first, the same way as the earlier maps, and generated nothing. Olympus
shipped as a playable greybox in v0.33. The layout could be judged before any art was paid for.
Problem. On the first try, NPCs walked off the edges and froze at the jumps.
Solution. We fixed navigation until none fell in a 60-second run.
A 1.5x version followed, with a Stairway to Heaven and a Garden of Eden. We kept both. The original is Olympus V1 and
the larger one is Olympus V2. Both are playable, for A/B testing.
2. What must come before any 3D asset
Before any art, we settled the non-negotiables. They became the art strategy in docs/MAPS.md, written down as part
of the master plan and the map plan.
| Rule | What it means |
|---|---|
| Lock the style first | No 3D work until the look is fixed. |
| Kit pieces | Statues, plinths, columns, stair modules. They do not depend on the layout, so they can be made early. |
| Set pieces | They must wait until their area stops changing. |
For the style lock we made two concept pictures, one per image model, for 8.5 credits together. Concept A won
clearly. Its sky was the deciding element.
3. Why not the whole arena as one 3D model?
| Option | What it gives | Trade-off | Verdict |
|---|---|---|---|
| One image-to-3D of the whole arena | One object, about 30k triangles, one texture | Mush up close, wrong scale. It would judge concept A unfairly. | Rejected |
| Arena kit piece by piece (floor, dais, balustrade, plinths in Blender; islands generated) | An arena with characters visible in it | About 30 credits for step 1, without statues or throne | Chosen |
Which generator to use was settled by looking at the result. The first temple island from Hunyuan3D (15 credits) came
out waxy and rounded. So Meshy 7 (38 credits) became the rule for anything seen close, and Hunyuan3D is used for
far background only.
4. Every reference picture is reviewed before it goes to 3D
Problem. One island was submitted to 3D on an earlier approval, without a review of the picture itself. A 3D job costs
credits whether or not the picture was right.
Solution. A fixed gate. No 3D job is submitted until its reference picture has been looked at and approved for that
job. The gate works: one distant island was rejected four times in a row (white, white, too brown, too grey) and never
went to 3D.
To make review easy, we reorganised the Blender scene into four tabs: the playground, pending references, other
references, and modelling. Every finished model goes straight into the playground.
5. The sky, piece by piece
Problem. The painted sky had a hard bottom edge. The cut-off broke the immersion of the scene.
Fixing it took several passes. Each one exposed the next fault.
| Fault | Cause |
|---|---|
| Fog read orange, not golden hour | The fog colour was off. |
| Two suns | Half a panorama had been repeated round the circle. |
| Light from the wrong direction | The scene light did not come from the painted sun. |
| A blue arc overhead | The old sky dome showed through. |
| Cloud colour wrong three times | It was matched on the source picture, not on the rendered pixels after tone mapping. |
Solution. Match colours on the rendered output. The cloud colour ended at #f7ca88 against the picture's horizon at .
#fbcc8c
6. The finding: we had been using generation for too much
Problem. We looked into how the popular "whole scene from one text description" Blender demos are made. The
answer was uncomfortable. No whole-scene generator exists. Those scenes are scripted Blender Python for the
architecture, plus PBR materials, plus an HDRI sky. AI 3D is used only for props. Higgsfield's own free Blender
instructions already said so: blocking is 0 credits, generation is for showcase props only.
Cause. We had not read those instructions before spending.
| What we did | What it cost | What we should have done |
|---|---|---|
| Every piece: picture → image-to-3D → placed in the game | About 250 credits by the later count (Zeus, Athena, throne and the green island alone were 152) | Model architecture and simple props in Blender for 0 credits. Generate only organic and showcase pieces. |
Solution. We proved the new route at once with a plinth modelled in Blender: 3.5k triangles, a gold Greek key on a
marble band, 155 KB, 0 credits.
We also decided to document every mistake, so we grow from each one. That file is docs/LESSONS.md.
7. Smaller lessons from the same work
| Problem | Cause | Solution |
|---|---|---|
| The lawn | The generated green islands had gaps in their leafy tops. We covered them with a flat lawn. The lawn ruined the look. | A grass texture matched to the model's own leaf colour ( #738e3d against #768f3e). Rule: a surface players stand on is solid and readable, and a patch uses the model's own colours. |
| The frozen bake | A lighting bake run in the live Blender window froze it for more than 15 minutes. It had to be killed, and unsaved work was lost. | The same bake took 8.5 seconds in a headless Blender. Heavy jobs now always run headless. |
| The playdough shield | Statues were halved from 31k to 15k triangles with JPEG normal maps. They looked soft up close. | Showcase pieces are now baked from high to low detail and keep lossless normal maps (chapter 10). |
8. Reviewing the whole pipeline
We then reviewed the whole body of map work end to end, looking for where it went wrong and how to build more
efficiently. The finding was the one above: credits were spent before the free instructions were read.
| Option | Trade-off | Verdict |
|---|---|---|
| Downloadable asset packs | We reviewed them. They do not match the level we are aiming for, which is Overwatch level. | Rejected |
| Build our own | More work, at the quality bar we set. | Chosen |
The credit balance tells the story in one line. It went from 912 to 297.5 across this stretch of work.
What this gave us
Two playable versions of Olympus, a style lock, and a pipeline with the costs in the right places. Architecture and
simple props are modelled in Blender for 0 credits. Generation is reserved for organic and showcase pieces, and only
after the reference picture is approved. Every mistake on the way is written in docs/LESSONS.md.
High-to-low baking and local AI on 6 GB
October 2026 · v0.35.5 to v0.36 · Statues that survive a close look, and generation that costs no credits.
| What we built | Eight god statues on Olympus V2, a high-to-low bake pipeline ( tools/blender/hero_bake.py), and a local ComfyUI setup with SDXL, ControlNet and Hunyuan3D. |
| The challenge | A 306k-triangle statue has to run in a game at about 20k without losing its detail. Generation credits were the constraint on everything. |
| Key decisions | Bake, never just decimate. Statues face the altar. Records show the final approved design only. ComfyUI over Automatic1111, because it can be scripted. |
| The result | v0.36: all eight gods stand on Olympus V2. Picture edits, texture close-ups and image-to-3D now run locally for 0 credits, within the limits of a 6 GB card. |
1. Decimate is not a pipeline
Problem. Olympus V2 needed its gods on their plinths. Ares came first. Before spending, we weighed two routes:
generate at high resolution and scale down, or generate his sword and shield separately. Ares came back from Meshy
7 (ultra, 44 credits) at 306k triangles. Straight decimation to 15k shattered his blade into shards.
Cause. Decimating the raw generated mesh throws away fine geometry with nothing to carry the detail. It is the same
shortcut that had produced the "playdough" shield on Zeus and Athena.
| Option | Result on Ares | Verdict |
|---|---|---|
| Straight decimate, 306k → 15k | Blade broke into shards. Fine detail gone. | Dropped |
| Bake: watertight remesh, decimate the clean copy, new UVs, bake colour / normal / metal from the high model at 2048, normal map as PNG | About 20k triangles, 7 MB, detail carried by the maps | The rule for every showcase piece since ( tools/blender/hero_bake.py) |
Solution. We re-baked Athena through the new route and left Zeus alone, so the two could be compared side by
side. Then we re-baked Zeus too.
2. Facing the right way
Problem. Ares was not looking at the colosseum. His facing did not match the side his nameplate was on.
Solution. Every statue now faces the plinth side that looks at the altar.
Poseidon, Hades, Artemis, Apollo and Hermes followed. Each reference picture was revised until approved before it
went to 3D. Each model cost 44 credits. v0.36 is the release where all eight gods stand on Olympus V2.
3. Records show the final design only
Getting a design right takes many pictures. That iteration is internal. Once a design is ready, it is the design, and we
stick with it. Documents, patch notes and commits record the final approved design only. A local commit and tag that
said more were removed and redone before the push. This journal follows the same rule.
4. A wrong assumption about credits
We had assumed that a job rejected by the generator still costs credits. We checked the balances. A rejected job costs
nothing.
5. The first local generator
Problem. Credits were the constraint on all of the map art. The next tool had to cost none. It also had to be
scriptable end to end, with no manual steps in a tool's interface.
| Option | What it gives | Trade-off | Verdict |
|---|---|---|---|
| ComfyUI | A local API we can script directly | Setup on a 6 GB card | Chosen |
| Automatic1111 | The older, more familiar interface | Slower on 6 GB and harder to automate | Rejected |
Solution. ComfyUI went onto the RTX 2060 with an SDXL model and a ControlNet.
Getting it to start was its own small fight.
- Launching it from the terminal failed.
- A stuck process ran into a 30-minute limit.
- The card needed a different CUDA build.
In the end it is started by double-clicking a batch file.
What it bought: picture edits and texture close-ups at no credit cost. An approved face is kept pixel-identical by
repainting only a masked area.
6. Local image-to-3D
Hunyuan3D, running inside ComfyUI, produced a full body from one picture in about five minutes for 0 credits. The
shape was plausible. The face was poor.
We then pushed for maximum quality with no limit on generation time.
| Local Hunyuan3D job on the RTX 2060 | Time | Credits |
|---|---|---|
| Full body, first pass | about 5 minutes | 0 |
| Body at the card's maximum | 10 minutes | 0 |
| Separate head at the card's maximum | 18 minutes | 0 |
The body and the head are merged in Blender.
The limit is the hardware. One job ran 14 minutes because the game was using the same GPU. Generation and
play-testing cannot share the card. A bigger card is under consideration. The setup is also written down so that a new
machine knows exactly what it needs to download.
What this gave us
A statue pipeline that holds up close: generate high, bake to about 20k triangles, keep the detail in lossless maps. And
a second generation route that costs time, not credits. Paid generation is now reserved for the pieces where its quality
is needed.
The character quality problem
- 11Unarmed character: decimation, pose, drift
- 12Fused-shell hair: why it tears when it moves
- 13Character quality gap: cause and four routes
Unarmed character: decimation, pose, drift
October 2026 · his own branch, merged to main as v0.37 and v0.37.1 · An unarmed fighter in an engine that assumed every
character holds something.
| What we built | Agent J, a fast off-tank who fights with fists and kicks. Also a git worktree workflow for parallel character and map work, and docs/HERO_KITS.md. |
| The challenge | The engine assumed a weapon in the hand. A Blender preview showed the wrong pose. A 12- degree camera offset pulled his combo sideways. |
| Key decisions | Real unarmed clips, not hidden-sword moves. Check proportions before rigging. A strike travels along the character's body, for every character alike. A change stays on the character it was made for. |
| The result | Agent J shipped in two entries, v0.37 and v0.37.1, from 655,622 triangles down to 59,996, with a 128-damage combo cycle. |
1. Two workstreams, one folder
Problem. The map work was running on main. We wanted to start a character in parallel.
Cause. A branch alone is not enough. Both workstreams share one folder, so switching branch for one would silently
switch the other.
Solution. Agent J got a git worktree: his own branch in his own folder.
The cost. It showed up later. His copy of the game had no statues on Olympus until main was merged into it.
2. The model
Agent J came back from Meshy at 655,622 triangles. We brought him to 59,996 with the face protected. Facial detail
is the priority when downscaling.
| Region | Triangles kept |
|---|---|
| Head | 17% |
| Body | 7% |
Problem. Strands of his fringe were skin-coloured.
Cause. The face picture had been projected onto the fringe.
Solution. A manual step and a scripted step together. We painted over the bad patches by hand in Blender. Then we
compared the saved texture with the original and refilled those areas from neighbouring hair. The first refill colour was
too light and was redone.
Problem. We had already shipped one character with the wrong proportions (Agent L). We did not want a second.
Solution. Before rigging, we compared Agent J's height proportions to the other melee characters. His legs were
41% of his height, against 47 to 50% for the others. We turned down a preset fix and tuned neutral sliders on the bare
mesh. The tuned body went to Mixamo with the proportions already baked in.
3. No sword
Problem. To get Agent J moving, we had first lent him Agent Zero's sword and kit. But Agent J has no weapon. He is
a fighter with just his fists.
Cause. Every character before him held something, and the engine assumed it. The sword arm stayed locked in its
carry pose while he ran.
| Option | What it gives | Trade-off | Verdict |
|---|---|---|---|
| Hide the sword, keep Agent Zero's moves | Minutes of work | Greatsword swings with empty fists look wrong | Rejected |
| Real fists: punch and kick clips, arms owned by the animation | The first true unarmed character | Engine work | Chosen |
Solution. Boxing and kicking clips from Mixamo. His combo became a jab-cross, a knee and four punches (128
damage a cycle), with a Flame Kick and a Flame Sweep.
A check on clip speed. We suspected the speed settings chosen in Mixamo had been lost on his Q and E abilities.
We measured every clip against its file. They matched to the hundredth of a second. We then applied the speed-ups
we wanted by name (Jab Cross 1.5x, Punch Combo 2x), because Agent J is a fast fighter-type off-tank.
4. Tuning the wrong arms
Problem. The jab looked cramped, with the elbows tucked into the body. We built a slider panel in Blender to tune it
by eye, and tuned it.
Cause. The preview was wrong. Mixamo saved the clip on a skeleton with its arms level. Agent J rests with his arms
47 degrees lower. We had loaded the clip straight onto Agent J's rig, and Blender applied it relative to the wrong rest
pose. It was the same trap as Agent L's draw (chapter 07), walked into again.
| Elbow angle | Old Blender view | Game |
|---|---|---|
| Guard, frame 1 | 54° | 73.6° |
| Jab, frame 10 | 122° | 142° |
Every arm had shown about 20 degrees more bent than the game. The elbows that looked tucked into his body were
the preview's error. The tuning had only extended the elbow back to where the original clip already was. We went full
circle, because the original was already close to perfect.
Solution. A standing rule. Before any Blender scene is used for a decision, one elbow angle is measured there and in
the game, and they must match.
5. The 12-degree drift
Problem. Holding W through his combo carried Agent J 13.4 m. We dampened the forward key. He then drifted
sideways.
Cause. It goes back to chapter 07. The camera looks 12 degrees to the left of where the body faces. The dampening
had been applied along the body, but the keys push along the camera.
| Option | What it gives | Trade-off | Verdict |
|---|---|---|---|
| 1. Measure the key dampening along the camera | No sideways pull from the keys | None | Kept |
| Option | What it gives | Trade-off | Verdict |
|---|---|---|---|
| 2. Make his strikes travel where the camera looks | Moves go straight up the screen | Agent J becomes the only character whose body points off his travel, and his effects look angled | Tried, then undone |
Solution. We built both and judged them in play. Option 1 stayed. Option 2 broke consistency across the roster. It is
now a standing rule: a strike travels along the character's body, for every character alike. If that is ever changed, it
changes for all of them together.
6. Changes stay on the character they were made for
Problem. Agent J's guard-plus-click kick did nothing.
Cause. Our first diagnosis was incomplete. It found one cause, and the fix built on it did not work. There were two. The
guard pose pinned his feet. And holding guard cancels any basic attack, which the kick counted as.
A second mistake. The fix was first applied to every character. That was wrong. Agent L's guard must still be able
to cancel.
Solution. The change was scoped back to Agent J through his own data. Rule: a change made for one character
stays on that character.
Problem. In game, the feet in his boxing guard looked wrong next to the same clip on Mixamo's site. On Mixamo the
feet move slightly. In game they looked glued to an anchor point.
Cause. Not the clip. It matched the file to 0.1 degrees. What moved his feet were two engine-wide corrections that
read the idle clip. One keeps the lower foot on the floor. One corrects ankle angles. His new idle started on the ball of
the foot.
Solution. A rearrangement of his stances. The old idle stays. The fighting stance is the guard. A boxer's step-and-
drag is the guard walk.
7. Versions, the merge, and Character kits
Versions. Version numbers were moving too fast. Character changes are now batched together. All of Agent J is two
entries, v0.37 and v0.37.1.
The merge. A worktree cannot touch main's folder, so the merge was run from main. It was a clean fast-forward.
Character kits. We also wrote docs/HERO_KITS.md. It describes every character as he plays today, in plain
language, so the cast could be rebuilt in another engine years from now. It holds the latest version of each kit only,
with no history.
What this gave us
An engine that no longer assumes a weapon, and three rules that apply to every character after Agent J. Verify a
Blender preview against the game before deciding anything from it. Strikes travel along the body for the whole roster.
A fix for one character is scoped to that character's data.
Fused-shell hair: why it tears when it moves
October 2026 · her own branch in its own worktree, v0.37.2, not merged · Our first healer, and a limit of generated character
models.
| What we built | Agent C, the first healer, as a 0-credit placeholder: Agent E's body with pink hair, a wand made in code, and a full healing kit. |
| The challenge | Shortening her hair exposed a hollow back. Her hair is one fused shell with the body, so it either stays stiff or opens holes. |
| Key decisions | Retarget the walk onto her own skeleton. Compare variants in Blender before anything goes into the game. Judge visual fixes in a live match. Stop at variant 2e and commit. |
| The result | The kit checker passed 49 abilities with 0 mismatches. The hollow went from 254 see-through pixels to 146. The hair is still not right, and we know why. |
1. The kit was the quick part
Agent C is our sixth character and the first healer. The requirement was a placeholder at no credit cost: Agent E's body
with pink hair and a wand made in code.
Her kit:
- Light bolts that heal or hurt for 25.
- A heavy orb.
- A cleanse.
- A launch.
- A 20 m healing circle.
The kit was built and measured quickly. Every number that the design had not specified was listed out for review. The
kit checker passed 49 abilities with 0 mismatches.
That is the pattern of this whole project in one character. The mechanics were the quick part. Her hair took far longer
and is still not right.
2. A borrowed walk
Constraints. No greatsword walk on Agent C. No new Mixamo downloads onto her skeleton.
Problem. We gave her the shared girl's walk. Her legs were fine below the knee and twisted at the hip.
Cause. That walk had been recorded on Agent E's older rig. Its thigh bones sit about half a turn round their own
length from Agent C's skeleton.
Solution. We retargeted the clip onto her own skeleton, the route Agent B already uses. The motion came through
clean.
3. The hollow back
Problem. Her hair was to be shorter than Agent E's. After the cut the hair looked good, but her upper back was
hollow.
Cause. Something nobody had measured. On the generated model, the long hair was the surface of her upper back.
There was no back underneath.
We ranked the options and tried them in order.
| Attempt | What it did | Result |
|---|---|---|
| Patch | Glued the inner layer of hair onto her back | Fine on the character select screen. Wrong in a match, where the patch and the swinging hair slid against each other as two pieces. |
| Bind all hair to the body | Bound all the hair to the body, not just the patch | Did not hold up in a live match either. |
| Lower the cut | The original hair covers the hole | Kept. The simplest fix was the best one. |
We measured the result by counting see-through pixels on her back.
| Model | See-through pixels on the back |
|---|---|
| Short hair, before the lower cut | 254 |
| Short hair, lower cut | 146 |
| Untouched long-hair model | 8 |
Two rules came out of this. Measure an asset before editing it. And a visual fix is judged in a live match, never claimed
from a static view.
4. Hair that follows the head, or the body
Problem. On a turn, the lower half of her hair rode her back like a backpack.
Cause. The hair on her skull followed the head and the hair below followed the chest. The chest twists 32 degrees in
her run and the head 5. That is 27 degrees of disagreement in one piece of hair.
| Variant | How the hair is bound | Result |
|---|---|---|
| Current | Head on the skull, body below | The steep L shape |
| 1 | Head only | Stiff |
| 2 | 70% head, 30% body | The best of the first set. 70/30 accepted. |
| 2b | A gradient from 70% to 90% | No visible difference |
| 2c, 2d, 2e, 2f | Finer gradients from crown to below the neck | 2e kept for now |
How we compared them. The variants were played side by side in Blender, on a recording of a real walk and sprint
captured from the game.

Options 2e, 2d and 2c on the recorded run, from behind: the view that decided the hair, and the one that sent us back to Agent E's
own model first (chapter 14).
A process mistake. Two of the variants (2d and 2e) were put straight into the game. That became a rule. Options
are compared in Blender first. Nothing goes into the game until one has been picked. A test never swaps the live files.
We stopped there on purpose. Variant 2e stays for now, and everything was committed as v0.37.2.
5. Over-scoped
Problem. Finishing Agent C meant one thing: swapping in her character model. We treated the whole open list in our
notes as the job, and started building a test suite, an NPC healer and a benchmark switch.
Solution. We stopped that work. She was committed to her branch as she stood. One real bug did come out of it: a
match crashed when Agent Zero marked an NPC Agent C. But the scope was wrong. The lesson is to confirm the size
of a task before starting it.
6. Why this is a wall and not a bug
Six variants of weights did not fix Agent C's hair, because the weights were never the problem. Her hair is part of one
fused shell with nothing beneath it.
| Choice | Trade-off |
|---|---|
| Hair bound stiffly | The shell stays intact. The hair does not move. |
| Hair allowed to move | It moves. Holes open in the shell. |
This is the same compromise we made for Agent E (chapter 02), reached again from the other side.
What this gave us
A working healer kit, a clean retargeting route for borrowed clips, and a precise statement of the limit. A generated
character whose hair and body are one shell cannot have hair that both moves and stays closed. Chapter 13 takes on
that question.
Character quality gap: cause and four routes
October 2026 · Why our characters are not production-grade yet, and the routes that could close the gap.
| What we built | A root-cause analysis of character quality, a comparison of four routes to fix it, and a five-step plan. |
| The challenge | The game had seven characters (Agent C still on her own branch) and 476 commits across its branches. At that pace, an Overwatch or Fortnite level game was one to two years away. Our schedule is three months. |
| Key decisions | The problem is how the model is constructed, not the engine. We do not build on another game's extracted assets. We stay on Three.js. Netcode starts in parallel. |
| The result | A reachable target: a vertical slice of six characters, one finished map, playable on LAN. The plan is a recommendation. Nothing has been built from it yet and the model route is not yet chosen. |
1. The pace problem
Problem. The schedule for a production-grade shooter is three months. At the current pace it would take one to two
years. That is not acceptable.
What we observed. Every jump in speed so far came from adding a tool, not from working harder. Each new tool
made the workflow many times quicker. So the question became which tool, or which method, comes next.
2. What was wrong with the characters
The complaint was specific.
| Character | State |
|---|---|
| Agent J, Agent Zero | Acceptable |
| Agent L | 95% there |
| Agent E and the others | Not acceptable |
| Agent C's hair | 4 out of 10, where 10 is the minimum we accept |
Hair was the clearest symptom. Our hair physics are a patch over a deeper fault. They are fine for now. They are not
fine for production-grade material.
3. A route we rejected: another game's extracted assets
The modding community makes very good custom Fortnite characters. The method uses FortnitePorting, UModel and
Blender. It pulls the official character models, skeletons and textures out of the game files, loads them into Blender,
and modifies them. We evaluated it as a route to better characters. It fails on three counts.
| Reason | Detail |
|---|---|
| Licence | Those meshes, textures and skeletons are Epic's property. Any character built on them would have to be thrown away the day the game leaves our own PC. We do not ship work built on assets we do not own. This alone decides it. |
| It gives us Fortnite's characters, not ours | Turning one into Agent E or Agent L means sculpting new accessories on top of the base models. That is exactly the expert Blender work we do not have. |
| It is not an engine feature | Those models look good because a character artist spent months on each one, not because of Unreal. A Meshy model dropped into Unreal has the same hair gaps. |
A correction to our own guide. DEVELOPER_GUIDE.md section 1 says the Meshy characters are already close to
the right tier. That holds at gameplay distance. It does not hold on the character select screen, or for anything that has
to move, like hair and cloth.
4. The root cause: how the model is constructed
The evaluation did point at the real difference. It is the construction of the model.
| Production character (Fortnite, Overwatch) | Our characters today | |
|---|---|---|
| Construction | Separate parts: body, hair clumps, each garment | One fused shell from Meshy. Hair is welded to the scalp and shoulders |
| Hair motion | Each clump has its own bone chain, hand-painted weights, and collision against head and shoulders | tools/hairbones.mjs finds hair by colour and binds it to one chain, limited to 4-12° of swing (CHAIN_PRESETS.hair in src/rigged.js) |
| Under the surface | Scalp under hair, body under clothes | Nothing. Move the hair further and holes open |
| Topology | Edge loops at joints, so bends are clean | Triangle soup (Agent J: 655k decimated to 60k), so bends pinch and skin shows |
Problem. Hair barely moves, and it cannot be made to move more.
Cause. The 4-12° limit is the patch. We capped the swing because a fused shell tears if it moves more.
Solution. No physics tuning fixes that. The hair has to be a separate object over an intact head.
5. Options we considered
| Route | Fixes hair and gaps? | Our effort | Risk |
|---|---|---|---|
| A. Meshy body + separate hair. Generate the character bald. Generate or build hair as its own mesh. Split it into clumps, with a bone chain per clump and real spring physics with colliders | Hair yes. Body proportions and skin gaps only partly | Low. The Blender work is scripted | A generated hair piece is still a shell, just a movable one |
| Route | Fixes hair and gaps? | Our effort | Risk |
|---|---|---|---|
| B. VRoid Studio. Free, slider-based, anime style. Exports VRM, which is glTF, and three.js has a spring-bone hair library for it | Yes. Hair is authored as strands with physics built in, and there is a full body under the clothes | A few hours per character in a GUI, no Blender skill | The look shifts to anime (it does suit our characters). Armour must be attached as separate Meshy pieces |
| C. Commission or buy a licensed stylised character from a character artist | Yes. The only route to no flaws at all | Money and 2-4 weeks per character | Cost, and the art style must be locked first |
| D. Port to Unreal | No. Same assets, same flaws | The whole 15k-line game rewritten. The work would live in binary editor assets, which are far harder to script than plain code | Eats most of the remaining two months |
Cascadeur is installed and not yet used. It stays the right tool for motion. It does nothing for model quality.
6. What the remaining time can and cannot buy
Overwatch polish is roughly 3-6 artist-months per character. No tool closes that gap in 70 days. What is reachable is a
vertical slice: six characters whose look and physics read as intentional, one finished map, playable on LAN.
Characters are not even the biggest schedule risk. There is no netcode at all, and LAN play needs roughly 2-3 weeks
of it. The architecture helps here. An NPC is already a Player driven by a fake keyboard (chapter 05). So a remote
player is a Player driven by a network one. After that, map art is still the largest visual gap.
7. The plan
| Step | What | Why |
|---|---|---|
| 1 | Bake-off on one character (2-3 days). Take Agent C. Put the current model, route A and route B side by side in Blender on recorded game motion. Pick the winner by eye | Agent C has the worst hair, so she is the hardest test |
| 2 | Rebuild the hair system (2-3 days). The single chain becomes one chain per clump, with head and shoulder colliders | Whichever route wins, every character benefits |
| 3 | Re-do characters in order of dissatisfaction. Agent E first. Agent J and Agent Zero only get the hair pass | Effort goes where the gap is largest |
| 4 | Start netcode in parallel from week two | It is the item that cannot be compressed at the end |
| 5 | Stay on Three.js | A port fixes none of the asset flaws |
The plan is a recommendation. Nothing has been built from it, and the route is not yet chosen.
What this gave us
A precise diagnosis. Our characters fall short because each one is a single fused shell, not because of the renderer or
the engine. That turns a vague quality complaint into a construction problem with four priced routes, a test to choose
between them, and a scope that fits the schedule.
Making a generated character game-
ready
- 14Agent E rebuild: lineup, timeline and cost
- 15Faceting: weld before decimating
- 16Seam gaps and a capped collar
- 17Master first, then reduce
- 18Fixing AI paint errors by hand
- 19Texture atlas, first rig, source holes
- 20Voxel remesh fills: the monster neck
- 21Full-res body: hood, shoulders, normals
- 22Welding a new head onto a body
- 23Game budget, KTX2 and skinning spikes
- 24Build cost and the rules it left
Agent E rebuild: lineup, timeline and cost
5 to 9 October 2026 · Agent E, our first character, rebuilt from her Meshy sources and put back in the game as v0.38 · Four
days and more than a hundred named builds, judged side by side in Blender, then in the game.
| What we built | A new Agent E, W7: a full-resolution benchmark model with a new head welded to the body, a clean hood and shoulders, and one skin tone. From it, a game version at 47,557 triangles, rigged on her existing Mixamo skeleton, with her textures compressed for the graphics card (213 MB down to 53 MB). Along the way: three earlier game models (R8fix, R8x, M8x), a painting and sculpting toolkit inside Blender, and a texture-compression path the whole roster can use. |
| The challenge | Chapter 13 asked how far a generated model can be pushed towards a Fortnite-level character. Agent E had wedges on her face and belly, hair fused to her body, holes behind the shoulders and paint in the wrong places. A generated model looks finished in the generator's own viewer. Taking it apart, reducing it and making it move exposes everything it never built. |
| Key decisions | Weld before reducing, bake after. Keep a full-resolution master and fix it there. Let the eye decide every visual question, and give the eye tools: a brush, a marker, an eraser, sliders. When a junction keeps failing, take the piece from a source that has no junction. Judge geometry bald, and judge a rig in motion. |
| The result | W7, the new benchmark for Agent E, in the game as her default look: 47,557 triangles, 53 MB on the GPU. 35 to 48 hours of active work over four days, and a recipe written down for every character after her. |
1. What we were trying to do
Agent C's hair (chapter 12) had shown that a generated model's hair and body are one shell. Before building a new hair
system on top of that, we went back to the first character and looked at her up close. The goal was simple to state:
the same character, at the same game budget, with a smooth body and a sharp face. Chapter 13 had planned a bake-
off of routes. In practice it became a rebuild.
Two constraints held throughout. Nothing is judged from a script's own render: every candidate stands in a row in one
Blender window, and the decision is made by eye. And a candidate only counts once it holds up in the game, moving,
on the character select screen and in a match.
2. The four days at a glance
| When | The problem we were solving | What we built | Where it ended |
|---|---|---|---|
| 5-6 Oct | Why does the in-game face look worse than the sculpt? (chapter 15) | Four shading-only faces, a split-parts download, twelve Meshy repaints (T1-T12), three Meshy remeshes, the first rebuilds R, R8 and R10 | The wedges were our unwelded reduction. Face from T8, hair paint from T3 |
| 6-7 Oct | Seams that opened and a collar that moved (chapter 14) | R8fix v1 to v7, the Turtleneck | On par with the old model (v6, 56,875 triangles) |
| 7 Oct | The step we skipped: master first (chapter 17) | Masters F8 and F10, R8x and R10x, the two routes | R8x at 39,344 triangles |
| 7 Oct | Colour a script cannot fix (chapter 18) | A paint file with one-click buttons, a marker-and-diff workflow | R8x hand-painted: the pick |
| When | The problem we were solving | What we built | Where it ended |
|---|---|---|---|
| 7-8 Oct | Into the game, and a hole that was not the rig (chapter 19) | A 63 MB atlas, the first rig, the shoulders rebuilt from the mould | Streetwear X in the game: the only game-ready build that was not broken |
| 8 Oct | Geometry, not colour (chapter 20) | Collar welds v1-v7, Route B (one voxel shell) and Route C, M8x as the final figure | B7's body kept as the benchmark body; M8x in the game; Rule One |
| 9 Oct | Back to full resolution (chapter 21) | The T8 body at 319,000 triangles with B7's back, a rebuilt hood top, the shoulders fixed | Option 2, the body every later build stands on |
| 9 Oct | Joining the head so she reads as one person (chapter 22) | Neck welds W1 to W7, a solved neck width, one skin tone, a choker variant | W7, the new benchmark for Agent E |
| 9 Oct | From benchmark to game on a 6 GB card (chapter 23) | Per-part budgets, a hand reduction, the rig, compressed textures | 47,557 triangles, 53 MB, in the game |
3. The lineup
Every build stood in the window next to the ones before it. These are the families, with the verdict each one got.
| Build | How it was made | Triangles | Verdict |
|---|---|---|---|
| A (the old game model) | The original Streetwear, decimated once, unwelded | 28,758 | The face and hair read well, but the unwelded reduction had scarred her legs, belly and face |
| R8 / R10 | T8 (T10) cut into eleven parts, each reduced unwelded, re-baked | 53,799 | Right face and colour. Holes at every seam |
| R8fix v6 | Welded parts, voxel-remeshed clothes, hoodie from Meshy's Ultra remesh, all baked | 56,875 | The first rebuild to match the old model's look without its wedges |
| F8 / F10 | The full model with T3's hair paint through a second UV map | 605,392 | The masters |
| R8x | F8's parts welded and reduced to budgets, skin baked, cloth on Meshy's texels, painted by hand | 39,344 | Of the three routes, the one we judged best to carry on fixing |
| Streetwear X | R8x baked to one atlas, rigged, shoulders rebuilt from the mould | 46,709 | The only game-ready build with nothing broken, so it was the one that went into the game (8 Oct) |
| B7 | Every sculpt piece voxel-fused into one closed shell | about 50,000 | A perfect body with a monster neck. We kept the body as the benchmark and left the neck to be solved separately |
| M8x | Meshy's Ultra remesh with B7's back grafted on and the new hair | 43,563 | In the game as a skin (8-9 Oct) |
| Option 2 | The T8 body at full resolution, B7's back, a voxel hood top, hand sculpting | 318,734 | The base for the weld |
| W7 | Option 2 with the new head welded at a solved neck width, one skin tone | 475,573 | Strong enough to replace every earlier build as the new benchmark for Agent E |
| W7 game | W7 cut part by part, reduced by hand, rigged, textures compressed | 47,557 | In the game as her default look (v0.38) |
4. What it cost
Measured from the timestamps of the working sessions. Active time counts every stretch where work was happening;
a gap of more than 20 minutes (lower figure) or 90 minutes (upper figure) counts as a break. The upper figure is the
closer one: many long gaps were spent painting and sculpting by hand in Blender, which leaves no timestamp.
| Day | Active time | What the day produced |
|---|---|---|
| 5 Oct (evening) | 0.6 h | Tool scan, baseline, four faces |
| 6 Oct | 2.0 to 4.1 h | Twelve repaints ranked; R8 / R10; M8 / M8u / M8x; R8fix v1-v4 |
| 7 Oct | 10.4 to 12.0 h | R8fix v6-v8; F8 / F10; R8x; the paint file; the atlas; the first rig |
| 8 Oct | 9.5 to 15.0 h | Shoulders; collar welds; Routes B and C; M8x; the head transplant |
| 9 Oct | 12.9 to 16.6 h | Option 2; W1-W7; W7C; W7 game; the rig; KTX2 |
| Total | 35.4 to 48.3 h, over 97.5 h on the clock | About 2,200 files, 28 GB: 233 Blender files, 179 GLB builds, 294 build scripts, over 1,000 check renders |
At least eleven of those hours were hands-on in Blender: painting the jacket and skin (7 October; hands-on stretches
of 55 minutes, 1 h 36 and 3 h 51), marking the collar (8 October, an hour), erasing, sculpting and flattening the hood
and shoulders (9 October, about three hours), trimming the new head, and the final reduction to game size. By the
night of 9 October the last 7,854 triangles of the game model were removed by hand, part by part.
Money: almost none. Meshy's retextures were free rerolls, an Ultra remesh costs about 44 credits, and the standing
budget set during this work is 500 credits per character without a question, 1,000 at the cap. The cost was time, and it
went where the table says. For scale: refining Agent E took three times longer than any feature built since the start of
the game, which is why we wrote the recipe down, so the characters after her do not pay that cost again.
5. Where it stands
W7 game is Agent E's default look in v0.38, first in her skin picker, with Streetwear, Streetwear X and M8x still
selectable. Its textures load compressed (BC7 on a PC graphics card). The full-resolution W7 is the master: any later
fix is made on it and carried down. Two things are open: the seam at the back of her neck stretches up to 4.6 cm in a
sprint, and her hair still uses the swing settings tuned for the old, longer hair.
The honest summary of four days: a generated model can be taken to a clean, game-size character. The recipe that
works is the one chapter 10 had already found for the statues: reduce the welded mesh, then bake. Everything we
tried around that cost a build and taught us a rule (chapter 24). And the decisions that moved the model furthest were
not code. They were a brush stroke, a marker on a hood, and the switch from hunting for a flaw to working from a
screenshot of it.
The chapters that follow in this part take one problem each: why the game face looked worse than its sculpt; seams
that opened and a collar that moved; the step we skipped; colour that only an eye can fix; into the game, and a hole
that was not the rig; geometry, not colour; back to full resolution; joining the head; from benchmark to game; and
what it cost, with the rules it left.
Faceting: weld before decimating
5 and 6 October · The question that started the rebuild, the sources we gathered to answer it, and the three facts that shaped
everything after: weld before reducing, bake after reducing, and give the face its own budget.
| What we built | A baseline measurement of the in-game face, four shading-only test faces, Meshy's split-parts model used as a part label, twelve free repaints (T1 to T12) and three Meshy remeshes, ranked by eye. |
| The challenge | The game model's face was faceted, with wedges on her cheeks and belly, while the same sculpt looked smooth in Meshy's own viewer. |
| Key decisions | Weld before reducing. Stop moving colour between models: build the game model out of the painted model and bake each part from its own source. Give the face a budget of its own. |
| The result | The wedges were our unwelded reduction, not the sculpt. A welded leg at 1,424 triangles came out flawless where the old one at 2,900 had cracked. Face from T8, hair paint from T3. |
1. The face, measured

The game model as it was: a face with no detail, flat eyes.
Problem. The Streetwear model in the game had a faceted face, painted-on eyes, fringe colour smeared onto the
forehead and wedges on her cheeks and belly. We started with a baseline in headless Blender under neutral light.
| Agent E Streetwear | Agent C | |
|---|---|---|
| Triangles, whole character | 28,758 | 24,898 |
| Triangles above the head joint (face and the hair on the head together) | 4,354 | 4,354 |
| Colour texture | 2048 × 2048 | 2048 × 2048 |
| Share of that texture used by the head and its hair | 6.3% (about 263,000 pixels) | same |
| Agent E Streetwear | Agent C | |
|---|---|---|
| Texture density on the head | about 11 pixels per cm | same |
| Hair bones | one chain of 5 | one chain of 5, unused (hair follows the body) |
The face itself was 1,345 triangles and about 62,000 texels, scattered across the sheet. The dark patch over her nose
and mouth was not in the texture: flat-lit, the skin there is one clean colour. It was geometry. 1,315 corner points of the
face were duplicated, and neighbouring face triangles met at 33 degrees.
A first, cheap pass changed shading only:
| Version | What changed | |
|---|---|---|
| A | Now | The file as the game loads it |
| B | Welded + smooth | The face's split corner points joined, so light blends across it |
| C | B + soft shading 60% | Face shading pulled 60% toward a smooth sphere round the head |
| D | B + soft shading 100% | Face shading taken wholly from that sphere |
It helped, and it was a stopgap. The real question was why a sculpt that looks smooth in Meshy's viewer comes out
faceted in ours.
2. The cause: the wedges were ours, not Meshy's
Cause. At full resolution (605,392 triangles) the sculpt is smooth everywhere. Meshy exports every vertex split along
its UV seams, and our original reduction had decimated that unwelded mesh by more than 90 percent. Each seam was
an open edge: the decimate protected the edges and chewed the surfaces between them. The wedges, and the holes
the base model had, were the seams. The face had also been given only 3 percent of the triangles, because a single
decimate hands out triangles by surface area.
Solution. Weld first (remove_doubles: 327,905 vertices to 302,642), then reduce. A welded leg at 1,424 triangles
came out flawless; the old one at 2,900 had cracked. That one fact moved everything else in this part.
3. Separable parts: Meshy's split-parts download
The tool scan we ran first found nothing that turns a picture into a rigged, part-separated character. It did find that
Meshy can return a model as separate pieces. Re-rendering the original with "split parts" instead of "texture" gave us
this:
| Result | |
|---|---|
| Parts | 11 separate pieces: hair, head, hoodie, midriff, shorts, two legs, two shoes, two hands |
| Hair | Its own closed piece, with properly sculpted locks and fringe |
| Each part | Watertight (no open edges) |
| Triangles | 648,585 in total; the hair alone is 249,134 |
| Texture | None, and no UV layout |
| Skeleton | None |
No colour, and nothing under the hair. We measured what is behind it:
| Height | Is there body under the hair? | How far the hair stands off it |
|---|---|---|
| 1.52 m and up (back of the head) | None at all | n/a |
| 1.36 to 1.48 m (back of the neck, top of the hood) | Mostly none | 3 to 9 cm where there is any |
| 1.32 m and below (her back) | Yes, everywhere | 5 cm at the hood, 21 cm at the waist |
So we used the split model as a label, not a mesh: it says which triangle of the painted model belongs to which part.
Where the hair comes off, a scalp liner in hair colour closes the gap.
4. Colour: three projections that failed
Problem. The first rebuild (R, 53,410 triangles, 6,000 on the face, about 17 texels per cm instead of 11) showed that
the geometry was now right: the abs, legs and lower face came out perfect. The colour was not. Carried over from the
old game model, it made her look alien: smudged eyes, a speckled neck, pale hems.
Cause. The two surfaces were 3.4 mm apart at the legs and 13.5 mm at the arms. Projecting colour across that gap
reads the wrong texel. We added a safety check (no bake if the surfaces are more than 3 mm apart). When it stopped
the next bake at 5.9 mm, we first said Meshy had regenerated the geometry. That was wrong: with a proper fit the gap
was 0.07 mm.
Solution. Stop moving colour between models. Build the game model out of the painted model itself, reduce it part
by part, and bake each part from its own source. The gap is then zero by construction.
5. The sources: twelve paints and three remeshes

A Meshy retexture at full resolution: a better face, the eyes still odd.
Meshy can repaint a model without touching its geometry, and the rerolls were free. We had the Streetwear sculpt
repainted twelve times. Every T has the same 605,392 triangles in the same order; only the paint differs. Ranked by
eye, face first, then hair from behind, in a window sorted 2K / 4K / 8K so nobody had to memorise numbers.
| Colour texture | Face | Skin warmth, 1 = palest, 12 = warmest | Hair from behind | |
|---|---|---|---|---|
| T1 (2k) | 2K | Banned: botched eyes, fringe painted on skin | 3 | Acceptable |
| T2 (2k) | 2K | Borderline acceptable | 7 | Acceptable |
| T3 (2k) | 2K | Banned: botched eyes, fringe painted on skin | 8 | Best detail |
| T4 (2k) | 2K | Banned: botched eyes, fringe painted on skin | 1 | Acceptable |
| T5 (2k) | 2K | Borderline acceptable | 6 | Acceptable |
| T6 (4k) | 4K | Pass: sharp eyes, face for a new character | 9 | Bad: weird left-side detail only |
| T7 (4k) | 4K | Pass: soft eyes, face for a new character | 10 | Bad: weird left-side detail only |
| T8 (4k) | 4K | Pass: fierce eyes, suits Agent E | 11 | Bad: weird left-side detail only |
| T9 (4k) | 4K | Softer Agent E, runner-up | 5 | Acceptable |
| T10 (4k) | 4K | Top contender, like T8 | 12, the warmest | Bad: weird left-side detail only |
| T11 (8k) | 8K | Banned: botched eyes, fringe painted on skin | 2 | Bad: weird left-side detail only |
| T12 (8k) | 8K | Pass: big eyes, mixed opinion, new character? | 4 | Acceptable |
The decision: face and body from T8 (R8) or T10 (R10), hair paint from T3. A bigger texture did not mean a better paint:
the 4K and 8K hair was worse than the 2K, and the game ships 2048 anyway.

What a Meshy-painted face is up close, hair hidden: the fringe was painted into the forehead skin, brows and all. No retexture
escapes this; it is how an image-to-3D painter sees a fringe.

Hair paint smeared onto the face, on Meshy's flagship model.
We also had Meshy remesh T8 three ways. Its own retopology, one fused shell each:
| Model | Triangles | Maps | Normal map | Verdict |
|---|---|---|---|---|
| M8 (remesh) | 29,368 | 4K colour + metal/rough | none | Clothes and hair good. Skin lost her roundness; the face a blob at game size. |
| M8u (remesh + unwrap UV) | 40,969 | 4K | none | Same, cleaner islands. |
| M8x (Ultra) | 104,888 | 4K | none | Its belly button texture was noticeably better than the others'. Hair and hoodie geometry later came from it. |
Reading the files themselves explained why M8 looked worse in Blender than in Meshy's viewer: the remesh drops the
normal map (two images in the file, not three), and Meshy's viewer lights it more kindly. We concluded that with a
normal map the remeshes would have been the obvious choice. Without one, our own rebuilds still won where it
mattered most for a body: they kept her roundness and had none of the veins and ridges the remeshes left on her
legs. What the remeshes did very well was the clothing and the hair, so that is what we took from them.

The window at its widest: the retextures, the old game model, the rebuilds and the masters. A clone army, assembled only to pick
one face, one hair and one hoodie.
6. Two ways to put colour on a reduced mesh
This is the fact behind both the fused eyes and the black teeth at the hoodie hem that come up in chapter 17, and it
took a day to see that they were the same fact.
| Route | What happens to the paint | Where it showed |
|---|---|---|
| Keep Meshy's UVs through the decimate | Every edge collapse interpolates the UV coordinates. Exact on a flat surface; on dense paint it slides the texels. A thin dark band in the texture (the painted shadow under a hem, a lash line) is stretched into wedges by the big triangles. | R8x: inner eyelids pulled to the centre; black wedges running down from the hem onto the belly. The same two spikes in five builds while the geometry under them changed every time; the clay render never had them. |
| Reduce, unwrap fresh, bake from the painted source | Every texel is read from the painted surface at the point it lands on. Colour follows the painted hem and lash line whatever the triangles do. A 4K or 8K source gives a 2048 bake more than it can use; a 2K source slightly starves it. | R8, every R8fix, and R8x the moment its head and skin were baked. |
The rule: reduce, then bake. Keep the original texels only at full resolution.
7. Where the triangles should go
A single decimate hands out triangles by surface area, and Meshy painted her whole face into 3 percent of the mesh.
The counts, measured with the same part test on each file:
| Part | Full | R8 / R10 | R8x one shell | R8fix v6 | Budget settled on |
|---|---|---|---|---|---|
| Hair | 243,779 | 14,110 | 20,919 | 17,289 | 12,000 to 16,000 |
| Hoodie | 106,620 | 10,316 | 10,012 | 14,783 | 10,000 to 13,000 |
| Shorts | 99,820 | 5,298 | 8,514 | 7,428 | 5,500 to 6,500 |
| Shoes (2) | 36,514 + 36,507 | 2,030 + 2,029 | 3,282 + 3,265 | 2,019 + 2,011 | 2,000 each |
| Head and face | 18,840 | 5,841 | 1,716 | 4,239 | 6,000 to 7,000 |
| Legs (2) | 13,566 + 13,172 | 2,911 + 2,842 | 1,424 + 1,435 | 1,551 + 1,568 | 1,500 each |
| Belly | 12,169 | 2,885 | 1,131 | 1,453 | 1,500 |
| Hands (2) | 12,223 + 12,182 | 1,470 + 1,463 | 1,056 + 1,048 | 1,007 + 913 | 1,100 each |
| Scalp liner | - | 2,604 | - (hair fused) | 2,614 | 1,100 to 2,600 |
| Total | 605,392 | 53,799 | 53,802 | 56,875 | under 60,000 |
Two things the table says. Legs and belly at 1,100 to 1,500 are flawless once welded; the old 2,900 bought nothing.
And the face needs a budget of its own, around a third of its full detail, or the eyes fuse. We also decided not to aim
for the 60,000 ceiling: most characters sit well below it, and every triangle we do not need is one less that can make
the game lag.
What this gave us
A diagnosis that held for the next four days: the generated sculpt was fine, our reduction was not. A source for each
part (T8's face, T3's hair paint, M8x's clothes and hair shape). And three rules that every later build followed: weld
before reducing, bake after reducing, and decide the face's budget before anything else.
Seam gaps and a capped collar
6 and 7 October · From the first part-by-part rebuild (R8) to R8fix v6, on par with the old model · The longest single fight of the
remake was one junction: where the hoodie meets the neck. It took eleven builds, and the answer was not another rule.
| What we built | R8 and R10, the first part-by-part rebuilds, then R8fix v1 to v6: welded parts, a few large texture islands, 2.5 mm overlaps at the seams and voxel-remeshed clothes. |
| The challenge | Holes at every seam, then a collar that either left a gap at the neck or wrapped it. The collar alone took eleven builds. |
| Key decisions | Weld each part before reducing it. Colour every face by what a rule would do before writing the rule. When a junction keeps failing, ask what the original did right: take the hoodie from M8x, which has no cap. |
| The result | R8fix v6 at 56,875 triangles, on par with the old model: the first rebuild that matched the old model's look without its wedges. |
1. R8 and R10: the right face, and holes at every seam

The first row: a 2K retexture (T1), the game model (A), and the first two rebuilds (R8, R10). A's shine is the old game material; the
rebuilds already carry the retextures' paint.
R8 and R10 were built straight from the painted model: T8 (or T10) cut into eleven parts by the split-parts test, each
part reduced to its own budget, unwrapped fresh, and T8's paint (T3's for the hair) baked onto it.
| R8 | R10 | |
|---|---|---|
| Face and body | T8's paint | T10's paint |
| Hair | T3's paint on both | |
| Triangles | 53,799 | 53,799 |
| Face | 6,000 triangles | |
| Hair | Its own piece, 14,000 triangles, with the hair-coloured scalp under it | |
| Maps | Colour, normal and roughness, 2K each, baked from Meshy's own | |
They looked much better than the game model, because the face and colour now came straight from the painted
model instead of being carried across from another mesh. What they still lacked was closure: holes on her inner shin,
at the hip and around the shorts.
First attempts. Pinning the cut edges (R8b) took the model from 53,799 to 76,678 triangles, doubled the hair and
made black needle triangles. Overlapping the parts by 2.5 mm (R8c) still left black slivers.
Cause. Found by looking at the baked textures, not at renders. The unwrap had made thousands of tiny islands (most
of the 2048 sheet was black gaps), and Meshy exports every vertex split at its UV seams, so each part was reduced
as thousands of loose scraps.
Solution. Weld each part before reducing it, and pack a few large islands per part with a 16-pixel margin, every texel
pushed 24 pixels out over the black. That was R8fix.
2. R8fix, step by step
| Step | R8 original | R8fix | What it fixed |
|---|---|---|---|
| 1. Welding | Each part reduced as Meshy exported it: every vertex split at the texture seams, so the reducer saw thousands of loose scraps | Each part welded into one surface before reduction | Smoother reduction; the black slivers at island edges; most of the scarred look |
| 2. Texture layout | Thousands of tiny islands; most of the sheet was black gaps | A few large islands per part, 16-pixel bake margin, every texel pushed 24 pixels out over the black | Island edges no longer sample black; more real resolution on skin |
| 3. Overlap at seams | Parts butted edge to edge; the reducer pulled edges apart (shin, waistband) | Legs tucked into the shorts and shoes, belly under the hem and waistband, hands into the sleeves, each pushed 2.5 mm inward | The holes |
| 4. Clothes and shoes | Cut from the painted model, reduced | Rebuilt from the sculpt's closed pieces through a voxel remesh, then reduced | Even curves on the hoodie, shorts and shoes, like Meshy's remeshes |
| 5. Trim | - | Remeshed clothing faces whose nearest painted triangle is skin or hair are removed | The streaked band at the collar (the sculpt's torn hoodie edge) |
| 6. Budget | Hoodie 10k, hair 14k, legs 3k each, hands 1.5k each | Hoodie 12k, hair 16k, legs 2.5k each, hands 1.2k each | Triangles moved to the parts that show |
With v3 (55,931 triangles) we judged the clothes, skin and smoothness done: all of them looked much better, so every
later build left them alone. What was left was the collar.

A leftover of the hole problem: a slit on the belly after one reduction, a fold in an overlap strip. It went when the strips went.
3. The collar, eleven times

The gap: where the trimmed hoodie no longer met the neck, with the torn cap edge showing black.
Problem. From v3 on, the hoodie either left a gap at the neck or wrapped it.
Cause. R8 never had the problem because its hoodie was cut out of the painted model: one continuous shell, hoodie
rim to inner collar to neck skin. The voxel route made the hoodie a separate closed object, and Meshy closes each split
piece with a cap that follows the body underneath. Every trim rule drew the line between cap and hoodie somewhere
wrong. We wrote four rules before asking what R8 had done right.
The diagnostic that ended it: colour every face of the remeshed hoodie by what a rule would do to it.
| Colour | What it is | Faces |
|---|---|---|
| green | real hoodie: within 4 mm of the painted hoodie surface | 10,714 |
| red | Meshy's cap: the split sculpt closes the hoodie over the neck hole and the hood cavity; nowhere near any painted hoodie | 1,023 |
| blue | the rim ring where the hoodie meets the skin (< 2.5 mm from skin) | 712 |
| yellow | faces whose bake ray would hit skin or hair | 218 |
The builds in order, and why each one failed:
| Build | Rule | Result |
|---|---|---|
| v3 | Trim clothing faces whose nearest painted triangle is skin or hair | A pale wedge inside the collar (the hoodie's inner shell) |
| v4 draft | v3 + Ultra-remesh hair at 16k with T3 paint, inner shell removed, liner neck black | The collar gap: the trim took the hoodie faces that meet the neck |
| Build | Rule | Result |
|---|---|---|
| Turtleneck | v4 with deletion off: the cap kept | A black sleeve wrapping her neck. Wrong for this outfit, but it had a style of its own, so we saved it as a future skin instead of throwing it away |
| v4 | Delete the cap where it is more than 4 mm from faces labelled hoodie; tuck the rim 3 mm | The collar gap was still there on the right side, and the hoodie still had a hole in it. The labels lied: the vote had called the inner collar head and the hood lining hair |
| v5 | Cap = more than 4 mm from any painted surface, plus the tuck | Built, never judged: the options table below replaced it |
| v6 | No rule. The hoodie cut from M8x, Meshy's Ultra remesh, which is one fused shell and has no cap | On par with the old model at last, without its wedges. 56,875 triangles |
The options we weighed before v6:
| What | Collar | Cloth smoothness | Cost | |
|---|---|---|---|---|
| v5 (running) | draft + tuck rule for the hoodie | should close, but it's still a stitched edge | voxel smooth | 8 min |
| hybrid | remeshed hoodie everywhere except a band near the neck taken from the painted cut (continuous with the skin) | continuous, but an overlap seam a few cm down | voxel smooth | 1 h / 2 h |
| M8x clothes | hoodie, shorts, shoes cut from the Meshy Ultra remesh of T8 by the same part test we used for its hair; T8 colour and normal baked on | no cap exists: M8x is one shell, the collar comes out continuous like R8, and the 2.5 mm under- overlap we already use for skin parts does the rest | Meshy's remesh, whose clothing was already judged good | ~30 min optimistic / 1.5 h conservative |
The narrowing that made it work: the voxel-smoothed clothes were already good everywhere except the neckline, so
swapping all of them for M8x's would have thrown away work that was right to fix a problem that lived in one place.
Only the hoodie came from M8x. The neck part extends two rings under it.
4. What a build costs, and why it took eight minutes
A build took eight minutes, and with eleven collar builds in a row we needed to know why it was not instant, and
whether the GPU or the memory was the limit. Where the time went, stage by stage:
| Stage | What | Rough share of the 8 min |
|---|---|---|
| import + fit | T8 (605k triangles), the split sculpt, M8x; 10 rounds of nearest-point fitting | ~1 min |
| classification | every one of the 605k painted triangles checks all 11 sculpt parts for the nearest one (6.6 M lookups), 3 vote rounds, then the same for M8x's 100k faces | ~2-3 min, pure Python, single core |
| parts | voxel remesh of shorts and shoes, cutting, welding, decimating 11 parts, UV unwrap and pack | ~1 min |
| bakes | 6 Cycles bakes at 2048² (normal, colour, metal/rough for body and hair), on the CPU | ~2-3 min |
| textures + export | 24-round dilation in numpy, PNG writes, GLB, 7 check renders | ~1 min |
The GPU was off limits: three open Blender windows owned the RTX 2060's 6 GB.
5. The process mistakes, and the rules they left
| Rule | The incident |
|---|---|
| Version every build before it runs: own folder, own number, own file. Review and approve it before starting the next, so a failure is caught on the build that caused it | Two patch scripts failed silently; v5 was reported while only v4 existed, so no build could be trusted to match its number |
| Never touch an approved build | A rebuild chain started replacing R8 and R10, both already approved; an approved build is the reference every new one is judged against, and replacing it removes the yardstick |
| A deletion that leaves a hole is never a fix | The trim ate the collar faces and left holes in the hoodie: a hole on her is a worse defect than the gap the deletion was meant to close |
| Change only the part that was named | A collar fix dropped the shorts rule and left blue denim on her skin line, though only the small collar gap had needed fixing and everything else in that build was already good |
| Measure before writing a geometry rule: colour the faces a rule would keep, drop or move | Four trim rules written without that picture; the fifth picture showed the cap in red in one image |
| When a junction keeps failing, ask what the original did right | The answer to the collar was in R8 all along: one continuous shell |
What this gave us
R8fix v6, the first rebuild that matched the old model's look without its wedges. And the pattern that came back on 8
and 9 October: a junction between two separately made pieces is an invented edge, and the cure is a source in which
the edge does not exist.
Master first, then reduce
7 October · Masters F8 and F10, the R8x family, and the two routes on record · The plan had been to put T3's hair on T8 and
keep the full model. What came back was a game-size build. Going back for the full model we had skipped taught us more
than any build before it.
| What we built | Masters F8 and F10 (T8 or T10 with T3's hair paint read through a second UV map, 605,392 triangles), and R8x, reduced from the master part by part. |
| The challenge | We had skipped the full-resolution master and gone straight to a game-size build. Reduced from the master, the eyes looked fused and black teeth ran down from the hoodie hem. |
| Key decisions | Master first, then reduce. The face gets its own budget (7,000 triangles). Every part that carries skin or a face is baked; cloth and hair keep Meshy's texels. |
| The result | R8x at 39,344 triangles, with a body flawless at fewer triangles than any build before it, and a written order of work for every character after Agent E. |
1. The question
Problem. When R8 and R10 were first made, the plan was to put T3's hair on T8. The expected result was a full
model with no other change, to be reduced afterwards. What came back was a game-size build, chopped, reduced and
baked in one go. We realised the order we had always meant to follow was merge the T variants → get one perfect
variant → repaint minor flaws and downscale to game size. The shortcut had quietly become a whole other route, so
we now had two, and no master behind either.
So we built the step we had skipped. Since every T is the same mesh in the same face order (face centres differ by 0.0
mm), the merge is a second UV map: F8 is T8 with T3's hair paint read through T3's own layout on the 243,871 hair
faces. F10 is the same on T10. 37 MB each. Both looked right, which confirmed the merge costs nothing in quality: it is
only a second UV map, so every texel stays original.
| Master | R8fix v6 | |
|---|---|---|
| Mesh | T8's 605k, one shell | 56.9k, 11 parts, hair separate + liner |
| Paint | T8 4K + T3 2K, original texels | 2048 bakes of those |
| Hood red | in T8's texture | baked copy |
| Hair skin-colour patches | absent (a v6 artefact: M8x strands sit where T3's surface is skin, the bake picks skin) | present |
| Use | source for painting, the cinematic tier, the original | the game model |
Which of R8's reasons for chopping into parts still apply once the master exists:
| Reason R8 chopped into parts | Still needed? |
|---|---|
| put T3's hair paint on T8's body: the UV layouts differ | no: F8 carries T3's UVs as a second map |
| the hair must be its own object (hair physics chains) | yes |
| per-part budgets: face 6k, hair 16k, hands 1.2k, instead of a uniform 9% everywhere | yes, to spend triangles where they show |
| the decimate must not slide edges across a part border (hoodie and skin, hair and face), which smears colours at the seam | yes, mildly |
| the scalp liner (a hole appears under the hair once the hair is separate) | yes, once the hair is separate |
| re-unwrap + bake to 2048 maps | no: that was only because the parts were decimated unwelded; welded parts keep Meshy's UVs and textures |
2. A whole model reduced at once: R8x
The first R8x was F8 welded and decimated in one go to R8's count. R8x and R10x came out surprisingly good: no
flaws and no holes in the body, where R8 and R10 had both. That made the one weakness stand out: the eyes looked
fused.
| Part | R8 / R10 | R8x / R10x | R8fix v6 |
|---|---|---|---|
| hair | 14,110 | 20,919 | 17,289 |
| hoodie | 10,316 | 10,012 | 14,783 |
| head + face | 5,841 | 1,716 | 4,239 |
| shorts | 5,298 | 8,514 | 7,428 |
| Part | R8 / R10 | R8x / R10x | R8fix v6 |
|---|---|---|---|
| belly | 2,885 | 1,131 | 1,453 |
| legs (2) | 2,911 + 2,842 | 1,424 + 1,435 | 1,551 + 1,568 |
| shoes (2) | 2,030 + 2,029 | 3,282 + 3,265 | 2,019 + 2,011 |
| hands (2) | 1,470 + 1,463 | 1,056 + 1,048 | 1,007 + 913 |
| scalp liner | 2,604 | - (hair fused) | 2,614 |
| total | 53,799 | 53,802 | 56,875 |
We also noticed that their legs had half the triangles of R8 and R10, yet looked flawless and without holes. That told
us that welding, not triangle count, decided smoothness, and that a uniform decimate starves the face: 18,840 face
triangles are 3 percent of the model, so a uniform cut leaves 1,716.
The parts version took budgets per part instead, set by eye:
| Part | Full | R8x (parts) |
|---|---|---|
| head + face | 21,259 | 7,000 |
| hair | 244,083 | 11,999 |
| hoodie | 105,995 | 9,999 |
| shorts | 99,897 | 6,499 |
| shoes | 36.6k each | 2,000 each |
| legs | ~15.2k each | 1,500 each |
| belly | 14,503 | 1,500 |
| hands | ~12.7k each | 1,099 each |
| liner | - | 1,146 |
| total | 605,392 | 47,340, 12 objects, no bake, Meshy's 4K/2K textures |
3. Eyes that warped, and teeth at the hem
Problem. Even at 7,000 triangles the eyes still looked warped, and a thin slit appeared on the belly with black teeth
running down from the hoodie hem.
We offered five options for the head:
| What | Fixes | Risk | |
|---|---|---|---|
| 1. Reproject | welded decimate as now, then fresh unwrap + T8 baked onto the reduced head (2048) | the texture slide (most of the warp) | eyelid rims still collapse a little; geometry only |
| 2. Seam lock + reproject | before decimating, the vertices on Meshy's UV- island borders (eye and mouth islands) get a low collapse weight; then reproject | slide + rim collapse | needles if the lock is absolute; so weight 0.2, not 0 |
| 3. Hard-protect the features | eyes, lips, brows at weight 0, a graded ring around them, the rest of the head takes the cut; reproject | rims untouched | cheeks and jaw get coarser at the same 7k |
| 4. Symmetry | X-mirror on the decimate so both eyes collapse identically | lopsided eyes | none; stacks with any of the above |
| What | Fixes | Risk | |
|---|---|---|---|
| 5. Bigger head budget | 10k instead of 7k, nothing else | fewer collapses everywhere | spends 3k triangles on what is mostly a texture problem |
The answer was none of them. The head had already been fixed once, on R8fix, so it made more sense to study how
than to invent a new technique, and going back through every R8fix variant closely showed it. R8fix's head was
welded, decimated plainly to 6,000, unwrapped fresh and baked from T8. The bake, not a geometry trick, had fixed the
face, so the five options had been solving the wrong problem. Applied at 7,000: the face was good and the slit was
gone.
The hem teeth followed the same path. We tried overlap strips (they made a shelf: 215 belly faces facing inward
against 28 before), snapped borders, then shared border anchors every 6 mm. Then the comparison that settled it:
| Model | Why the hem was clean or not |
|---|---|
| F8 / F10 | nothing reduced; one shared edge at 605k |
| R8 / R10 | parts decimated unwelded (hem = open edge, protected) + re-baked |
| R8fix v6 | different meshes but re-baked |
| R8x / R10x | welded + original UVs + no bake: each side draws its own ~10 mm chords through the wavy hem |
Solution. Bake every part that carries skin or a face: head at 2048, belly and legs at 1024, hands at 512, fresh
unwraps from T8. Cloth and hair keep Meshy's texels. R8x final: 39,344 triangles. Everything that went through a bake
behaved; everything that kept Meshy's texels through a reduction bit us.
4. Two routes, and how each family is achieved
| Route | Steps | What it gives | What it costs |
|---|---|---|---|
| Merge first (the expected one) | Merge the T variants into one full model (T8's body, T3's hair, T12's hoodie ...) → fix the paint on that master → reduce and bake once to game size. | One master that is the truth. Every paint fix is made once, at 4K, and every later game build inherits it. | Fifteen minutes to make the master: the merge is a second UV map per donor. |
| Reduce first (the one we took) | Chop T8 into parts, reduce, unwrap, bake T8 onto the body and T3 onto the hair in the same step → fix what the bake shows (R8fix v3 to v7). | A game model straight away. The merge happens inside the bake, by pointing each part at its donor. | Every paint fix is a rule in the bake or a repaint of a 2048 map, redone per build. Nothing to paint on at full resolution until the master exists. |
| R8 / R10 | R8fix v6 / v7 | R8x / R10x | |
|---|---|---|---|
| Parts from | painted T8, split-parts test | skin from T8; hair and hoodie from M8x; shorts and shoes voxel | the F8 master |
| Welded | no | yes | yes |
| Reduce | per part, budgets | per part, budgets | per part, budgets, shared seam anchors |
| Textures | all baked (2048 body + 2048 hair) | all baked; v7 hoodie from T3 + collar inpaint | skin baked; cloth and hair keep Meshy's texels |
| Hair | T8's, cut 93% | M8x at 16k | T8's at 12k |
| R8 / R10 | R8fix v6 / v7 | R8x / R10x | |
|---|---|---|---|
| Total | 53,799 | 56,875 | 39,344 |
| Known flaw | seam cracks | inner-hood red (T8's paint) | the same inner-hood red: both use T8's hoodie |
They converge. R8fix's bake already reads two donors; reading from a merged master changes one line. The difference
is where the paint fixes live: on the master, once, or in the recipe, every time. For every character after Agent E the
order is master first.
5. Three routes for every character after her
We set the three final approaches side by side to decide which one every future character should follow.
| Route | What it gives | Cost per character | |
|---|---|---|---|
| 1 | Reduce-then-bake, one body (R8 → R8fix) | parts cut from the painted model, welded, reduced, re-unwrapped, everything baked to one 2048 body map + hair map | fewest files, smoothest skin, but every paint fix is a rule in the recipe, and cloth texels are resampled |
| 2 | Merge-first master, parts, selective bake, brush (R8x, the pick) | master = best T's merged by UV map; parts welded + budgets; skin re-baked, cloth and hair on Meshy's own texels; whatever Meshy's painter got wrong is painted by hand, by part | one more file (the paint .blend), sharper cloth, and the fixes are ours, which is the part that actually decides the look |
| 3 | Meshy remesh as the base (M8x) | Meshy's retopology for everything | clean hair and cloth for free, but the skin lost her roundness; a donor for hair and hoodie geometry, not the character |
The order for the next character: generate retextures → rank faces and hair in the window → merge the winners into
the master → cut, weld, reduce (the face never below a third of its detail) → re-bake the skin only → paint file → rig.
What this gave us
A master to paint on, a reduced model whose body was flawless at fewer triangles than any build before it, and a
written order of work. It also fixed what a merge means for us: putting one variant's part on another produces the full
model first, not a shortcut to the game.
Fixing AI paint errors by hand
7 October · Red paint on her collarbones, three automatic fixes that looked like burn marks, and the brush that replaced them ·
The day colour stopped being a rule in a script and became a stroke made by a person.
| What we built | A paint file in Blender with one-click buttons per part, a black brush and a skin brush, and a marker-and-diff workflow in which the saved strokes are the selection. |
| The challenge | T8 ran hair-red paint about a centimetre onto her collarbone skin. Three automatic fixes left marks that read as burns. |
| Key decisions | Diagnose and rank the options before acting. Colour on a face is a judgement: the eye marks, the script converts exactly the marked texels, and nothing else changes. |
| The result | R8x, hand-painted, became the branch for Agent E Streetwear: the best of the three fixes. The fringe painted into her forehead was parked. |
1. Red where it does not belong

The red: T8's inner-collar paint running onto the skin, on faces the part test rightly calls neck.
Problem. T8 paints the inside of the collar and the hood lining hair-red, and ran that red about a centimetre past the
fabric onto the collarbone skin. T1 to T5 go straight from skin to black.
Cause. Meshy's painter, not our build: the red is in T8's own texture, on the master too.
Since every T is the same mesh, the hoodie could read its paint from any of them. The choice of hoodie source:
| Hoodie source | Maps | Collar | What the 2048 bake gets |
|---|---|---|---|
| T3 | 2K | skin then black | About 900² of texels for the hoodie: slightly starved. Chosen first (v7, 106,299 faces re-pointed). |
| T12 | 8K | correct | More than the bake can use. The sharper option. |
| T8 | 4K | hair-red inside | What v6 had. |
2. Three automatic fixes, three burn marks
Fixes made straight away had been costing us builds, so we decided that from here on every fix starts as a diagnosis
with ranked recommendations, and nothing changes until one is chosen. We set out four options (rule inpaint, collar
skin from T3, hand paint, black) and the rule inpaint was chosen first.
| Attempt | What it did | Verdict |
|---|---|---|
| Inpaint | Inside a collar zone from the chest to just under the jaw, hair-red texels take the colour of their skin neighbours: 165,427 texels | Better, but not yet right |
| Inpaint on T12 | The same on the sharper hoodie: 162,489 texels | Overdone: the collarbone skin had been clean to begin with, and the fill turned it into what read as burn marks |
| Two-class fill | Skin-side red becomes skin, fabric-side red becomes black (counts below) | Botched: splitting the red into skin and black still left burn marks where the two met |
| Model | Red texels → skin | Red texels → black |
|---|---|---|
| R8fix (T3 hoodie) | 49,887 | 122,397 |
| R8fix (T12 hoodie) | 61,750 | 93,966 |
| R8x (T12 hoodie) | 20,039 | 38,911 |
Cause. Colour on a face is a judgement. A script sees texels; where a region borders two colours, any automatic fill
mixes them, and the eye reads the mix as a bruise. Agent J's hair had been fixed the same week by hand painting
(chapter 11). And we realised the split parts gave us the order a painter would use: the body can be painted on its own
first, before the jacket goes on, so a fix on the skin never touches the cloth.
3. The brush
Solution. A paint file for R8x, where every part is its own object with its own map: one-click buttons per part (paint
skin, paint jacket, paint belly, paint shorts, paint hair, show all, look at face) that hide what is in the way and put the
right map under the brush; a black brush and a skin brush; and a SAVE button. The workflow:
| # | Step | Who |
|---|---|---|
| 1 | Paint a marker stroke, in any colour, over what is wrong | Person, in Blender |
| 2 | Save | Person |
| 3 | Diff the saved map against the previous save: the changed texels are the mark | Script |
| 4 | Convert exactly those texels to the target set for that mark: flat black for the jacket strip, a shadow fading over skin at the neckline, skin colour, hair red | Script |
| 5 | Re-export into the comparison window, judged in rendered light | Script, then person |

The paint file. A marker stroke on the jacket (any colour will do), the texture it lands on at the right, and the one-click panel.
Whatever is marked becomes what we say it is.
What the passes did, in order:
| Pass | Texels | Note |
|---|---|---|
| First skin strokes | 79,546 | A colour test caught only 1,736: the strokes were cream, not white. A diff of saves finds strokes; a colour test does not |
| Flat skin fill (#FBD0C0, the median of 34,409 ring samples) | 78,987 | |
| Black strip under the jacket | 50,287 | |
| Shadow band at the neckline | 70,040 | 100% black at the strip fading to 80% read too light against the jacket, so we deepened it to 92% over skin |
| Jacket blobs | 5,476 | 3,999 specks reverted: noise from re-encoding on save |
| Jacket touch-up | 25,185 | 14 blobs |
| Thin waistband string | 13,430 | Undone: the string is so thin that the change could not be subtle, and the square edges of the brush showed, so the original paint was better left as it was |
| Hair fringes to hair red (#701D25, the median of T3's hair texels) | 10,726, then 10,278 | 26 blobs, then 24 |
Two things had to be learned on the way. The brush projects through the mesh's active UV map, so the jacket strokes
landed at random on T12's 8K map until that layer was switched. And Blender's brush colours are linear, not display
colours, so a brush set to black first painted grey.
The verdict: of the three fixes, the brush was the best. The eye put each colour exactly where it belonged,
which no rule had managed. R8x, hand-painted, became the branch for Agent E Streetwear.
4. What a brush cannot fix: the face under the fringe
Problem. Meshy painted hair colour into the forehead and brow texels. Painting it out by hand failed: the hair colour
sat in the same texels as the brows and forehead, so there was no clean skin underneath to bring back. That made us
ask how a studio artist avoids the problem in the first place:
| Them (Overwatch, Fortnite) | Us (Meshy) | |
|---|---|---|
| Head | sculpted bald, clean skin everywhere, even under the hair | one fused shell, no scalp, fringe is the forehead surface |
| Hair | separate asset: hair cards (textured strips with alpha) or groomed strands, placed over the head | part of the shell; cut off afterwards by a part test |
| Skin texture | painted in layers (Substance Painter / Photoshop): base skin, blush, brows as their own layer, makeup, dirt | one flattened image from Meshy's painter |
| Brows | a layer on the face material, or small alpha cards on the head mesh | painted texels mixed with the fringe |
| Fixing a mistake | hide a layer, repaint one layer, re-export | repaint texels by hand |
We parked it: with her hair on, it blends in at this level of quality. Two days later the whole question went away,
because her head was replaced (chapter 22).
What this gave us
A working division of labour that every visual task since has used: the eye marks, the script converts exactly what was
marked, and nothing else changes. And a rule we had been circling since chapter 12: colour is decided by a person
looking at it, in the light the game will use.
Texture atlas, first rig, source holes
7 and 8 October · Getting the hand-painted R8x to 50 MB-class size, rigging it, and finding out that the hole in her shoulder
had been in the source all along · The first time the game, not Blender, was the judge.
| What we built | One game atlas for R8x (body 2048, hair 1024), a first rig on her existing skeleton, and her shoulders rebuilt from the split-parts sculpt. |
| The challenge | The painted R8x carried 27 maps and 698 MB of GPU memory. In the game the rig webbed her shorts and stuck her hair to her elbows, and her left shoulder had a hole. |
| Key decisions | Bring every character to the 50 MB range. Judge geometry bald, from the angle in question, before anything goes on top. Revert to the 90% state instead of fixing the fixes. |
| The result | Streetwear X in the game on 8 October: 46,709 triangles, 63 MB on the GPU, 0 contract failures, the only build of her that was game ready and not broken. The hole had been in the source all along. |
1. How heavy is a character?
We noticed that Agent L, Agent J and Agent Zero looked the best in the game at the time while having the fewest
vertices, which told us that the method used to bake and reduce matters more than the count. The current Agent E
looked a tenth of Agent L's quality while using more. The game was already starting to lag and was unplayable on a
Mac, so we set the end goal of bringing every character down to the 50 MB range, not 100 MB+.
| File | Triangles | Maps | Largest | GPU texture memory | |
|---|---|---|---|---|---|
| In-game average (Agent E, Agent Zero, Agent B, Agent C, Agent S) | 22-25 MB | 25-33k | 3 | 4096² | 101 MB |
| Agent J, Agent L (baked route) | 11-24 MB | 42-60k | 3 | 2048² | 50 MB |
| Current Agent E Streetwear in game | 24.8 MB | 28,758 | 3 | 4096² | 101 MB |
| R8fix v7 as-is | 25.5 MB | 56,878 | 6 | 2048² | 101 MB |
| R8x full-res | 63.5 MB | 37,955 | 27 | 8192² | 698 MB |
| R8x, maps capped at 2048 | 26.9 MB | 37,955 | 27 | 2048² | 245 MB |
| M8 remesh, whole | 18.4 MB | 29,368 | 2 | 4096² | 134 MB |
Problem. The painted R8x carried 27 maps, three of them whole-body Meshy atlases (T12 at 8K, T8 at 4K, T3 at 2K)
of which the hoodie, shorts, shoes and hair used slivers: 698 MB on the GPU. We had picked 8K out of habit, not for a
reason: the extra resolution had never protected a build through reduction, where something went wrong every time.
Solution. A bake: for every texel of one new 2048 map, a ray reads what is painted on the part surface underneath,
whatever map that part was using. The body became one object with one fresh unwrap and one 2048 set; the hair its
own object on a 1024 set. Nothing geometric moved.
| R8x full- res | R8x, maps capped at 2048 | R8x game atlas | Agent L (reference) | |
|---|---|---|---|---|
| File | 63.5 MB | 26.9 MB | 13.9 MB | 24 MB |
| Triangles | 37,955 | 37,955 | 39,101 (27,203 body + 10,752 hair + 1,146 liner) | 41,851 |
| Maps | 27 | 27 | 6 (body 2048 ×3, hair 1024 ×3) | 3 |
| GPU memory | 698 MB | 245 MB | 63 MB | 50 MB |
What it cost: texel density. The hoodie went from a fifth of an 8K atlas to a quarter of a 2048; the head from its own
2048 to a fifth of one; the hair from 2048 to 1024. We judged the loss acceptable for the memory it saved, with one
bake miss, a single dot filled from its neighbours. Below 50 MB is not a modelling job: every route ends at one 2048
set. GPU-compressed textures would be the next floor (chapter 23).
The three game-size options as they then stood:
| R8fix (baked; skin from the painted cut, hair and hoodie from the Ultra remesh) | R8x (parts from the master, cloth on Meshy's texels, hand paint, atlas bake at the end) | R8fix + M8 clothes and hair (Meshy's remesh for every part that is not skin) | |
|---|---|---|---|
| In game | 57k triangles (budgets can lower it), one 2048 set, 50 MB. Hair separate, liner: the chains work. | 39k triangles, one 2048 set, 50 MB. Same hair and liner. Lightest of the three to skin. | About 45k triangles, 50 MB. Same. |
| R8fix (baked; skin from the painted cut, hair and hoodie from the Ultra remesh) | R8x (parts from the master, cloth on Meshy's texels, hand paint, atlas bake at the end) | R8fix + M8 clothes and hair (Meshy's remesh for every part that is not skin) | |
|---|---|---|---|
| Look | On par. Everything resampled once by the bake. Fixes are rules, and every rule fill was a burn mark. | The one we picked: sharper cloth texels, the strokes exactly where the brush put them. | Meshy's topology never tears. Not as smooth and even, but it holds up at low counts. |
| Modelling, per character | One command, about 8 minutes, no painting. Needs retextures, split parts, an Ultra remesh. Risk: a rule misfires where nobody can see it until the build is done (the collar: eleven builds). | One command, about 2 minutes, plus the brush (Agent E: about an hour), plus a 5- minute atlas bake. No remesh. | As R8fix, plus a remesh per character (Ultra: about 44 credits). |
| Other characters | The most automatic: a batch could run overnight. Right for placeholders and bots. | Needs an eye per character. That eye is what made Agent E the best of the three. | Credits per character; otherwise R8fix. |
| Where it fails | Colour. | Our time. | Meshy's bill, and skin. |
The verdict: R8x for every character that matters, R8fix as the batch route, M8 as a donor when a part tears under
reduction. On credits, a 44-credit remesh is small next to the budget we set: 500 Meshy credits per character without
review, and 1,000 at the cap. We still decided not to force a remesh on every character, because it takes time and is
clunky; it earns its place as an option to try when a part is not working.
2. The first rig: copied, not solved
The atlas went into the game on her existing skeleton (52 bones plus a 5-bone hair chain), with weights copied by
nearest surface from the old Streetwear mesh. The file contract passed: 13 rules, 0 failures. The game showed what
Blender had not.
| What we saw | What it was |
|---|---|
| Shorts joined between the legs | Shorts vertices at the crotch took weights from the nearest old surface, which there is often the other leg; posed, each leg pulls the fabric toward the other: a web |
| Hair stuck to both elbows | Strands hanging beside the arms in the rest pose took arm weights |
| Inner collar lip jagged, fine in Blender | Adjacent lip vertices alternate between head, neck and spine weights; the idle pose turns the head and the lip zigzags |
| The paint looking like hot glue | The strip and shadow are black in the colour map, but the roughness and normal underneath are still skin, smooth and glossy. Black + glossy reads as wet |
The first three fixes worked: hair weights only from the old mesh's hair, the hoodie rim rebuilt with its dense border, the
shoes baked into their own maps. Then a smoothing pass on every weight, and she came out looking cracked, skin
and clothes both broken. That taught us the lesson that set the rule for the rest of the week: all three models had
started with few problems, about 90% there, and each round of fixes took them down to 80%, 70% and lower, because
every fix created new problems to mend. So we look for the root cause and prevent it in the first place, instead of
mending problems one by one. We reverted to the 90% state.
3. The hole in her shoulder
Problem. In the game her left shoulder had a hole. Three rig passes chased it (a plain transfer, hair from the old hair
only, a per-side transfer with smoothing). Then two geometry guesses: hair winning the tie in the part test (a rule
returned 7,552 faces from hair to body), and the thin double layer of the hood collapsing in the decimate.
Cause. Found by a different test: we put a bald Agent E in Blender, from behind at 45 degrees, three sources side by
side. With the hair out of the way, the F8 master clearly had holes in it.

The painted master with the hair hidden. Under the hair, the hair was the only surface Meshy ever painted.

The split-parts mould, bald. The shoulders exist here: as a flat plate stuck on the hoodie piece.
| Source, hair hidden | Rear shoulders |
|---|---|
| F8 painted master (605k) | hollow, both sides |
| R8x, M8x (cut or remeshed from it) | hollow |
| Split-parts sculpt: hoodie piece + arm pieces | closed (a fill plate on the hoodie; the arm pieces are only forearms and hands) |
We had earlier ruled out holes in F8 from a thumbnail, and a thumbnail with the hair on could never have shown them.
Every build cut from the painted shell inherited the hollow.
Solution. Hoodie and both arms from the sculpt pieces: a 2 mm voxel remesh first (the raw solid is double-layered at
the hood and a plain decimate tears it), then three ray tests per face. Paint above within 8 cm: keep. No paint above
and more than 4 mm from any paint: the closing cap, dropped (3,837 hoodie faces). Paint above is hair: keep and fill
flat, hoodie black or arm skin.

The rebuilt shoulder in the game, before the brush: no hole, but a blotch of red and black.
Better than a hole, but still not passable. The design had always been a backless top with a hoodie attached to the
neck, so the rebuilt shoulder had to read as skin above the sleeves and as black only on the hood. So the last step
was the brush again: the strip above each sleeve marked as skin (11,072 texels), the hood back as black (122,840
texels, the reddish ones refilled from genuine black), and a live slider on the skin patch to set the tone by eye.
That passed, and we moved on: this version of Agent E was the only one that was game ready and not broken. In the
game on 8 October: 46,709 triangles, six maps (body 2048, hair 1024), 63 MB on the GPU, 16.5 MB file, 0 contract
failures.
4. The order that worked
| # | Step | Why |
|---|---|---|
| 1 | Bald body in Blender, from the angle in question | The hair hides holes; a thumbnail hides everything |
| 2 | Bald body in the game | The game's lights and pose show what Blender's rest pose does not |
| 3 | Approval by eye | A bald, coloured body with no holes on the shoulder has to be seen before a build is trusted: the file contract had passed with the hole in it |
| 4 | Then the hair goes on | Hair looks like a mush even in Fortnite and Overwatch, so we accept it there. What the eye reads is a smooth body and a sharp face, and clothes must not have literal holes |
What this gave us
A 50 MB-class target for every character, the first rebuilt Agent E in the game (Streetwear X), and two rules that held
from then on: a rig is judged in motion, and a hole is looked for in the source before it is blamed on the stage that
revealed it.
Voxel remesh fills: the monster neck
8 October · A jagged collar, the day we wrote Rule One, two whole new routes (B and C) built and rejected in an afternoon, and
Meshy's own remesh made into the final figure · About forty named builds in one day.
| What we built | Collar welds v1 to v7, two new routes built and rejected in one afternoon (B, one voxel shell; C, A fused around its own head), and M8x made into a finished figure with B7's back and new hair. |
| The challenge | A jagged collar and a sticker on the hoodie that renders could not find, then a body that was perfect except for a monster neck. |
| Key decisions | Rule One: the eye marks, the script measures. Build the scene and let a person mark the spot. Check every build before showing it. No fill pieces. |
| The result | B7 kept as the benchmark body, M8x in the game as a skin (43,563 triangles), and the cause of the monster neck: the voxel remesh fused the hood's capped opening into her neck. |
1. A sticker and a jagged lip

Streetwear X on the character select screen: the shoulder hole gone, the front collar lip jagged.

The sticker: a flat patch on the black jacket at the shoulder.
Problem. The shoulder hole was fixed, but looking at the model we could still see a flat patch on her hoodie (the
sticker), the front of the hoodie near her neck looked like botched black remnants scattered over it, and the front
hoodie lip was jagged.
Cause. The hoodie swapped in from the sculpt kept its inside hood walls: 564 faces, about 50 cm², standing 1 to 2 cm
off Meshy's painted surface, and a second collar lip about 2 cm outside the neck piece's own. We had checked the
swap only from behind.
2. The day we wrote Rule One
We then spent half an hour hunting the sticker with renders, ray tests and 24-angle grids. A person looking at the
model found it in seconds. That set a different way of working for anything visual: the eye finds the problem and marks
it, and the scripts measure and fix exactly what was marked.
It went to the top of our working rules the same minute, above Rule Zero (chapter 06). From then on a visual problem
starts with a scene: the model loaded, framed and lit, with a red marker brush and pins, and the spot is marked by the
person who can see it.
3. The collar, welded
With the marks to aim at, the collar took seven versions in four hours.
| Version | What changed | Measured |
|---|---|---|
| v1 | The 564 front hood walls cut | 12,436 hoodie faces left |
| v2 | 83 edge points tucked 1.5 mm under the neck piece, 67 eased | Gap at the marks: median 3.4 mm, widest 7.5 mm before. Smaller, but we could still see the gaps, so tucking alone was not enough |
| v3 | Every hoodie collar edge laid on the neck piece, sunk 1 mm | Seam: median 3.8 mm, widest 14.9 mm before; 1.0 mm and 1.0 mm after |
| weld | Welded at the step where the parts merge | 432 edge points welded; 70 not, all on the hood rim more than 8 mm away |
| v4 | 672 open-point pairs merged within 3 mm near the strokes | Open points within 1 cm of the strokes: 1,308 before, 368 after |
| v5, v6 | Texture rebuilt, 7 neck holes filled, the crusty surface relaxed (437 points, 26,702 texels) | The smoothing opened see-through slits: rejected |
| Version | What changed | Measured |
|---|---|---|
| v7 | v4's shape, only the surface map flattened, the red band behind the head replaced by the sculpt's own hair piece | Better, but we did not judge it a commercial-grade finish, so the collar was not yet the end |
It had never been welded, and that was an architectural choice. Eleven parts, each welded only within itself; the
seams were held by 6 mm border anchors, which gives two matching edges, never one joined edge. The anchors had
then been switched off for the hem, and the mould's hoodie came from separate sculpt geometry that shared no seam
at all. A weld belongs at the step where the parts merge.
4. Route B: everything in one shell
The joins we expected, since this was a multi-part build, but we counted far more flaws than the joins could explain.
The causes: Meshy's shell has nothing under the hair; a 13x cut (605,000 to about 46,000 triangles) tears thin areas;
parts from mixed sources were never made to meet; and fixes by rules, made without seeing. We concluded that
colour was the easy part, because Agent E's colours are quite straightforward, and that the flaw was the geometry, so
the next route had to rebuild the geometry rather than keep repainting it.
| Route | What | Estimate |
|---|---|---|
| A | Keep patching this build: the sculpt's hair piece behind the neck, back to v4's hoodie shape, fix each new hole as it is marked | Open-ended |
| B | Voxel-remesh each sculpt piece into one closed shell (body, hoodie, shorts, shoes, hair), reduce evenly, paint flat colours, bake only the face, eyes, hair and drawstrings | Optimistic: one session to a bald body. Conservative: two to three sessions to the game |
The optimistic estimate held. B1 was the sculpt pieces fused into 36,000 triangles with 0 open edges. B2 baked
colour from A (only 242 of 36,000 faces had no source within 1.5 cm). B3 took its surface detail from a 494,000-face
clean shell and its face from the original 4K head. B5 and B7 pulled the neck back onto the painted original (110 points,
then 13). So far the voxel remesh had done well: we found B superior to A in many ways, including the texture on her
jacket from behind, which told us the route was worth finishing.
B7, about 50,000 triangles, rigged: a body we judged perfect except for one fault, the neck. It had
swollen into a monster neck that no hand work could fix and no number of revisions would
save, so we stopped revising it and went after the cause.
First diagnoses. The fused shell; then the hoodie collar filling the space. B8 (the hoodie as its own shell) came
back with a black face; B9 (B7 with A's head and neck) with a face badly damaged by many large triangles.
Cause. Meshy's hoodie piece is a closed solid whose neck hole is sealed with a flat cap. The voxel remesh fused that
cap into the body and turned it into neck: her neck came out at the diameter of the hood's opening, 54 to 70 mm
against A's 30 mm, two to three times the original diameter.
Route C was the other way round: A without its head, the whole body fused, A's head put back. Its neck was 35 mm,
close, but A's open parts became 2 mm double-walled slabs that a reduction cannot collapse: about 150,000
triangles, a mess of shards.
| Check | C3 | A |
|---|---|---|
| Neck radius | 35 mm | 30 mm |
| See-through spots | 169, mostly at the sides of the face where A's head was cut | 20 |
| Body triangles | about 150,000 | 36,000 target |
Three builds (B8's black face, B9's torn face, C3's shards) had been shown without being checked first, and each one
wasted a review on a fault a basic check would have caught. The rule since: every build is checked before it is shown,
and a failed build is deleted, not displayed.
The lineup at the end of the afternoon, every iteration on display:
| Model | Status |
|---|---|
| A | Route A v7 |
| B7 | One shell, monster neck |
| B8 | Hoodie detached; face bake is black |
| B9 | B7 with A's head; holes at the neck and face |
| B10 | Neck hole opened, but the neck is still 54 mm and the face is black |
| C1 | A fused into one shell, about 150,000 triangles |
| C2 | C1 with the inner walls stripped; torn |
| C3 | A fused, about 150,000 triangles; shards |
| C4 | A second attempt to get C3 to game size, still about 150,000 triangles |
| M8 | Meshy's remesh, 29,000 triangles. Static: not rigged, hair is part of the mesh |
| M8u | Meshy's remesh with unwrap, 41,000 triangles. Static |
| M8x | Meshy's Ultra remesh, reduced to game size: 48,000 triangles, maps baked at 2048. Static |
B and C were dropped except one: we kept B7 as our benchmark for a perfect body with a failed head and neck, with
the method that made it, so that later bodies have a clean one to be measured against.
5. Meshy's remesh as the final figure
That pointed us to a simpler option: use M8x, M8u or M8 as the end figure. Meshy's remeshes had none of the flaws
we had spent the day chasing, so the only repair we expected was their hollow shoulder. Three steps made it work.
The hair split, without fills. Every face of the remesh goes to its nearest sculpt piece; where hair and body
overlap within 1.5 mm, the body keeps the face. The first version filled the hollows under the removed hair from the
fused shell, and pasted pieces of B's monster neck in front of her neck.
| Model | Body | Hair | See-through spots, with fill | Without fill |
|---|---|---|---|---|
| M8 | 21,400 | 9,700 | 339 | 59 |
| M8u | 28,900 | 13,700 | 337 | 65 |
| M8x, at game size | 33,400 | 16,200 | 310 | 46 |
The see-through spots came from the fill piece itself, a step our own rules had added, and it was also what carried B's
monster neck into the remesh. Without it the spots fell from over 300 to under 70 on every model, so we dropped it
for good: no fill pieces.
B7's back. We wanted the remeshes' backs to match the B variant's, because B7's back was the cleanest torso we
had; only the torso back, though, not the head or neck, which were exactly where B had failed. B7's faces from the
waist to the shoulder tops replace the remesh's, grown 18 mm so they tuck under its edge, and welded. Seen from
behind, the graft showed no seams. Open points left on the back: M8 43, M8u 38, M8x 26.
New hair. A fresh 2K remesh of T3 (M3, 10,121 triangles), fitted to the sculpt within 0.17 mm. Next to the old hair the
difference was night and day: redder, fresher and far less distorted, so we put it on every model.
| Model | Part | Vertices | Triangles |
|---|---|---|---|
| M8x | Own body and hoodie | 25,075 | 27,954 |
| B7 back and shoulders | 4,428 | 5,329 | |
| B7 face-material bits | 236 | 159 | |
| Body total | 29,739 | 33,442 | |
| M3 hair | 12,470 | 10,121 | |
| Whole model | 42,209 | 43,563 |
We checked B7's back and shoulders and the M8 and A bodies and found no holes or cracks in any of them, so we
kept them as parts for later builds instead of discarding them with the routes they came from.
6. The hair in the game
Problem. In the game the hair looked glued to her shoulders and clipped into them when she looked up.
Cause. Measured: the hair over the neck and shoulders was 38% bound to the head bone, so it turned rigidly with the
head; and Agent E used the shared hair settings, which tie the lower hair 70 to 85% to the spine with 4 to 12 degrees
of swing.
Solution. Weights by height down the chain (root 1.39 m, then 1.22, 1.05, 0.89, 0.73), the front locks on the chest, and
her own hair settings: spine follow 8 to 50%, swing 6 to 26 degrees, more fall, a wider body capsule. Clipping in a
sprint was left for a later patch.
That night the hood's blood-red stain was painted black, a dark pimple on M8x's nose was found by hand-painted skin
dabs where our scan of 433 texels had found nothing and overwritten with a 231-texel disc of surrounding skin, and
5,589 peach texels in her hair went red. A face paint that wiped her brows made her look like a different person, so we
undid it: keeping her likeness mattered more than an even skin. M8x went into the game as a skin (43,563 triangles,
contract 0 failures).
What this gave us
Rule One. A benchmark body (B7) and the method that made it. The cause of the monster neck, which shaped every
voxel step after it. And the realisation that closed the day: M8x's mesh had too many scars to be the end, and a clean
finish would have to start from more detail, not from a reduced remesh. The next morning we went back to the full-
resolution sculpt (chapter 21).
Full-res body: hood, shoulders, normals
9 October, morning · The T8 sculpt at full detail as the body, B7's back grafted on, a hood top that had never been visible, and a
shoulder that took eleven attempts · The first day much of the shaping was done by hand in Blender, with tools built for the
job.
| What we built | Option 2: the T8 body at full resolution (318,734 faces) with B7's back, a voxel-banded hood top, and hand tools inside Blender: an eraser, sculpt brushes, heal and paint, point edits. |
| The challenge | A smoothing step that left her crusty with holes, a hood top no earlier model had ever shown, and a right shoulder that stayed lumpy through eleven attempts. |
| Key decisions | Every join is made at full resolution and reduced once, at the end. Never smooth or delete by a region rule without saying so first. Hands in Blender do the shaping. |
| The result | The body every later build stands on. The shoulder's real causes were stored normals, a stale normal map and a skin tone a few values off; the left shoulder mirrored onto the right finished it. |
1. Why go back up
We had meant to use the T8 mesh entirely. M8x's mesh had too many scars to finish from, and we wanted a proper
high-definition weld between the head and body made before anything was reduced to game size, while there was
still full detail to weld. The plan, in four steps: the T8 body; B7's back for the shoulders; the paint (shoulders, hood,
inner collar); then a new high-resolution head fused on. From then on we welded at full resolution only: every join is
made at full detail, and reduced once, at the end.
The look of the hoodie was a choice too. Next to A's, B7's hoodie looked glossy and high-resolution, and we traced the
difference to its settings:
| Setting | A and M8x's own hoodie | B7 |
|---|---|---|
| Roughness | Meshy's roughness map, average 0.75, rougher in patches | One flat 0.70 everywhere |
| Surface-detail map | Baked from Meshy's painted shells | Baked from B7's own clean shell of about 494,000 faces |
| Geometry | Cut and reduced parts | Smooth voxel shell |
| Metallic | 0 | 0 |
We chose B7's surface. After step 3 the body stood at 319,000 triangles with B7's back welded on (51 points), the
hood stain painted out (61,898 texels) and the collar repainted (13,039). Everything looked as good as it once had, but
the new lighting showed us flaws the old lighting had hidden: the shoulder skin geometry needed some smoothing.

Step 3 from behind, in the new lighting: the hood and the rear shoulder where B7's back meets T8's skin.
2. Step 4: the smoothing failure
Problem. Step 4 put a new head on (the full-resolution head from a second Meshy generation of Agent E, with its
hair) and smoothed the shoulders on the way. We saw the same crusty look this smoothing method had produced
every time before: parts of the model botched, with holes everywhere. That told us the step had done more than it
said, so we stopped and traced exactly what it had changed before going any further.
Cause. Step 4 moved 105 shoulder points halfway toward their neighbours, eight times, with no blend into the fixed
edge; and its neck cut deleted every face within about 6.5 cm of the neck centre above the slice without checking
what it hit, which could bite the hood and collar.
Solution. Back to step 3. The rule went into our lessons the same minute: never smooth or delete by a region rule
without saying so first.
3. A hood top nobody had ever seen
We could see jagged lines along the top of the hood. We wanted it smooth, so that the hood would read as intact and
in shape even with no head inside it. We had no reference to work from: none of our prior models had shown that area,
because hair had always covered it.
The first diagnosis blamed the old hair cut. Measured by angle, the T8 hood rim was level; the ragged edge was the
top border of B7's graft, about 1.5 cm above and 3 cm below the rim. Two options:
| Option 1: plane cut | Option 2: voxel band | |
|---|---|---|
| What | One tilted plane just under the deepest dip; only hood faces above it deleted, never skin; a short lip folds down and in, painted hood black; no existing point moved | The band behind the neck copied out, given fabric thickness, voxel-remeshed at 6 mm (B7 and T8 layers merge into one shell), smoothed on its own, laid back over the band |
| Option 1: plane cut | Option 2: voxel band | |
|---|---|---|
| Estimate | 30 min optimistic, 1.5 h conservative | 45 min optimistic, 2.5 h conservative |
| Faces removed | 4,371 | 2,496 |
| Faces added | 630 lip faces | 2,916 shell faces |
| Most height removed | about 6 cm at the back | none; the shell tops out at the old height |
| Triangles | 316k | 322k |
Option 2 won by far visually, so we took it. Option 1 had a practical case, since a lower hood minimises clipping, but it
left visible gaps and holes; we kept the method for places where clipping matters more than looks. Option 2 was
almost acceptable: a few bumps were left on the sealed hoodie, and judging those by eye called for an eraser rather
than another script.

Option 2's hood top: an even voxel mesh, with a few stray spikes along the edge that had to be pressed down, not deleted.
4. Tools for the hands
From here much of the work was done by hand, with tools built for the job inside the same window:
| Tool | What it does | Why we needed it |
|---|---|---|
| 3D eraser | Circle-select faces, delete, done; every erase saved as a timestamped copy | The few bumps on the sealed hood were easier to judge and remove by eye than by any rule |
| Sculpt smooth, flatten, inflate, relax | Blender's sculpt brushes, one click from the panel | Smoothing worked well, but a few stubborn spikes survived it and needed brushes that act only where they touch |
| Tool | What it does | Why we needed it |
|---|---|---|
| Heal (clone, blur, smear) and paint | Texture brushes with a colour picker | Texture flaws needed a healing brush like the one in Meshy, so they could be painted out from the colour around them |
| Edit points, smooth selected, collapse selected | Point-level edits without opening holes | Spikes that left a hole when deleted |
| Shortcut tables | Select, dissolve, merge, hide, orbit, in plain words | Hand work in Blender runs on its shortcuts, and they were new to the hands doing it |
Of the brushes, flatten was the one that fixed the hoodie. Red remnants on the inner hoodie were marked with the heal
brush as a selection and only the red texels inside the strokes turned black: 2,384 of 19,016, then 1,262 of 36,047,
everything else put back exactly. The shorts hem glued between the legs was deleted by hand, and the microscopic
holes it left were closed with one face per hole in flat denim colour, after a first patch in skin and red was rejected (we
wanted it blue and white like the jeans, so the patch would disappear into the denim) and a second used too many
triangles.
One bug cost paint before it was found: the SAVE button wrote the new PNG but packed the old texture into the
.blend, so every reopen lost the latest strokes.
5. The right shoulder, eleven attempts

Where B7's graft (left, coarse) meets T8's skin (right, dense): about 10 mm triangles against 2 to 3 mm.
Problem. The back of her right shoulder looked lumpy next to the left, even after hand sculpting.
| Attempt | What it did | Verdict |
|---|---|---|
| RS1 | Each coarse graft face split once; new points on a smooth curve; no existing point moved (327,508 triangles) | All three rejected: more density did not make the shoulder look smoother, a first sign that the shape was not the cause |
| RS2 | Split twice, close to the density of the rest of the delts (337,168) | |
| RS3 | RS2 plus gentle smoothing fading to zero within 2 cm (8,144 points moved) | |
| TS1 | Smoothing without shrinking: 831 points, typically 1.7 mm, at most 9.5 mm | Rejected: no visible improvement, so smoothing was not the answer either |
| TS2 | Split twice first, then gentler: typically 0.3 mm, at most 3 mm | |
| Normals + tone | Stored custom normals cleared; the graft's old normal map unplugged; the graft's skin matched to T8's (1.5 less green, 7 less blue, out of 255, over 1.44 million texels) | Good: the first change we could see working, which pointed at shading rather than shape |
| Mirror, 2 cm fade / 0.75 cm / none | The left shoulder mirrored onto the right: 3,186 points, typically 0.6 to 1.1 mm, at most 5.2 mm, no new holes | Clearly better; we kept the 0 cm version, a straight mirror with no fade |
Cause. Not the geometry. Our first three diagnoses were the voxel band, a hidden second layer, and a shadow
artefact. The real ones were three: stored custom normals that never update after sculpting, a normal map baked for
B7 still plugged into the graft, and a skin tone a few values off.
| Side | Graft skin (RGB) | T8 skin next to it |
|---|---|---|
| Her left | 253, 213, 205 | 253, 210, 194 |
| Her right | 253, 212, 200 | 253, 211, 195 |
Hand sculpting had helped too, with a cost the numbers explained: flatten and smooth shrink convex shapes, so the
shoulder lost some roundness. The mirror kept the left shoulder's roundness on both sides.
6. Working safely on someone else's hands
| Rule | The incident |
|---|---|
| Never close a window that shows unsaved changes, whatever our checks say | A comparison of faces and paint called two saves identical; a sculpt only moves points. A shoulder sculpt was nearly lost |
| The window always loads the newest save | A rebuild started before the latest save landed |
| Buttons look objects up by name at the moment of the click | Ctrl+Z invalidated stored references: "error smoothing" |
| Keep the figures 1.2 m apart and each in its own slot | Spacing cut to 0.8 m, then 1.0 m: hands overlapped and one figure landed on another's spot |
| A rule-based cut before a face-by-face search | Rebuilding the head's missing chin by matching 322,000 faces one by one ran past ten minutes; the rule cut took 14 seconds |
What this gave us
Option 2: the full-resolution body, 318,734 faces, with an intact hood top, closed shorts and two matching shoulders,
on which every later build stands. And a different kind of collaboration from the one this part started with: by the
afternoon of 9 October the hands in Blender were doing the shaping, and the scripts were doing what hands cannot
(measuring, mirroring, matching tones to the value).
Welding a new head onto a body
8 and 9 October · A new head for Agent E, welded to the body so that face and neck read as one person · More than a dozen
named welds on two models, scored out of ten along the way, until W7 became the new benchmark for Agent E.
| What we built | A head transplant from a second Meshy generation, welds W1 to W7 on the full-resolution body, a solved neck width, one skin tone, and a choker variant (W7C). |
| The challenge | The 2 cm of neck where two different models must become one surface: botched chins, mismatched rings, wrinkles, and a blotch that five fixes could not find. |
| Key decisions | Cut both necks flat, then bridge. Solve the neck width as a number instead of tuning it. The body keeps its tone and the head moves halfway toward it. |
| The result | W7, 475,573 triangles, became the new benchmark for Agent E. The weld method is written down step by step. |
1. Why a new head
The face under the fringe could not be repainted (chapter 18): Meshy had painted the fringe into the forehead skin. A
second Meshy generation of Agent E came with a cleaner head, its own hair, and its own maps. Rather than repaint a
face, we transplanted one: her head and all her hair as one piece onto our body, cut at mid-neck. The hard part was
never the head. It was the 2 cm of neck where two different models have to become one surface.
2. First attempts, on the reduced model
The first transplants went onto M8x at game size. Each failure taught a constraint:
| Attempt | What happened | What it taught |
|---|---|---|
| Hair only | Her hair fitted onto M8x's head by position and turn; it pointed wrongly about its pivot, and we learned that the dark band under the crown is part of the hair, so removing it had been a mistake | Move the head and hair together, as one piece |
| Head + hair, cut at mid- neck | The old chin stayed floating in front like a mask, and the head had a hole: her scalp is painted as hair, so separating hair from head opened it | Remove the whole old head; never split her scalp from her hair |
| Neck join v1 | Zippered two jagged rings 5 mm apart, so it folded back on itself | Cut both necks flat first, then bridge |
| Neck join v2 | Its flat cut sliced through her nape hair, and its rings grabbed collar edges | |
| Neck join v3 | M8x's neck cut flat at 1.43 m, hers at 1.462 m through skin faces only; Blender's Bridge Edge Loops across the 3 cm gap, 4 cuts: 1,136 bridge faces | Better, not perfect: for the first time the topology read as a neck instead of random shards, which told us flat cuts and a bridge were the right route |
| Neck ring | Radius | Height |
|---|---|---|
| M8x's neck | 31 mm | 1.455 m |
| Her neck | 38 mm | 1.450 m |

A bridged neck before recolouring: each new face reads its texture from wherever its corners landed. The fix was one neck-skin
texel for every bridge face.
The same method at full resolution (474,578 triangles) was so clearly better than the reduced versions that there was
nothing to compare, and we deleted them. That was the moment the rule became: welds are made at full resolution,
and reduced once, at the end. Two more full-resolution welds built on the step-4 body (n5a, n5b) were rejected, and
we set a rule that has held since: a failed model is deleted and cleared, never put on show.
3. W1 to W7
The real welds were made on Option 2 (chapter 21). Each was scored.
| Weld | What changed | Verdict |
|---|---|---|
| W1 | Head's neck cut flat at 1.458 m to drop the choker, body's stump at 1.450 m, the two rings (214 and 164 points) joined by one row of neck skin | 7/10: a point off for lines left from the choker, two for a chin patched with filler. The chin had been botched in every weld so far, which told us it had to be protected before the cut rather than repaired after it |
| W2, W3 | The cut follows the neck's shape, not a width: only surface within 5 cm of the measured neck centre. The chin masked (16,599 downward-facing front skin faces protected, the same count before and after). Cut above the choker's highest point all round. Open edges near the neck 262 → 10 | 8/10: a point off each for a misaligned neck and for remnants sticking out of the body's neck. But cutting above the choker solved the choker problem, and masking the chin kept it intact |
| W4 | One missing chin triangle (already missing on Option 2) restored, with 26 other single dropped faces; a Neck width slider, BODY ON / OFF, the loose head editable | The same weld, ready for hand work |
| W5 | Rebuilt from the head as trimmed by hand (Neck width 0.139, size 0.91, 1.5 cm back, 1 cm down); only 942 faces left below the cut | 9.5/10: no major problems and no holes. But the body's neck and the head's neck did not meet at the same circumference, so a noticeable bump showed when zoomed in: what was left was to match the two rings 1:1 |
| Weld | What changed | Verdict |
|---|---|---|
| W6 | The head's neck stretched to match the body's ring (532 points, typically 0.4 mm), seam points reset; skin toned to the face | The diameters now matched, but the stretch left the surface scarred and rough: better, though not yet game ready |
| W7 (W7s) | The neck width solved instead of stretched: 0.08 gives a 31.5 mm head neck at the join, equal to the body's (0.139 had given 31.0 mm), then the same smoothed stretch, up to 2.3 mm | The best weld by a clear margin: matching diameters and a smooth surface at once |
W6's wrinkles, measured. The head's neck was more wrinkled in the band just above the join, and we measured
why. The body's ring is jagged and slightly oval and off-centre; the head's is rounder. Stretching each point to its
partner gave neighbours different stretches.
| Unevenness between neighbouring points | First W6 | New W6 |
|---|---|---|
| Typical | 0.13 mm | 0.05 mm |
| Worst 5% | about 1.2 mm | 0.41 mm |
| Largest | 2.93 mm | 1.70 mm |
Better, and still not right. A hand fix on W6 was tried and abandoned: nothing good came of it. The better route came
from the cause itself. The stretch only existed because the head's neck had been sized down and then stretched back
up, so it made more sense to compute the neck width that sizes it properly in the first place than to keep smoothing
the stretch. The neck width was solved, not tuned.
| Model | Neck | Stretch |
|---|---|---|
| W6 | 0.139 + smoothed stretch | up to 2.7 mm, typically 0.5 mm |
| W7 | solved 0.08, no stretch | none, so where the shapes differ, the seam row leans in or out |
| W7s | solved 0.08 + the same smoothed stretch | up to 2.3 mm, typically 0.3 mm, for a 1:1 diameter |
W7s became W7. Methods we considered and declined, for the record: matching vertex counts then bridging (the
bridge already joins 214 to 164), merge-by-distance everywhere (her fringe touches her chest, and merging everything
would glue the two together, where they must stay separate), a Boolean union (needs watertight shapes), and a
normals transfer (kept as a last polish only).
4. One skin tone
Her body was too pale beside her face, so we set the target for one skin tone: somewhere between the two, or the
face's own colour.
| Measured on | Skin colour (0-255) |
|---|---|
| Face | 229, 171, 160 |
| Body before | 253, 209, 194 |
We moved the body toward the face, because the face carries identity (a face paint the night before had made her
look like a different person): a multiplier on 3.16 million body texels and 1.44 million graft texels. The tone came out a
95% match, close enough to call the tone itself solved. But the fix made a new fault: a dark band at her neck that read
as a choker. The fade across the join had left the seam row darker than both sides.
| Height | Red channel |
|---|---|
| 1.430 m, body | 230 |
| 1.440 m | 235 |
| 1.450-1.454 m, seam row | 239 |
| 1.456 m, head side | 240 |
| 1.470 m | 247 |
| 1.478 m, head | 249 |
That table is after the fix. The rule it left: after any colour fade, measure the colour by height in 2 mm steps.
The final W7 took the other direction, the one later chosen for the choker variant: the body keeps its own tone, and the
head goes halfway toward it with a soft ceiling, so the face's bright channels do not clip.
| W7 | Skin colour (0-255) |
|---|---|
| Body | its own original tone, 253, 209, 194, untouched |
| Head | halfway from the face toward the body |
| Neck, body side up to the head side | 254 down to 248, smoothly, with no step at the join |
W7 became the new benchmark for Agent E: the model the later builds are measured against.
5. The choker variant, and a blotch that was not paint
W7C is W7 with a black choker band from the new head. Its first versions moved the choker (24.5 mm higher; then
25% wider, 98% of it outside the neck), and then three faults came back at once: a face paler than the body (its red
channel had to rise 10.5% and clipped), 92 hair-red patches on the neck, and a choker top standing 11.5 mm off the
neck. Those three were fixed. The fourth was not: the neck between the choker and the head stayed blotchy, and we
wanted it one even colour.

The neck above the choker, lit: two tones in patches.
We flattened the colour (30,842 texels to one tone, then 37,653), replaced the stored normals on 4,035 faces,
smoothed the choker's top edge (spikes 19.6 mm to 0.6 mm). None of it changed how the neck looked, which was the
first sign that the fault was not in the colour at all. Then the screenshot that settled it:

The same neck with no colour at all, solid view: the marking is still there. Not paint, not roughness, not UVs. The mesh.
| Neck patch | Triangle size | Bend between neighbours | Looks |
|---|---|---|---|
| Large triangles | about 4 mm | about 2.6° | the smooth, darker patch |
| Neck patch | Triangle size | Bend between neighbours | Looks |
|---|---|---|---|
| Tiny sliver triangles | about 1 mm | 7 to 20° | rough |
Fix after fix had missed, so we stopped fixing and stepped back. If the fault was in the mesh, more colour work would
never reach it, and the answer might be a different weld altogether. We set out the options:
| # | Option | Trade-off |
|---|---|---|
| 1 | W7 plus a choker. Take W7 with its approved smooth neck and add the choker from the untrimmed head, fitted by height. No new neck work | Our pick, and the one chosen |
| 2 | Rebuild W7C's neck strip as an even mesh with a flat tone | A visible line at the jaw is likely |
| 3 | Re-weld W7C the W7 way, choker back on top | More work for the same result |
| 4 | Graft W7's neck from the jaw down | Fiddly, gains nothing over 1 |
The final band: cut from the original head on two fitted planes, sitting 2.5 mm outside the neck with its edges folded
in, moved 1 cm higher, with its own black material (painting it into the shared head texture would have blackened 119 of
W7's own neck faces).
What this gave us
W7, the benchmark. A neck join method that is now written down step by step (cut by shape, mask the chin, solve the
width, fade the tone, measure by height). And the strongest case yet for Rule One: the blotch that five fixes could not
find was obvious the moment a person looked at it with the colour switched off.
Game budget, KTX2 and skinning spikes
9 October, afternoon to midnight · W7 at 475,573 triangles taken to 47,557 without touching her face, rigged, compressed for
the graphics card and put in the game as v0.38 · The last reduction was done by hand.
| What we built | W7 game: budgets per part, a hair-blob fix, a hand reduction with a REDUCE SELECTED button, a rig on her existing skeleton, and a KTX2 texture loader the whole roster can use. |
| The challenge | Take 475,573 triangles to game size without touching her face or opening holes, then fit her textures on a 6 GB card. In the game, spikes came out of the ends of her hair. |
| Key decisions | The face is never reduced. Hair is found by its texels, never by position. Character textures ship compressed for the graphics card. A rig is measured in motion. |
| The result | Agent E in the game as v0.38: 47,557 triangles, textures down from 213 MB to 53 MB on the GPU, and nothing below 1.35 m stretching more than 2 cm in a sprint. |
1. The brief
The brief: a game-ready W7, with the face preserved and no holes in the body, allowed to run up to 50% over the usual
game budget for a first pass. We gave the first pass that headroom on purpose: it was better to see that nothing went
wrong at a generous size, and tighten afterwards, than to break her face or open holes chasing the final number at
once.
| Check | First W7 game |
|---|---|
| Triangles | 475,573 down to 69,979, against 44,000 to 48,000 for the current game models |
| Face | fully protected: all 6,682 face triangles kept, with a 1 cm soft border |
| Holes | none new. Open edges went from 2,424 down to 2,166, because collapsing edges can merge existing gaps but never cuts new ones |
| Textures | the same ones as W7, unchanged |
It was almost there: the hair held up apart from one or two skin-coloured blobs. We decided the next pass should give
slightly less to the face and less again to the hair. Hair does not need fine geometry to read well: even AAA games
render hair as a mass, and what sells it is the physics, so its triangles were better spent elsewhere.
2. Budgets by part, and a mask nobody intended
A second build cut the model in three passes, part by part:
| Part | Triangles before | Triangles after |
|---|---|---|
| Face | 6,682 | 5,689 (-15%) |
| Hair | 141,869 | 23,315 |
| Body skin | 83,240 | 22,111 |
| Shorts | 143,847 | 9,327 |
| Hoodie torso | 56,088 | 4,568 |
| Other (shoes and so on) | 12,723 | 3,981 |
| Sleeves | 26,668 | 1,666 |
| Head and neck skin | 4,456 | 678 |
| Total | 475,573 | 71,335 |
Problem. The new build was botched: the bottom half of her face had turned dark red, so she looked as if she were
wearing a mask. A face is the one part that cannot regress, so rather than patch the new build we went back to the
previous W7 game, pixel for pixel.
Cause. The step that fixed skin-coloured blobs in the hair decided what was hair by position: head material, outside a
face box that ended at 1.50 m. The lower half of her face sat below 1.50 m, so it counted as hair and was recoloured.
Solution. The previous build was restored byte for byte. The rule: hair is defined by its texels, never as the head
minus a box, and every recolour step counts how many faces it touched inside the face and refuses to save unless
that count is zero.
3. The budget, set by eye
Measured on the restored build, then set part by part:
| Part | Now (triangles) | Change | Target | Built (v4) |
|---|---|---|---|---|
| Face | 9,074 | -20% | 7,259 | 7,270 |
| Hair | 22,633 | -50% | 11,317 | 11,333 |
| Hoodie | 11,275 | -30% | 7,893 | 8,171 |
| Part | Now (triangles) | Change | Target | Built (v4) |
|---|---|---|---|---|
| Shorts | 11,109 | -30% | 7,776 | 7,583 |
| Stomach | 2,604 | +50% | 3,906 | 4,031 |
| Back (skin) | 459 points | to 2,500 points | about 3,050 | 2,417 points |
| Legs, shoes, hands, shoulders, neck, other | maintain | as now | as now | |
| Total | 69,979 | about 54,000 | 55,411 |
The hair blobs. In the reduced hair, big triangles stretch across skin-coloured texels while their centre still lands on
red. Three attempts: judging each face by its centre texel only; searching only near earlier strokes; and a skin test so
loose that hair highlights passed as skin. The fourth sampled each face at seven points and used a strict peach test.
| Check | Result |
|---|---|
| Blobs found and fixed | 611 |
| Still showing skin colour | 0 |
| Face or neck faces touched | 0 |

The blobs: skin-coloured texels showing through where reduced hair triangles stretch across the paint.
Then a paint job by hand, in marker colours: red strokes where hair showed skin, black on the hoodie, skin on a leg
patch. The red strokes were reverted to the original hair pixels (6,340 texels) and the 211 hair faces under them given
real hair texture; 52 within 2 mm of the face skin were left alone. An apparent hole on her right shin, which no
measurement could find, turned out on a closer look to be not a hole at all but a black marking on one of the big
triangles; that is why the hole checks never saw it, and it was painted out by hand.
4. The last 7,854 triangles, by hand
| Target | Triangles to remove | Points to remove, roughly |
|---|---|---|
| 48,000, like Streetwear X | about 7,400 | about 3,800 |
| 46,000, the middle | about 9,400 | about 4,900 |
| 44,000, like M8x | about 11,400 | about 5,900 |
A REDUCE SELECTED button with a ratio slider was added to the panel: select a region, reduce only that, and nothing
outside the selection or its border row changes. The reduction itself was made by hand, part by part:
| Part | Points before | Points after | Triangles before | Triangles after | Change |
|---|---|---|---|---|---|
| Face | 4,066 | 4,066 | 7,089 | 7,089 | - |
| Hair | 6,548 | 6,548 | 11,667 | 11,667 | - |
| Hoodie | 5,435 | 5,435 | 8,172 | 8,172 | - |
| Stomach | 2,939 | 2,526 | 4,031 | 3,406 | -16% |
| Back (skin) | 2,417 | 1,046 | 4,072 | 1,562 | -62% |
| Shoulders | 790 | 790 | 1,108 | 1,108 | - |
| Shorts | 5,966 | 5,142 | 7,582 | 6,590 | -13% |
| Legs | 1,382 | 840 | 2,431 | 1,434 | -41% |
| Shoes | 2,966 | 1,514 | 5,181 | 2,583 | -50% |
| Hands | 1,434 | 1,434 | 2,633 | 2,633 | - |
| Neck | 140 | 140 | 67 | 67 | - |
| Other | 2,459 | 2,216 | 1,378 | 1,246 | -10% |
| Total | 28,639 | 24,712 | 55,411 | 47,557 | -14% |
The cuts went where nobody looks. Her back had never been strong in the original T8 body, because it was meant to
be covered, so it made sense to reduce it most rather than spend triangles on a surface that was not built to be seen;
the back hidden inside her hoodie lost many points for the same reason. Face, hair, hoodie, hands and shoulders were
untouched. No new holes: 1,990 open edges before and after. The two last saves, one with a repainted back, were
compared side by side under seven lighting setups (preview, studio, noon sun, back light, evening, night, flat game
light). Both held up in every light, and we chose the 22:57 save as the one to rig for the game.
5. The rig, and her shorter hair
W7 game went onto her existing Mixamo skeleton, with body weights copied from M8x's body side by side (her left
from the left half, her right from the right). Her new hair is much shorter than the old: it runs from 1.18 m (the upper
back) to her crown, where the old hair reached her waist. So only the top two links of the hair chain carry it: the crown
follows the head, the lower hair swings on the chain, and the front locks, which now end at her chin, follow the head
instead of the chest.
The first rig run put her 6 m from her skeleton and gave most of her body a finger bone: the saved file carried the
preview window's row offset on the object, and our script baked it into the mesh. The weight report caught it before
anything shipped (every point unweighted), and the rule since is to print an object's transform and bounds before
rigging it.
6. Fitting a 6 GB card
Before adding her, we needed to know whether she was the biggest character on the roster, and whether the game
could still run without lag on a 2060, our target mid-range card. By triangles, she is not the biggest. By video memory,
she is.
| Character | Triangles | Texture memory on the graphics card |
|---|---|---|
| Agent E W7 | 47,557 | 213 MB (one 4096 map + six 2048) |
| Agent E Streetwear X / M8x | 47,904 / 43,563 | 192 MB |
| Agent Zero, Agent S, Agent C, Agent B | 25-33k | 128 MB |
| Agent J | 59,943 (most) | 64 MB |
| Agent L | 41,851 | 64 MB |
The 4096 map is her whole body below the neck (26,720 of her 47,557 triangles: hoodie, shorts, stomach, hands,
shoes, legs, back, shoulders); face and hair share their own 2048. A graphics card stores an ordinary texture as raw
pixels, 4 bytes each, plus a third more for the smaller copies it uses at distance: 21 MB for a 2048 map, 85 MB for a
4096. Compressed formats the card reads directly (BC7 on a PC) take 1 byte a pixel.
Solution. Every map re-encoded as KTX2 (Basis Universal, high-quality mode, with mipmaps), at the same sizes. The
current release of the encoder we used refuses any image over about 12 megapixels, and her 4096 map is 16.8, so we
pinned the release before that limit. Each compressed map was decoded back by the game's own decoder and shown
in Blender beside the uncompressed model. They looked the same, so the compression cost nothing visible, and we
added the loader and switched her over. The game gained a KTX2 loader that works offline, for every character; her
maps load as BC7.
| Before | After | |
|---|---|---|
| Texture memory on the graphics card | 213 MB | 53 MB |
| File | 31.3 MB | 33.3 MB |
| Map sizes | 4096 + six 2048 | the same |
What is left for the 2060 is no longer memory. Ten characters at about 50 MB each fit easily on 6 GB. The test run
counted about 1,000 draw calls a frame, and each character costs one per material slot, more again for shadows. The
cheap next steps: drop her 2-triangle face material (one draw call and two 2048 maps for two triangles), compress
the other characters the same way, and measure on the 2060 itself before anything bigger.
7. Spikes that only moved in the game

The character select screen: thin skin-coloured spikes from the ends of her hair. In Blender, at rest, there were none.
Problem. Sharp spikes came out of Agent E in the game, but not in Blender. Since the mesh was the same in both
places and only the game moved her, we suspected the rig.
Cause. It was the rig. 25 points near the ends of her hair strands were still tied to her upper spine (about 65% Spine1,
26% Spine2). The rig script decided what was hair by the colour at each face's centre; thin faces inside the strands
show skin-coloured texels, failed the test and kept the body weights copied from M8x. In the game the hair chain
swings the hair while the spine holds those points, and the faces between stretch into spikes. Blender only showed
her at rest.
Solution. Every head-material point outside the face and neck that still carried body weight now follows the hair
chain. Measured in the game, sprinting: nothing below 1.35 m stretches more than 2 cm. Still open: the seam where
her hair meets her neck skin (1.40 to 1.45 m) stretches up to 4.6 cm in a sprint or when she looks up. The rule: a rig is
measured in motion before it is called done, because the rest pose proves nothing.
The milestone was committed as v0.38: the rigged model, the loader, the skins, the docs and a screenshot. The raw
work (233 Blender files, 28 GB) stays outside the repository.
What this gave us
Agent E in the game at 47,557 triangles and 53 MB, from a 475,573-triangle benchmark, with the face untouched by
every reduction. A compression path the whole roster can use. And the clearest picture yet of where the frame time
goes on a mid-range card, which is the next problem.
Build cost and the rules it left
5 to 9 October · The machine, the process, and the 35 lessons the remake wrote into our documents · Most of the cost of
these four days was not geometry. It was the way we worked, until we changed it.
| What we built | An account of where the four days went, problem by problem, and the rules the rebuild wrote into our lessons file: 35 numbered lessons (E1 to E35) and two sets of standing rules. |
| The challenge | A build that took eight minutes took seventy and then crashed. Most of the cost of the four days was the way we worked, not the geometry. |
| Key decisions | Free the machine before blaming the code. Change exactly what was marked, and nothing else. |
| The result | The next build ran in 56 seconds, and the rules are grouped by how we review, build, judge and test. |
1. The machine
Problem. A build that took eight minutes took seventy, then crashed. We blamed the new code.
Cause. The code took two seconds in isolation. The machine (16 cores, 32 GB of memory, an RTX 2060 with 6 GB)
was at its ceiling.
| Process | RAM | GPU memory | GPU load |
|---|---|---|---|
| The retexture comparison window (16 figures, two 605k masters, 12 textured models, animation playing) | 12.3 GB | 3.6 GB | 30 % |
| The R8 comparison window (playing) | 4.6 GB | 0.1 GB | low |
| Agent L's sheathe file, Agent J's kick file (unsaved work) | 3.7 + 1.2 GB | 0.2 GB | 0 |
| Our project dashboard app | 1.0 GB | 0.5 GB | 55 % |
| Chrome (all tabs) | about 4 GB | 1.2 GB | - |
| The build itself | 0.9 to 4 GB | 0 | 0 (CPU bakes) |
| 4 GB of 32 free, 60.6 of 65 GB committed. A game had been running during the stall. | |||
Solution. Playback off in both windows; the twelve retextures cut to the five best faces; the build stamped per part
so a stall names its line. The next build ran in 56 seconds.
2. Where the time went
| Problem (chapter) | Clock time, start to finish (approx.) | Named builds | What decided it |
|---|---|---|---|
| Why her face looked worse (chapter 15) | about 3 h | R, R8, R10, R8b, R8c, M8, M8u, M8x; 12 repaints | Looking at the baked textures, not at renders |
| Problem (chapter) | Clock time, start to finish (approx.) | Named builds | What decided it |
|---|---|---|---|
| Seams and the collar (chapter 14) | about 3 h | R8fix v1-v6, Turtleneck | A face-coloured diagnostic that showed the cap |
| The step we skipped (chapter 17) | about 6 h | F8, F10, R8x v1-v5 + final | Digging the old recipe instead of inventing a new one |
| Colour by eye (chapter 18) | about 8 h, mostly painting | about 8 paint passes | The brush and the save diff |
| Into the game (chapter 19) | about 3.5 h | atlas, 5 rig passes, mould v1-v3 | A bald model from behind |
| Geometry, not colour (chapter 20) | about 14 h over a day | collar v1-v7, B1-B10, C1-C4, M8 / M8u / M8x with grafts and hair | Rule One; the capped neck hole |
| Back to full resolution (chapter 21) | about 4.5 h | steps 1-4, hood options 1-2, RS1-3, TS1-2, three mirrors | Hands in Blender; the stored normals |
| Joining the head (chapter 22) | about 6.5 h | neck v1-v3, n5a / n5b, W1-W7, W7C (about 8 builds) | A solved number instead of a tuned one; a screenshot with the colour off |
| Benchmark to game (chapter 23) | about 4.5 h | W7 game (4 builds), hair fix (4), 2 rig runs, 2 encodes | The reduction by hand |
3. The rules, with the incidents that wrote them
Each of these is now in our lessons file, most with a numbered entry describing what happened and what it cost.
Together they are 35 numbered lessons (E1 to E35) and two sets of standing rules, from these four days alone.
How we review and approve
| Rule | The incident |
|---|---|
| Rule One. The eye marks, the script measures. For a visual problem, build the scene and ask for the spot to be marked; then measure what was marked | Half an hour of renders, ray tests and 24-angle grids hunting a sticker-like mark that a person spots in two seconds (chapter 20) |
| Consult first, with the best recommendation; no action on a diagnosis | Six R8x builds chased one hem, each acting on a diagnosis nobody had reviewed. We decided that every change is now proposed first, with our best recommendation, because one agreed plan costs less than six builds in the wrong direction (chapters 17, 18) |
| Show it in the window before analysing it, and version it before it runs | A v5 reported while only v4 existed; two silent patch failures (chapter 14) |
| Check every build before showing it; delete a failed build, never display it | A black face, a torn face and a mess of shards shown unchecked, and rejected on sight: each cost a review that a check would have saved (chapter 20) |
| A mod replaces the figure in place; a variant stands beside it only on a real fork, and only after asking | Clones of clones filled the window with redundant figures, until it was hard to tell which one was current (chapter 17) |
| Change only the part that was named | A collar fix that also changed the shorts (chapter 14) |
How we build
| Rule | The incident |
|---|---|
| Weld before reducing; reduce, then bake | The wedges; the hem teeth; the fused eyes (chapters 15, 17) |
| Never use a closed split piece where it touches skin; take it from a fused shell | The collar, eleven builds (chapter 14) |
| A voxel remesh fills everything inside a closed shape; leave the hood out | The monster neck: a neck as wide as the hood's opening (chapter 20) |
| Joins are made at full resolution, by a flat cut on each side and a bridge, and reduced once at the end | Neck joins on the reduced model folded and tore (chapter 22) |
| Head and hair from a generated model are one piece; never split the scalp | A hole in the head where the scalp was painted as hair (chapter 22) |
| Never move or delete points by a region rule on a finished body; name every geometry change before it runs | Step 4: a region rule moved points on the finished body, and the build failed outright with a crusty surface (chapter 21) |
| A deletion that leaves a hole is never a fix; fills and paint go exactly where they were marked, nowhere else | The collar trim; the fill pieces in front of her neck; a face paint that made her look like a different person (chapters 16, 20) |
| Hair is found by its texels, never as the head minus a box; a recolour counts the face faces it touched and must count zero | The lower face turned dark red, as if she wore a mask (chapter 23) |
| After a colour fade, measure the colour by height in 2 mm steps | A fade that painted a choker (chapter 22) |
How we judge, and how we test
| Rule | The incident |
|---|---|
| Judge geometry bald, from the angle in question, before anything goes on top | F8 reported as having no holes, judged from a thumbnail, which could not show them (chapter 19) |
| A rig is judged in motion, in the game, never at rest | The first rig (chapter 19); the spikes (chapter 23) |
| Print a model's transform and bounds before rigging it | A rig that put her 6 m from her skeleton (chapter 23) |
| Never close a window that shows unsaved changes; a test never touches a user's save file | A shoulder sculpt nearly lost; a fit file deleted by a self-test (chapter 21) |
| Pick a source resolution only where a bake consumes it | 8K textures carried into a build with no bake: 698 MB (chapter 19) |
| Credits are cheaper than hours: 500 per character without a question, 1,000 at the cap; a remesh is offered as a try, never forced | A 44-credit remesh weighed against a day of rules (chapter 19) |
4. The pattern
Read together, the rules say three things. The generated model is a good sculpt and a bad construction: weld it, part it,
and keep the face's budget. The eye decides, and it needs tools, not reports: a brush, a marker, an eraser, a slider, a
row of figures in the same light. And every fix we made by rule, unrequested and unseen, made her worse: the models
were already about 90% there, and each unrequested fix took them lower, to 80%, then 70%. Once a model is that
close, a fix nobody marked has more to break than to gain. The builds that moved her forward were the ones that
changed exactly what was marked, and nothing else.
Reference
- 25Architecture reference
- 26Engineering rules and their incidents
Architecture reference
October 2026 · Reference page. Main is at v0.37.1, with Agent C on her own branch at v0.37.2.
The game in one paragraph
TPS Arena is a third-person shooter that runs offline in a browser or as a desktop app. It is about 15,000 lines of plain
JavaScript on Three.js, with no build step and no network at runtime. Characters, abilities, weapons and maps are
tables of data. Seven characters have real rigged models: Agent E, Agent S, Agent Zero, Agent L, Agent J, Agent B as a
placeholder, and Agent C on her branch. Six gun characters are still built from code.
Decisions that shape everything
| Decision | What we weighed | Why it went this way | Chapter |
|---|---|---|---|
| Three.js, offline | Unreal, Unity, Godot | It is plain code in one repo that we can read, change and test with scripts. Unreal's work lives in binary assets and an editor that is hard to automate. The renderer was never why the game looked flat | 01, 13 |
| Characters are rigged GLB files on Mixamo's skeleton | Characters built in code. Meshy's own rig | Painted faces on spheres never read as characters. Meshy's "Mixamo rig" had bones up to 177 degrees off, so no shared clip fitted it | 02, 03 |
| An NPC is the character's own code, driven by a fake keyboard | A separate bot class with its own numbers | Bots cheated in small ways. This is meant to be PvP one day, so nothing about a bot may differ from a player | 05 |
| Art is placed in Blender, the game plays it back | Grip and pose numbers in code, reviewed by screenshot | Twenty iterations to seat one sword in a hand by numbers, against twenty minutes of dragging it into place | 03, 06 |
| Bullets test capsules, not bodies | Ray tests against the rendered mesh | 3.6 ms a ray against four skinned bodies. 0.09 ms against a capsule | 05 |
| One door for knockback | Each ability pushing in its own way | Ground and air behaved differently. With one door a retune is one number | 05 |
| A clone is an ability, not a character | A second bot with its own charges | It did not double damage, appeared on the scoreboard and could be farmed for kills | 07 |
| Strikes travel along the body, for every character | Sending one character's strikes up the camera line | It made him the only character who looked angled | 11 |
| Map architecture is modelled. Generation is for character and organic pieces | Every piece from a picture through image-to-3D | About 250 credits spent on the wrong route | 09 |
| Showcase pieces go from high to low detail by baking | Straight decimation | It shattered a blade and made a "playdough" shield | 10 |
| Code in git, game-ready binaries through LFS, raw files never in git | A fresh repo. Rewriting history | Nothing lost. A clean repo is planned for v1.0.0 | 06 |
How a character is made today
| Step | What happens |
|---|---|
| 1 | A concept picture, in an A-pose with empty hands. It is approved before anything goes to 3D. |
| 2 | Meshy turns the picture into a textured model. Props (sword, scabbard, cloak) are generated separately, because a fused sword comes out as a lump. |
| 3 | Proportions are tuned on sliders in Blender, on the bare mesh, and baked in. |
| 4 | tools/heroprep.mjs prepares the mesh. Mixamo gives it the 65-bone skeleton. The tool brings it back. checks it against a 13-rule contract. Each rule is a bug that already cost us time. |
| 5 | Hair is re-bound by tools/hairbones.mjs, because the automatic rig ties long hair to arms and hips. |
| 6 | Clips come from Mixamo, played as exported unless we set a speed. A character's own moves are authored in Blender. |
| 7 | Grips and sockets are placed by hand in Blender and saved to a small file the game reads. |
| 8 | The kit is data in src/agents.js. tools/kitcheck.mjs fails if any description promises something the code does not do. |
Steps 2 and 5 are where chapter 13's bottleneck sits. The model arrives as one fused shell, and the hair fix can only
ever be a patch on that.
How it is tested
An Electron harness opens the game muted and runs a scripted list of steps. It saves a screenshot and the returned
numbers after each one. Almost every figure in this journal was measured that way.
| What is being checked | How we check it |
|---|---|
| Logic | The Electron harness. It measures logic well. |
| Frame rate | A separate benchmark that times the GPU at the real play resolution (chapter 08). The harness measures rendering badly. |
| How something looks | A visual change is reported as measurements and pictures. The final judgement is made by eye, in the running game. The harness does not replace that. |
The performance budget
Our bar: 120 fps on Low and Medium on an RTX 2060, with 80 to 100 as the floor. The game's code runs on one CPU
core, so the budget is draw calls and script time per frame.
| Item | Working limit |
|---|---|
| Characters | 30 to 60 thousand triangles |
| Props | 5 to 15 thousand triangles |
| Map pieces | 1 to 5 thousand triangles |
| Loading | Up to ten seconds, so that nothing is built or compiled during a match |
Engineering rules and their incidents
Reference page. The rules we work by, and the problem that produced each one.
None of these rules was written in advance. Each one exists because something went wrong first. We document every
mistake as well as every fix, so that we grow. The chapter where each incident is described is given in brackets.
How we review and approve work
| Rule | Where it came from |
|---|---|
| Rule One. The eye marks, the script measures. For a visual problem, build the scene (the model loaded, framed and lit) and ask for the spot to be marked with a brush, pins or a screenshot. Then measure what was marked | Half an hour of renders, ray tests and 24-angle grids hunting a flaw on Agent E's hood that a person spots in two seconds. Written above Rule Zero the same minute. (Chapter 20) |
| Rule Zero. Diagnose before fixing. Report the cause and the options, and agree the fix, before anything is changed. An open question is a question, not a work order | We judged Agent L's jump clip unusable, replaced it and committed the change without a review. The clip was whole. It only needed slowing down. (Chapter 06) |
| When a Meshy description is revised, change only the part under revision | A full rewrite of the description swapped the blue coat we wanted for something else. (Chapter 06) |
| Every credit spend is approved first, job by job | A 25-credit job was submitted as soon as its price came back, with no approval step. (Chapter 07) |
| Every picture is reviewed by eye before it goes to 3D | An island went to 3D on an earlier approval, with the final picture unseen. (Chapter 09) |
| In art tuning, do the one named step, then stop and review | A sword grip kept being reshaped beyond the step that was agreed. (Chapter 07) |
| A change made for one character stays on that character | A guard fix for Agent J was applied to everyone and broke Agent L's guard cancel. (Chapter 11) |
| Never call a visual change fixed. Report measurements and pictures. The judgement is made by eye, in the game | Agent C's hair was reported done several times and failed review in the game each time. Measurements cannot see what eyes see. (Chapter 12) |
| Variants are compared in Blender first. Nothing goes into the game until one is picked | Two hair variants were put straight into the live game files. (Chapter 12) |
| Every build is checked before it is shown. A failed build is deleted, never displayed | A black face, a torn face and a build broken into shards were shown unchecked. (Chapter 20) |
| Version every build before it runs, and show it before starting the next. A change to a build replaces it in place; a variant stands beside it only on a real fork, and only after asking | A "v5" was reported while only v4 existed; clones of clones filled the comparison window. (Chapters 16, 17) |
| Change only the part that was named | A collar fix also changed the shorts. (Chapter 14) |
| Design iterations stay private. The journal records the final design | Set as our policy when the statues were being designed. (Chapter 10) |
How we build
| Rule | Where it came from |
|---|---|
| Every change gets a patch note | We wanted AAA-style patch notes from the earliest builds. They are why this journal exists. (Chapter 01) |
| A character's quirk is an asset or pipeline problem. Fix the file or the tool. Never add a per-character flag | We added two wrong per-character flags while importing Agent Zero. (Chapter 04) |
| An NPC carries no number of its own | Our bots had their own speed, their own damage and a free headshot on every bullet. (Chapter 05) |
| Name moves by what the player does. A "jump attack" is Space plus left click, never the name of the clip that plays | Moves had been named after their animations. (Chapter 05) |
| A character-select description must match what the ability does. A script checks it | Descriptions kept promising lifesteal that had been removed. (Chapter 06) |
| One movement rule for all characters | Agent J alone was sent up the camera line and looked angled. (Chapter 11) |
| Weld a generated mesh before reducing it. Reduce, then bake; keep the original texels only at full resolution | The wedges on Agent E's face and belly were our unwelded reduction, not the sculpt. (Chapter 15) |
| Joins between two models are made at full resolution, by a flat cut on each side and a bridge, and reduced once at the end | Neck joins on the reduced model folded and tore. (Chapter 22) |
| Never move or delete points by a region rule on a finished body. Name every geometry change before it runs | A smoothing step and an unchecked neck cut left Agent E's surface rough, with holes. (Chapter 21) |
| A deletion that leaves a hole is never a fix. Fills and paint go exactly where they were marked | A collar trim, fill pieces pasted in front of a neck, a face paint that changed who the character looked like. (Chapters 16, 20) |
| No comment in the middle of a line of settings | A // silently switched off the rest of the line. It broke Agent E, then Agent Zero, then Agent S's main ability. We misread the last one at first as a balance problem. (Chapter 04) |
How we use the tools
| Rule | Where it came from |
|---|---|
| Read a tool's own free instructions before spending credits on it | We spent about 250 credits of image-to-3D on pieces that should have been modelled for nothing. (Chapter 09) |
| Heavy Blender jobs run headless, never in the live window | A bake froze Blender for 15 minutes and lost unsaved work. Headless, it took 8.5 seconds. (Chapter 09) |
| Measure the GPU, not the loop, at the real play resolution | The meter said 80 to 100 fps while the screen showed about 10. (Chapter 08) |
| Measure every art step against the last release | One texture pass cost about 20% of the frame rate. (Chapter 08) |
| Decimation is not a pipeline. Showcase pieces are baked from high to low | Decimation gave us a shattered blade and a playdough shield. (Chapter 10) |
| Match colours on rendered pixels, not on the source picture | Clouds came out orange three times after tone mapping. (Chapter 09) |
| Rule | Where it came from |
|---|---|
| Before a Blender scene is used for a decision, one joint angle is compared with the game | Arms were tuned in a preview that was 20 degrees off the game. The tuning was wasted. (Chapter 11) |
| A report that a move does not work checks three things before naming a cause: the clip plays, the hit lands, the pose shows | We reported half a diagnosis as the cause. The fix built on it did not work. (Chapter 11) |
| Measure what is under an asset before cutting it | Cutting Agent C's hair exposed that there was no back beneath it. (Chapter 12) |
| Judge geometry bald, from the angle in question, before anything goes on top | Holes were ruled out from a thumbnail; the painted master was hollow behind both shoulders. (Chapter 19) |
| A rig is judged in motion, in the game, never at rest | Spikes from the ends of Agent E's hair appeared only when she moved. (Chapter 23) |
| Never close a window that shows unsaved changes. A test never touches a user's save | A hand-sculpted shoulder was nearly lost; a self-test deleted a saved fit. (Chapter 21) |
| Character textures ship compressed for the graphics card | Agent E's maps took 213 MB of video memory; compressed, 53 MB, at the same sizes. (Chapter 23) |
| One game window at a time | Two or three copies of the game open at once made it unclear which build was under test. (Chapter 07) |
The pattern
Read together, most of these say one of three things. Measure before you change anything. Get approval before you
spend or fix. And when the question is how something looks, it is decided by eye, in the running game, and the eye is
given tools to mark exactly what it sees. Our most expensive mistakes were not coding errors. They were changes
made on an unreviewed judgement, where a review was what counted.
Roadmap
10 October 2026 · Always the last chapter. What is next, and why.
The target
Our schedule is three months. What is reachable in the remaining time, by the assessment in chapter 13, is a vertical
slice: six characters whose look and motion read as intentional, one finished map, and play over LAN.
What changed since the last version of this page
| Item | Then | Now |
|---|---|---|
| The character model route | Undecided between four routes (chapter 13) | Measured on one character. Agent E was rebuilt from her generated sources into a clean, game-size model (chapter 14), and the recipe is written down |
| Agent C | On her own branch | Merged into the main game (v0.37.2). Her hair stays on variant 2E for now |
| Item | Then | Now |
|---|---|---|
| Compressed textures | Planned, not started | Done for Agent E (v0.38): the game reads KTX2 textures, 213 MB down to 53 MB on the graphics card |
Decisions that come first
| Decision | What depends on it |
|---|---|
| Which character goes through Agent E's recipe next | Four days for the first character; the next should be faster, because the rules and tools now exist |
| When netcode starts | LAN play. Nothing exists yet, and it is the item that cannot be squeezed at the end |
| Hardware: a bigger graphics card | How far the free local models can go (chapter 10) |
Work in progress
| Item | Where it stands | What is left |
|---|---|---|
| Agent E | W7 in the game as her default look: 47,557 triangles, 53 MB on the graphics card | The seam at the back of her neck stretches up to 4.6 cm in a sprint; her hair swing is still tuned for the old, longer hair; a face material that covers 2 triangles |
| Agent C | Merged. Hair on variant 2E | The remaining hair slits, a jump clip and the wand angle |
| Agent L's draw | At 90% by our own measure, in game at twice speed. The Blender file and the frame-by-frame intent are kept in our draw notes file, so it can be picked up again | The last 10% |
| Agent B | Still a placeholder on Agent E's body | A model of her own |
| Agent L and Agent J | Still borrow a guard or an ultimate from other characters, marked as borrowed in | Their own guard and ultimate |
| Olympus V2 | The arena kit and all eight gods are in | Baked lightmaps, instancing for repeats, and distance versions of the models |
| Warehouse | Redesigned on paper as a covered warehouse with a 17-piece kit | The build |
| Cascadeur | Installed, not yet used | It is the planned tool for character attack animation |
Performance on a mid-range card
Our target machine runs an RTX 2060 with 6 GB. With textures compressed, memory is no longer the limit: ten
characters at about 50 MB each fit easily. The next limit is the number of things drawn each frame.
| # | Step | Why |
|---|---|---|
| 1 | Measure on the 2060 itself (npm run bench) | A real frame rate before anything bigger |
| 2 | Compress every other character the way Agent E's were | The other characters use 64 to 192 MB of textures each, uncompressed |
| # | Step | Why |
|---|---|---|
| 3 | Cut draw calls: about 1,000 a frame in a 5 v 5 test. Merge small material slots, starting with Agent E's 2-triangle face material | Each material slot is a draw call per character, and more again for shadows |
Planned engine work
From the developer guide's own plan. None is started.
- Flipbook effects for smoke and fire.
- A particle engine to replace the hand-written one.
- A sound pass.
The order, and why
| # | Next | Why |
|---|---|---|
| 1 | Carry Agent E's recipe to the next character | The vertical slice needs six characters whose look reads as intentional, and the route now exists |
| 2 | Start netcode | LAN play needs it. It is the item that cannot be squeezed at the end |
| 3 | Finish one map | The vertical slice needs one finished map |
Performance work runs alongside all three.
Beyond the game
The game is the first world, not the last. The same pipeline, generated models made game-ready, a budget that holds
on a mid-range card, maps built piece by piece, is what a walkable copy of a real building needs. That is the direction
after the vertical slice: places that mirror real landmarks, previews of projects that are not built yet, and the same
worlds in VR and AR.