Issue with Copying Animations to Another Humanoid

I’m writing a script for viewport arms which follow the player; these viewport arms are supposed to replicate the animations being played by the player model in workspace.
Below is all of the code within the script related to the playing of animations in the viewport arms:

local animator = script.Parent:FindFirstChildWhichIsA("Humanoid"):FindFirstChildWhichIsA("Animator")

local conn2
conn2 = animator.AnimationPlayed:Connect(function(anim)
	if not arms or not arms.Parent then
		conn2:Disconnect()
		return
	end
	
	local armsanimtrack = arms.Humanoid.Animator:LoadAnimation(anim.Animation)
	armsanimtrack:Play(0,0,0)
	
	local thersconnofalltime
	thersconnofalltime = game:GetService("RunService").PreAnimation:Connect(function()
		if armsanimtrack.Speed ~= anim.Speed then
			armsanimtrack:AdjustSpeed(anim.Speed)
		end
		if armsanimtrack.WeightCurrent ~= anim.WeightCurrent then
			armsanimtrack:AdjustWeight(anim.WeightCurrent)
		end
	end)
	
	anim.Stopped:Once(function()
		armsanimtrack:Stop()
		thersconnofalltime:Disconnect()
	end)
end)

This function does not work. When playing the game, it is very easily noticeable that the animations, plain and simple, are not playing. I did some investigation with the script myself, and I noticed that it worked when I changed all mentions of WeightCurrent to WeightTarget, but that’s not what I’m going for, since that ignores the FadeTime argument.
I’ve also tried using .Changed and :GetPropertyChangedSignal(), however those do not pick up on changes made to WeightCurrent and Speed.

I would appreciate any help I can get. I’ve been scratching my head for quite some time now.

Have you been able to figure out anything? I’ve been dabbling with a similar issue for a couple days to no avail. In my case I am just using WeightTarget and using the Fade In property when playing to smooth it out a little although it isn’t ideal. On every frame I also adjust the TimePosition, which helps a bit with the syncing. However one notorious issue I am currently facing is that the position of LowerTorso isn’t following the original humanoid offset correctly, despite being a clone of it.

I’m not sure if it is related to the same problem of WeightTarget, given that checking all animations playing at that frame shows the exact same values on both Source and Target. I’ll report back if I can find anything. There has to be a way to make a carbon copy of all animation tracks in their current state from one humanoid to another. I’ve seen people making 1:1 mirrors.

I tried the “Transform” method that is mentioned in a few threads but they give me the same issue.

I haven’t been able to figure out anything, but an idea I had was to make attachments as to where Motor6Ds should be connected.
For example, in an R6 rig, you could place two attachments at the left shoulder, one in the arm, one in the torso. You can then compare their CFrames to get a general idea of the difference between them, then being able to apply the transformations to a separate rig.

No clue if this works yet, though. I’ll follow up with a code example once I get around to it.

For this to work correctly, all animations would have to be either on the server or on the client, aswell as the scripts for it. Also, for them to correctly replicate you will need a fake HumanoidRootPart, and Torso with all the norm motor6D in them.

Great news, I got my method to work!
From my testing, it’s consistent enough for my objectives. This script should be placed in either a LocalScript or Script with RunContext set to “Client”:

local rs = game:GetService("RunService")

local attachmentpairs = {}
local rig = workspace:WaitForChild("Rig")

for i, desc in ipairs(script.Parent:GetDescendants()) do
	if desc:IsA("Motor6D") then
		local torsoatt = Instance.new("Attachment")
		torsoatt.CFrame = desc.C0
		torsoatt.Parent = desc.Part0
		
		local limbatt = Instance.new("Attachment")
		limbatt.CFrame = desc.C1
		limbatt.Parent = desc.Part1
		
		table.insert(attachmentpairs, {
			MotorName = desc.Name,
			MotorParentName = desc.Parent.Name,
			Pair = {torsoatt, limbatt},
			PairDefaults = {desc.C0, desc.C1}
		})
	end
end

rs.RenderStepped:Connect(function()
	for i, pair in ipairs(attachmentpairs) do
		local p = rig:FindFirstChild(pair.MotorParentName)
		if p then
			local m = p:FindFirstChild(pair.MotorName)
			if m then
				m.C0 = pair.PairDefaults[1] * pair.Pair[1].WorldCFrame:ToObjectSpace(pair.Pair[2].WorldCFrame)
			end
		end
	end
end)

Apologies if my code looks a little messy; I’m a very scatterbrained programmer.
Essentially, what this does is create two attachments where any Motor6Ds connect, one in each part. Every tick of RunService.RenderStepped, the distance between these attachments is calculated, then manipulating the Motor6Ds in the mirroring Rig.

It should be mentioned however that there is probably a better way to do this, but this method will also account for manipulations not performed by animations, which is a plus. I’ll mark this as a solution, unless anyone finds a bug with my code or finds a better solution.