Skip to content
Velaris

Building in public

The mascot that kept flying backwards

A 3D robot flies through our homepage. Getting it to face the right way took three failed fixes and taught me to stop debugging by eye.

Vithu ·

There’s a small robot riding a rocket through our homepage. It enters from the right, loops the headline, and flies down the page as you scroll, leaving a contrail.

Getting it to face forwards took three failed attempts, and each failure taught me something about debugging things you can see. Which sounds like it should be the easy case, and is actually the trap.

Failure one: fixing the symptom

The rocket flew backwards during its entrance. Nose pointing where it came from, contrail leading the way.

Obvious cause: the model’s forward axis didn’t match what the animation assumed. So I added a 180° flip to the yaw. It looked right.

Then it looked wrong again somewhere else — correct on entry, backwards during the orbit. I’d fixed one moment and broken another, because two separate pieces of code were both setting the yaw and I’d only found one. The flip papered over a conflict rather than resolving it.

Lesson: if a fix works in one place and breaks another, you patched a symptom. I unified the yaw calculation into a single rule. That was the actual bug.

Failure two: trusting my eyes

With one rule, it was still backwards. So I flipped the sign again.

The user’s response: “what you did? now the rocket is coming backwards. turning front and moving around in backwards.”

I had been debugging by looking at a render and deciding which way a stylised robot on a rocket was pointing. At that size, with motion blur from a contrail, my confidence was completely unearned.

So I stopped guessing and instrumented it — exposed the live pose values on window, flew the thing, and read the actual numbers alongside the actual geometry. The model’s nose is +Z. The flame is at local −Z. No flip was needed anywhere; I’d introduced the bug I was trying to fix, twice.

Lesson: “I can see it” is not the same as “I measured it.” Rendering is exactly where this illusion is strongest.

Failure three: verifying the wrong object

Separately, the rocket was rendering behind a panel it should have flown in front of. I set a z-index on canvas.parentElement, checked that element’s computed style, saw the value I’d set, and reported it fixed.

It wasn’t. React Three Fiber nests the canvas in two divs, and the one I’d targeted was position: static — where z-index does nothing at all. I’d verified my change by reading back the same wrong element, which confirmed only that assignment works in JavaScript.

The fix was to walk up to the ancestor that’s actually position: fixed and set it there. The lesson is the harsher one: a test that reads back your own write proves nothing. I should have taken a screenshot and looked at pixels, which is what I did after being told, again, that it wasn’t working.

The bug that only appeared later

Months of this and the trail still occasionally broke into loose triangles instead of a smooth ribbon.

The cause was genuinely interesting. The contrail samples the nozzle position on a fixed 0.028s timestep. Each frame it works out how many samples that frame spans — and it copied the current position into every one of them.

Above ~36fps that’s always one sample, so it never showed. Below it, each frame stacked several identical points. A zero-length segment has no direction, so the code fell back to a constant, the billboard normal flipped against its neighbours, and the quad folded into a bowtie — which renders as exactly the loose triangles you’d see.

The fix is to interpolate along the path actually travelled rather than duplicating a point. At high frame rates it’s a strict no-op, so nothing changed for anyone who never saw the bug.

Lesson: frame-rate-dependent bugs hide from whoever has the fastest machine. And when I first blamed a heavier page for the frame drop, I was wrong about that too — measurement showed a steady 60fps, and the defect had been latent since the beginning.

Why bother

A reasonable question. This is a marketing site for an AI operating system; nobody buys because of a 3D robot.

Two answers. The honest small one: it’s a mascot on a rocket, it’s fun, and building things that are fun is allowed.

The one I actually believe: the discipline transfers. Every failure above is the same failure — accepting a plausible signal instead of a definitive one. Patching where it broke instead of where it was wrong. Believing a render. Verifying against the object I’d just written to.

Those exact mistakes, made against an agent that can send email on your behalf, are considerably more expensive than a robot facing the wrong way. Practising the habit on something harmless is cheap.

To make the choreography reproducible we ended up building a small visual editor for the flight path, which loads the same constants and the same model the site ships. It turned “drag it until it looks right” into something with a saved, reviewable output.

The robot faces forwards now. Scroll the homepage and watch it thread the headline — it passes in front of one line and behind the next.