Retarget to any character
Apply motions generated with Uthana to your custom 3D characters. Retargeting automatically adapts animations to work with different character skeletons and proportions.
Overview
When you generate a motion with Uthana, it's created for a specific character skeleton. Retargeting allows you to apply that same motion to any character you've uploaded, automatically adapting the animation to match different bone structures and proportions.
Learn more about Retargeting.
How retargeting works
- Generate or select a motion: Create a motion using text-to-motion, locomotion, or video-to-motion, or use an existing motion
- Choose your character: Select the character you want to apply the motion to
- Download the retargeted motion: The motion is automatically retargeted when you download it with a specific character ID
Step-by-step tutorial
Step 1: Upload your character
First, upload your custom character model. Uthana will automatically rig it if needed.
See the Auto-rig / add a character guide for detailed instructions on uploading characters, including supported formats, auto-rigging options, and code examples for all languages.
After uploading, save the character ID from the response. You'll need it for retargeting motions.
Step 2: Generate a motion
Generate a motion using Text to motion, Locomotion, or Video to motion. Save the motion ID from the response—you'll need it for downloading the retargeted motion.
Step 3: Download retargeted motion
When you download a motion with a specific character ID, Uthana automatically retargets it to that character's skeleton.
- Shell
- Python
- TypeScript
- React
- C#
# Download retargeted motion as FBX (filename is customizable)
curl -L "https://uthana.com/motion/file/motion_viewer/$CHARACTER_ID/$MOTION_ID/fbx/motion.fbx" \
-u $API_KEY: \
-o retargeted-motion.fbx
# Download retargeted motion as GLB (filename is customizable)
curl -L "https://uthana.com/motion/file/motion_viewer/$CHARACTER_ID/$MOTION_ID/glb/motion.glb" \
-u $API_KEY: \
-o retargeted-motion.glb
import asyncio
from uthana import Uthana
client = Uthana("{{apiKey}}")
async def main():
# Download retargeted motion as FBX for a specific character
fbx_data = await client.motions.download(CHARACTER_ID, MOTION_ID, output_format="fbx")
with open("retargeted-motion.fbx", "wb") as f:
f.write(fbx_data)
# Download retargeted motion as GLB
glb_data = await client.motions.download(CHARACTER_ID, MOTION_ID, output_format="glb")
with open("retargeted-motion.glb", "wb") as f:
f.write(glb_data)
asyncio.run(main())
import { UthanaClient } from "@uthana/client";
const client = new UthanaClient(process.env.UTHANA_API_KEY!);
// Download retargeted motion as FBX for a specific character
const fbxBuffer = await client.motions.download(CHARACTER_ID, MOTION_ID, { output_format: "fbx" });
// Download retargeted motion as GLB
const glbBuffer = await client.motions.download(CHARACTER_ID, MOTION_ID, { output_format: "glb" });
import { useUthanaClient } from "@uthana/react";
function DownloadRetargetedMotion({
characterId,
motionId,
}: {
characterId: string;
motionId: string;
}) {
const client = useUthanaClient();
async function download(format: "glb" | "fbx") {
const buffer = await client.motions.download(characterId, motionId, { output_format: format });
const mime = format === "glb" ? "model/gltf-binary" : "application/octet-stream";
const url = URL.createObjectURL(new Blob([buffer], { type: mime }));
const a = document.createElement("a");
a.href = url;
a.download = `retargeted-motion.${format}`;
a.click();
}
return (
<div>
<button onClick={() => download("fbx")}>Download FBX</button>
<button onClick={() => download("glb")}>Download GLB</button>
</div>
);
}
public async Task<byte[]> DownloadRetargetedMotionAsync(string characterId, string motionId, string format = "fbx", string filename = "motion")
{
var downloadUrl = $"https://uthana.com/motion/file/motion_viewer/{characterId}/{motionId}/{format}/{filename}.{format}";
var response = await _httpClient.GetAsync(downloadUrl);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsByteArrayAsync();
}
// Usage
var fbxData = await DownloadRetargetedMotionAsync(characterId, motionId, "fbx");
var glbData = await DownloadRetargetedMotionAsync(characterId, motionId, "glb");
Downloading motion-only files
For animation data without the character mesh (useful for applying to characters in your game engine), download the motion-only GLB:
- Shell
- Python
- TypeScript
- React
- C#
# Download motion-only GLB (animation data without character mesh, filename is customizable)
curl -L "https://uthana.com/motion/animation/motion_viewer/$CHARACTER_ID/$MOTION_ID/glb/motion.glb" \
-u $API_KEY: \
-o motion-only.glb
async def main():
data = await client.motions.download(CHARACTER_ID, MOTION_ID, output_format="glb", no_mesh=True)
with open("motion-only.glb", "wb") as f:
f.write(data)
asyncio.run(main())
const buffer = await client.motions.download(CHARACTER_ID, MOTION_ID, {
output_format: "glb",
no_mesh: true,
});
const buffer = await client.motions.download(characterId, motionId, {
output_format: "glb",
no_mesh: true,
});
public async Task<byte[]> DownloadMotionOnlyGlbAsync(string characterId, string motionId, string filename = "motion")
{
var downloadUrl = $"https://uthana.com/motion/animation/motion_viewer/{characterId}/{motionId}/glb/{filename}.glb";
var response = await _httpClient.GetAsync(downloadUrl);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsByteArrayAsync();
}
// Usage
var motionOnlyData = await DownloadMotionOnlyGlbAsync(characterId, motionId);
Next steps
- Learn about Text to motion for generating animations
- Learn about Locomotion for directional, looptable locomotion clips
- Explore Asset management for managing your characters and motions
- Check the API reference for complete schema documentation