s&box/field-guide
the symptom, in your words

"Keep quaternion keys hemisphere-continuous"

✓ verified on engine 26.07lane: Rigging & animationposted
▸ SYMPTOM
  • Scripted animation keyframes look correct at each key pose, but the character explodes between keys — limbs swing a full 360 degrees during interpolation.
  • The artifact only appears in transitions, never at the keyed frames themselves.
  • The problem is intermittent — some rotations interpolate fine, others violently detour.
▸ CAUSE

Quaternions q and -q represent the same orientation but interpolation between them takes opposite paths around the 4D sphere. When you convert euler angles to quaternions per-keyframe independently, some keys may land on q and their neighbors on -q. The interpolator (slerp) doesn't know they're equivalent and takes the long path — a near-360 degree swing instead of a tiny step.

This is invisible at the key poses (both q and -q produce the same visible rotation) and only manifests during the blend.

▸ FIX

Negate the quaternion if it's on the wrong hemisphere:

snippet
# Python (Blender scripted keyframes)
import mathutils

prev_q = None
for frame in keyframes:
    q = compute_rotation(frame)  # euler to quat

    if prev_q is not None and prev_q.dot(q) < 0:
        q = -q  # flip to same hemisphere

    set_keyframe(bone, frame, q)
    prev_q = q
snippet
// C# equivalent
Quaternion prev = Quaternion.Identity;
foreach (var key in keys)
{
    var q = key.Rotation;
    if (Quaternion.Dot(prev, q) < 0f)
        q = -q;

    ApplyKey(bone, key.Frame, q);
    prev = q;
}

The check is one dot product per key. Apply it at authoring time (when generating keyframes) — the runtime interpolator doesn't need to change.

▸ WHY IT WORKS

A quaternion lives on the surface of a 4D unit sphere. Slerp always takes the shorter arc between two points on that sphere. When q and -q are on opposite hemispheres (dot product < 0), the "short arc" is actually the long way around in 3D rotation space. Negating one to match the other's hemisphere ensures slerp takes the minimal rotation path — which is what you see as a smooth transition instead of a violent spin.

This applies to any scripted keyframe pipeline — Blender Python, runtime procedural animation, or custom FBX exporters. The engine's own AnimGraph cross-fades handle this internally, but direct sequence playback with scripted keys does not.

Verified on engine 26.07 — seen in a real project.
s&box moves fast; an undated fix is a liability. Spot a stale detail?