PoleFX Engineering Journal / series
Building the 3D Club Scene
Retargeting mocap onto arbitrary bodies, an AI-built motion-QA lab, the camera department, and the acceptance process behind it — design and debugging notes from a real-time three.js club stage that is still very much in progress, not a tour of the finished thing.
In this series, in order

Hands on the pole: retargeting mocap onto bodies that don't exist yet
We bought one 30-second mocap take of a pole routine. Making it play on characters the performer has never met — feet on the floor, hands on the pole, nothing clipping through anything — took five rewrites of the arm alone.
Read the field note →
Watching a pole dancer with numbers: a motion-QA lab, and the mistakes it couldn't catch
The [last post](/2026-07-23-hands-on-the-pole/) shipped a retargeting pipeline with hard invariants and rate-limited motors: nothing should be able to teleport. So we built a browser-based motion-QA lab to check: seven cameras, 1,773 frames of telemetry a loop, a regression suite that plays the raw performance back for comparison. It found four families of failure the demo reel never showed, including one escape hatch that was quietly manufacturing exactly the teleports we'd sworn off. It also missed things, confidently, for days — which turned out to be the more useful half of the story.
Read the field note →
Shooting a stage that isn't there: an on-rails camera for a real-time scene
The dancer from the [last post](/2026-07-23-hands-on-the-pole/) needed a music video. We built her a camera department instead: dolly, jib, orbit and long-lens rails, a cut list synced to her choreography, and a lighting operator. No pose timeline: we author geometry, cut points, and a few spline control points, and the engine generates every frame between them. This is the part of the project where the physics stopped and the taste started.
Read the field note →
Driving a 60 fps WebGL scene from React panels without re-rendering it
Our club scene has ~15 control panels — levels, color grade, look presets, camera, sequencer — all ordinary React DOM. The scene they drive holds 60 fps on the machines we build on, and the scene itself never re-renders when a slider moves; the panel you are dragging does, and that split is the design. The trick is a ~40-line store pattern we now use everywhere: mutable fields for the frame loop, subscribe/snapshot for the panels, and a one-way "seam" object wherever the DOM and the canvas have to talk.
Read the field note →
Lighting presets that survive recalibration
We hand-balanced eleven brightness channels in our virtual club, auto-generated eight themed "looks" on top of them, and watched most of the looks come out unusably dark. The fix was to stop storing values and start storing intent (ratios and offsets against a frozen baseline) plus a luminance test harness so "darker than the others" is a number. Applies to the factory looks, not the ones you save. Includes one self-inflicted clamp bug and a camera preset that was secretly standing inside the crowd.
Read the field note →
One beat clock, one room
Most of the lights in our virtual club (wall, lasers, strobes, neon, mirror ball) were already on a shared bar clock and still had no idea the others existed. Hanging a set of spatial sequence modes off that clock turned nine independent effects into one instrument. The interesting parts are geographic: zones defined by where things hang, a brightness wave that sweeps the room by azimuth, and a floor rule meant to keep the hero prop lit that we only half-shipped.
Read the field note →
Your Eye vs. My Metrics
The two verdicts on one frame, ch02 at 4.51s. The HUD samples frames and reports clean; the swept-collision check integrates between them and reports a 91.5mm crossing. Neither is lying, and that gap is most of what this post is about.
Read the field note →
Writing a dance down: a machine-readable contact script for a pole routine
Motion capture records a skeleton, which is a recording of one body. We wanted the thing underneath: which hand held the pole, how high, through what arc, for how long, and who was carrying the weight. That artifact turns out to be 19 KB next to 686 KB of style curves, about 35:1 as the bytes actually serialize, and that ratio is the entire design. Labanotation had the same ambition in the 1920s, and climbers have had the informal version for decades.
Read the field note →
One rule for our AI-assisted QA: change the solver or change the gate, never both
When agents do the work and a harness grades it, the harness becomes the most attackable object in the project — not maliciously, just by ordinary drift. Our answer is a single versioned file of ratified acceptance criteria, one rule about which diffs may touch it, and a standing requirement that every tolerance carry a rationale. This is that file, the episode that produced it, and the one circularity still sitting in it that we have not resolved.
Read the field note →
Topology as Ground Truth
Depth of intersection was the wrong severity axis. A shin 5mm inside the pole that arrived from the far side is impossible motion; 20mm of pressed contact is ordinary technique. The performer could not pass a leg through steel, so her own paths tell me which side each limb belongs on, and the verdict stops being a number to tune and becomes a yes or a no. The detectors that produce it still have constants, and whether a shorter-armed body can get to the right side is a different question again.
Read the field note →
We asked three frontier models to attack our design. They contradicted each other on the fix we had already queued.
Before committing four to seven expensive sessions to the riskiest rewrite stage in the project, we wrote the whole problem out cold and sent it to three models from different labs, explicitly asking to be told why we were wrong. The useful output was not a design. It was one flat contradiction on our own next step, three methodology corrections, and a criticism that caught us rebuilding a bug we had just finished writing a lesson about. The responses themselves were not archived, so this is our retrospective account, not a transcript.
Read the field note →
The Plant That Walks
My extractor labeled 15.5 seconds of a 30-second take as a single foot "plant". Across that span the performer's ankle traveled 1224mm and her foot heading swung 179.5 degrees. She was walking, the script said standing, and every downstream system inherited the lie. Five solver campaigns died on the symptoms before anyone audited the data, and telling the truth made things measurably worse before it made them better.
Read the field note →
The check wasn't missing. It was lying.
We spent a day driving one number toward zero. Then the owner of the company watched five clips and reported five defects, four of which were the dancer's body passing through the pole — the category our collision check had been passing all day. The check existed. Its radii were hardcoded 40% thinner than the bodies they were testing. This is a post about what a green light is worth when nobody has calibrated it.
Read the field note →
The 727mm Teleport Was the Fix, Not the Bug
One knee, on one body, for one frame: 727mm in a sixtieth of a second. It took three generations of diagnosis and five failed fixes to work out that the jump was not where the defect lived, it was the leg returning to truth after half a second of confident wrongness. A debugging story about reading a timeline backwards from its loudest frame.
Read the field note →
Pause is not a fixed point: giving a debug tool the same physics as the thing it debugs
We handed our motion review page to a human for the first time and he broke it in three sentences: frame-stepping made the elbow flap like a chicken, pausing near the start seemed to restart the dance, and the space bar did nothing. Every one was real. Fixing them turned into a small essay on what "pause", "step back", and "jump to 12 s" even mean when the thing you are inspecting has seconds of internal state.
Read the field note →
Two philosophies, one take, and the metric that ranked them on noise
Two sessions attacked the same 30-second pole routine with opposite instincts about what a recorded performance even is. Violation counts said one won, 187 to 260, though only one of the three bodies looked like a rout at 2.5:1. Re-scored on magnitude and duration, they sat on top of each other around a millimeter wherever the pose was achievable, and both had hit the same wall from opposite directions: the hardest body's arms are 50 mm too short, and no amount of solver quality invents reach.
Read the field note →
