Quick answer: Add a SkeletonModifier3D child to the Skeleton3D. Override _process_modification to set bone poses every frame.

You write a one-shot bone override for procedural head-look. Animation overwrites it the next frame. Set in _ready — lost on reparent.

The Symptom

Bone snaps back to its bind pose or animation curve as soon as something else updates the skeleton.

The Fix

# LookAtModifier (extends SkeletonModifier3D)
extends SkeletonModifier3D

@export var bone_name: String = "Head"
@export var target: Node3D

func _process_modification():
    var sk = get_skeleton()
    var idx = sk.find_bone(bone_name)
    if idx == -1 or target == null: return
    var rest = sk.get_bone_global_rest(idx)
    var look = (target.global_position - sk.global_position * rest.origin).normalized()
    var rot = Quaternion(Vector3.FORWARD, look)
    sk.set_bone_pose_rotation(idx, rot)

Modifier runs after animation each frame. Override sticks because it’s reapplied every tick, by design.

Verifying

Move the target node. Head bone tracks. Pause animation player — head still tracks (modifier runs independently of AnimationPlayer).

“Modifier reapplies. Override stays. Animation plays underneath.”

Related Issues

For tween signal, see tween signal. For TextMesh3D, see TextMesh3D.

Modifier stays applied. Bone holds.