Black on Black
Bunched at the front
The Sweatbox is an old brick warehouse with galleries down both sides, a DJ booth at the far end under a purple LED panel, and a dance floor that had, until this week, nobody on it who meant to be there. The clubbers stood about, got bored, and wandered out through the front door on 40-metre strolls before wandering back in. On Tuesday the agent had built them a dance floor: a grid of spots in front of the booth, where a clubber dances alone first and then drifts into a ring of two to five, facing inward.
It was Thursday, so James was at work. The day ran the way most of them run now. Several agents, each on its own thread, queued for one editor through a coordinator, took a slot, did their piece, saved it and handed the machine back. He read what came out of it on his phone and answered the questions that only he could answer.
He looked at the dance floor and said it looked fantastic. Then he said they were all bunched up at the front.
They were. The agent went back through its own code and found it had told them to. The spots were weighted four, two and one by row, so the front row near the booth was picked four times as often as the back, and a dancer was allowed to drift eight metres to join a group, which on a floor that size meant anywhere. The fix was even rows, a preference for the free spot furthest from anybody already dancing, a penalty for crowding, and a shorter drift of four and a half metres. The spots are now spaced 2.6 metres apart and the grid sizes itself from the floor. The floor itself was then measured properly, clipped off the DJ's riser at one end and the bar front at the other, and came out at 25 spots, five by five.
GTA VI is set in a version of Miami, and its clubs will be very good. This is a Midlands warehouse where the dancers leave each other a bit of room.
Black on black
James had asked for three more things for the club. Steady colour on the floor under the beat flashes. A haze in the room, the kind you see light cut through. And dry ice: low fog that parts round the dancers and round the player.

The washes went in first, six spotlights pointing straight down, no shadows, tagged so the beat show would never switch them off. The first frame back was the one above. The floor was black.
The agent had set them to 12000. Unreal reads that number differently depending on which unit the light is set to, and in the unitless setting 12000 works out at about 19 candelas, which is roughly nineteen candles from eight metres up. The floor in the Sweatbox is very dark, reflecting a little over one per cent of what lands on it. Nineteen candles on that is nothing. The washes went to 2000 candelas, and the floor came up purple, magenta and blue at Low and at Epic.

The haze was the rest of the night, and the end of the night is the right place to start. The haze never appeared, and by two in the morning nobody knew why. It is going to be built a different way.
It was already running. The room had a Local Fog Volume in it, a sphere of fog that switches on with the club, and every number the agent read back from the game said so: the club's lighting level at 0.85, the haze at 0.51, the fog's density at 0.255. It was invisible at Low. It was invisible at Epic.
The agent tried three things in the live game, one after another, and none of them changed the picture. The third was to give the fog its own glow, a strong purple, so that even with no light on it there would be something to see. Nothing. That looked like a result. It was not one, and the agent found that out itself later in the evening, reading back through what it had done: it had set the glow on the fog in the editor's own copy of the level. When you press play, Unreal makes a fresh copy of the level to play in. The copy everyone was looking at never got the purple.
So the next session started from the engine's own source code rather than from guesses. That turned up two real facts. A Local Fog Volume is always a sphere, five metres across per unit of scale, whatever shape you stretch it to. And at Low, where volumetric fog is off, it is lit by the sun and the sky and nothing else. The club's lights never touch it. At ten at night the sun and the sky are both black, so the fog was black, in front of a black room. Black on black.
That explained Low. It did not explain Epic, where the fog is supposed to be lit by everything. The agent then did the glow test properly, on the copy being played, and the answer was worse than before. Purple fog, volumetric fog on: nothing. Volumetric fog off: nothing. One more setting changed: nothing. The graphics profile put the fog volume's drawing time at a thousandth of a millisecond, which is what drawing nothing costs. The profile also opened a window of its own that stopped every agent talking to the editor until somebody closed it, which went on the list of things not to do in a shared editor.
That was quarter to two. The decision was to stop asking the fog and build the haze out of the thing that had already been proved to light up: sprites, big soft ones, about sixty at a time, drifting high in the room.
Pushers
The proof that sprites light up was the dry ice, which had gone in about twenty minutes earlier.
The dry ice is a particle system of flat, soft puffs laid face-up on the floor, so they can never cut through it. The clever bit is the parting. The dance floor already knows where every dancer and the player are standing, so it hands that list to the fog ten times a second, and a small piece of custom code shoves any puff inside a person's radius out of their way and thins it. Drag brings it back when they move on.
It nearly did not build. Niagara's CPU path refused the custom code with out/inout parameters must be lvalues, which is a compiler's way of saying it will not read a list inside a loop in that form. The same code runs fine on the graphics card, so that is where it runs.

The first look was the frame above. The fog was there and it was lit by the club, and it read as a tan carpet laid over the whole floor. The puffs came down from about a fifth opacity to a sixth and got smaller, 120 to 340 centimetres across. Christie was the only one on the floor at half ten, crossing it rather than dancing, so the parting is the next thing to see, on a busy night.
Rockstar have a lighting department. The Sweatbox had an agent reading the engine's shader source at one in the morning.
Between tenants
The houses had finished long before the club started.
On Wednesday the first three derelicts went in on Kelsthorpe: 6 Ovaltine Crescent, 61 Bisto Road and 13 Brylcreem Close. By seven on Thursday evening the other five were done and saved: two Radworth terraces and three houses in Bewsall. Grey steel screens over the ground-floor windows and doors, broken glass on the ground under them, grime film on the windows upstairs, dusty rooms, weeds in the garden and rubbish in the yard. James's rule from the start was that it was not worth boarding up any house with a good interior, so the full steel treatment only goes on houses nobody can walk into anyway.

Every street in Britain has one, and they all look the same. A house between tenants for too long, screened up so nobody gets in, with the front window gone cloudy and a mattress round the back. That is the British argument for the game in one house. A game set in Miami has no reason to know what a void property looks like. A British player knows at a glance whether it is right.
Getting there was the usual run of small faults, each found by looking at the pictures. The broken glass under a terrace window sank 19 centimetres into the ground, because the trace that drops it onto the ground had been told to ignore the house, and so went straight through the step it should have landed on. A front garden set six metres deep put a mattress on the pavement in front of the next street. The gardens were cut to three metres, the mattress went round the back, and seventeen weeds that had grown out past the garden were pulled. One rear camera turned out to be underground, below a rising bank, and photographed the inside of a hill.
The Bewsall houses were the odd one. They are not separate objects in the level at all. Each street is one object holding copies of the same few house shapes, so there was no house to find, only a position inside a street, matched to within five centimetres.

The first photograph at any new spot came out with grey stand-in textures, because the real ones had not loaded yet. The pattern that worked was to take the after shots, hide the new pieces, take the before shots from the same cameras, and show them again. Eight houses, all saved, all photographed both ways.
The hand on the crown
The last job of the night was at the hairdresser's.
Agnieszka is the stylist at Fringe Benefits, and her cutting clip had been made for Terry, the barber at Barber Black Sheep, who is a different shape. On her, the scissors worked about twenty centimetres out to the side of her customer's head, snipping at the air beside her ear. The fix was to work the arm out again on Agnieszka's own body, with her own arm lengths, so that her hands land where the customer's head actually is. In the game she now works round the back of it.

James asked for the same treatment for Terry, which went in after midnight. One thing on Terry had been on the list for two days: a copper-coloured flap that appeared behind a bald customer's head and that nobody could account for. Copper is the colour of the salon in the setup, so it looked like a stray piece of the wrong shop. The agent looked at it again from the side. It was Terry's left hand, resting flat on the man's crown while the clippers went round, lit copper by the window behind it.
That one went off the list as not a bug.
Elsewhere on the estate
At Epic, the town now runs at 14 frames a second in the rain and 17 in the club, up from between one and four, after another thread throttled how much work each character does every frame. Every person in the crowd costs about 19 MB of graphics memory, so a hundred of them want just over 9 GB on an 8 GB card. That is the next fight. A smaller Nanite streaming pool saved about 230 MB of it and is now in the settings.
The fault
Local Fog Volume draws nothing in Unreal Engine 5.8 at Low or Epic (LocalFogVolumeVolumes 0.001 ms)
A Local Fog Volume used as room haze in a nightclub interior was invisible at every quality level. The game's readback said it was running (density 0.255, the club's lighting parameter live).
- At Low (r.VolumetricFog 0) a Local Fog Volume takes the analytical path and is lit only by the directional light and the sky light, plus its own FogEmissive. Point lights and spot lights never light it, so indoors at night it is black against a black room.
- The first FogEmissive test proved nothing: it was set on the component in the editor world while PIE was running. PIE plays a duplicate of the level.
- At Epic, with FogEmissive set on the PIE component, it still drew nothing with volumetric fog on or off and with r.LocalFogVolume.UseHZB 0.
profilegpushowed LocalFogVolumeVolumes at 0.001 ms. Root cause not found. - Its height channel was also dead: height offset -350 with falloff 180 evaluates to zero inside the room.
- A Local Fog Volume is always a sphere, radius 500 cm times the largest axis of the actor's scale. Non-uniform scale is ignored.
- ☠
profilegpuopens a modal GPU Visualizer window, which blocks every editor tool connection until it is closed.
The fix
Haze from lit sprites; washes in candelas; write tests to the PIE copy
- Haze: being rebuilt as camera-facing Niagara sprites (about 60 alive, 450-850 cm, alpha 0.05), because sprites ARE lit by local lights - the dry ice proved it on the same floor.
- Washes: six SpotLights with shadows off, set in candelas. 12000 unitless is about 19 cd and invisible on a floor of albedo about 0.013; 2000 cd reads clearly at Low and Epic.
- Dry ice: flat sprites facing +Z just above the floor, pushed out of each dancer's radius by a custom module reading a Vector4 array (position and radius, fed at 10 Hz). Runs as a GPU sim: the CPU VM rejects an array data-interface call inside a loop with
out/inout parameters must be lvalues. - Guard: any property test during PIE goes on the PIE world's component, never the placed one. A test that changes nothing on the editor copy is not a result.
- ☠ Renaming an emitter on a duplicated Niagara system broke every module's parameter map (129 errors). Delete and duplicate again instead.
The fault
NPC dancers bunch at the front of the dance floor near the DJ
Clubbers given a dance floor all collected in the front rows. The spot picker weighted rows 4, 2, 1 from the front, and the maximum drift to join a group (800 cm) covered the whole floor.
The fix
Even rows, furthest-free spot first, shorter drift
Rows weighted evenly; pick the free spot furthest from existing dancers; a crowding penalty; drift capped at 450 cm; 260 cm spot pitch with the grid sized from the floor extent. The floor area was re-measured to the open floor, off the DJ riser and the bar front: 25 spots.
The fault
Hairdresser NPC's scissors cut the air 20 cm beside the customer's head (shared animation clip, different MetaHuman body)
A cutting clip whose hand targets were solved on one body played on a second stylist with a different build. Her hands worked about 20 cm out to the side of the head.
The fix
Solve the clip's arm IK on the body that plays it
Rebuild a per-stylist clip with the inverse kinematics evaluated on that stylist's own skeletal mesh, so her arm lengths put the hands on the head. Verified in PIE: she works round the back of the head. The "copper flap" behind the barber's bald customer was his own hand on the crown in the clippers clip.
The kit
| Tool | Why this one |
|---|---|
| Unreal Engine 5.8 source | settled what a Local Fog Volume is lit by at Low, which no amount of trying settings would have |
| Niagara GPU simulation | the only path that accepts the dry ice's array-in-a-loop code |
| Claude Code, several agents through one coordinator | five threads took turns on one editor while James was at work |
| Before and after stills from the same cameras | caught the sunk glass, the mattress on the pavement and the underground camera |
Plain English
| Term | What it means |
|---|---|
| local fog volume | A box or sphere of fog placed in one spot, such as haze filling a club, instead of fog across the whole world. |
| volumetric fog | Fog worked out in small 3D cells round the camera so that lights can shine through it and show their beams; it costs graphics time, so lower quality settings switch it off. |
| editor world | Pressing play makes a fresh copy of the level to play in; a change made to the editor's own copy while the game runs does not reach the copy being played. |
| PIE | "Play In Editor" - pressing play inside the editor to test the game without building it first. Fast, and it behaves subtly differently from the real build. |
| Niagara | Unreal's particle system - rain, smoke, sparks, dust, fizz on a pint. |
| sprite | A flat picture, such as a puff of smoke, drawn by a particle system - usually turned to face the camera so it never looks flat. |
| candela | A unit of how bright a light looks from one direction. A candle is about one; a lightning strike is millions. |
| emissive | The part of a material that gives off light - a screen, a bulb, a neon sign. |
| inverse kinematics | Working an arm or a leg backwards from where the hand or foot has to end up, instead of playing whatever the animation happened to record. It is how a character's hand can land on a moving object the animator never saw. |
| MetaHuman | Epic's system for realistic human characters - face, body, hair and clothing, ready to animate. Every named person in the game is one. |
| scalability settings | The engine's quality dials - shadows, textures, effects, anti-aliasing - each of which can be set independently. Turning one up can cost far more than turning the rest up together. |
Things we still need to fix tomorrow:
- The room haze, rebuilt as lit sprites, is next, then a still for James at club hours.
- The dry ice parting round dancers is next to see, on a busy night.
- The washes run round the clock; tying them to opening hours is next.
- A crowd of a hundred at Epic wants about 9.1 GB on an 8 GB card. Hair and mesh costs per person are next.
- The terraces' back doors are next for steel screens.