Cloning from PlayerGUI to Workspace

For some background, I have a guitar customizer in the PlayerGUI. The player can customize all the guitar’s parts and preview the guitar in a ViewportFrame. The player clicks a submit button which is supposed to bring the ViewportFrame’s model “to life.”
I am trying to clone the model from the ViewportFrame to the Workspace, using RemoteEvents located in ReplicatedStorage.
The reason why I am trying to do it this way is it is much easier.

Client-sided script, cropped:

button.Activated:Connect(function()
	game.ReplicatedStorage.changeInstrument:FireServer(game.Players.LocalPlayer.PlayerGui.Instruments.Customizer.Viewer.ViewportFrame:FindFirstChildOfClass("Model"))
	-- This passes through the model inside the ViewportFrame as a parameter of FireServer()
end)

Server-sided function, cropped:

game.ReplicatedStorage.changeInstrument.OnServerEvent:Connect(function(player, model)
	if seat.Occupant and seat.Occupant.Name ~= "LeadBot" then
		if player == game.Players:GetPlayerFromCharacter(seat.Occupant.Parent) then			
			play:Destroy()
			play = model:Clone()
			play.Parent = instruments
			makeWelds()
		end
	end
end)

With the various combinations of parenting or cloning, nothing seems to work. Either the model doesn’t update in-game, or it does but only client-side.
Any help is appreciated.

1. Serialize Model Data on the Client

First, serialize the model’s parts and properties into a table on the client side.

local function serializeModel(model)
	local partsData = {}
	for _, part in ipairs(model:GetDescendants()) do
		if part:IsA("BasePart") then
			table.insert(partsData, {
				ClassName = part.ClassName,
				Name = part.Name,
				Position = part.Position,
				Orientation = part.Orientation,
				Color = part.Color,
				Material = part.Material,
				Size = part.Size,
				-- Add any other properties you need here
			})
		end
	end
	return partsData
end

button.Activated:Connect(function()
	local model = game.Players.LocalPlayer.PlayerGui.Instruments.Customizer.Viewer.ViewportFrame:FindFirstChildOfClass("Model")
	if model then
		local modelData = serializeModel(model)
		game.ReplicatedStorage.changeInstrument:FireServer(modelData)
	end
end)

2. Reconstruct the Model on the Server

On the server side, reconstruct the model from the serialized data.

local function reconstructModel(modelData)
	local model = Instance.new("Model")
	for _, partData in ipairs(modelData) do
		local part = Instance.new(partData.ClassName)
		part.Name = partData.Name
		part.Position = partData.Position
		part.Orientation = partData.Orientation
		part.Color = partData.Color
		part.Material = partData.Material
		part.Size = partData.Size
		-- Set any other properties you serialized
		part.Parent = model
	end
	return model
end

game.ReplicatedStorage.changeInstrument.OnServerEvent:Connect(function(player, modelData)
	if seat.Occupant and seat.Occupant.Name ~= "LeadBot" then
		if player == game.Players:GetPlayerFromCharacter(seat.Occupant.Parent) then			
			if play then
				play:Destroy()
			end
			play = reconstructModel(modelData)
			play.Parent = instruments
			makeWelds()
		end
	end
end)

This looks like it could work, but is hierarchy maintained within the result model? My model is also made up of MeshParts.

This one only works with the instances of base parts I didn’t really see your hierarchy but just adjust the script to your hierarchy

In the server side script, you can simply do this to set the properties of the part.

local part = Instance.new(partData.ClassName)
partData.ClassName = nil -- removes `ClassName` from the dictionary `partData`
for property, value in partData do
	part[property] = value
end
part.Parent = model