Auto-rig / add a character
Upload a 3D character model in FBX or GLB format to use with generated motions.
Learn more about Auto-rigging.
Step 1: Prepare your character file
- Supported formats:
.fbx,.glb,.gltf - File size must be less than 30MB
- Keep the file size reasonable to speed up auto-rigging
Step 2: Upload a character
- Shell
- Python
- TypeScript
- React
- C#
API_KEY="{{apiKey}}"
CHARACTER_FILE="/path/to/character.fbx"
CHARACTER_NAME="My Character"
curl -X POST https://uthana.com/graphql \
-u $API_KEY: \
-F 'operations={
"query": "mutation ($file: Upload!, $name: String!, $auto_rig: Boolean, $auto_rig_front_facing: Boolean) { create_character(file: $file, name: $name, auto_rig: $auto_rig, auto_rig_front_facing: $auto_rig_front_facing) { character { id name } auto_rig_confidence } }",
"variables": { "file": null, "name": "'"$CHARACTER_NAME"'", "auto_rig": true, "auto_rig_front_facing": true }
}' \
-F 'map={ "0": ["variables.file"] }' \
-F '0=@'"$CHARACTER_FILE"
import asyncio
from uthana import Uthana
client = Uthana("{{apiKey}}")
async def main():
result = await client.characters.create_from_file(
"/path/to/character.fbx",
auto_rig=True,
front_facing=True,
)
print(result.character_id)
print(result.auto_rig_confidence) # 0–1.0, higher is better
asyncio.run(main())
import { UthanaClient } from "@uthana/client";
const client = new UthanaClient(process.env.UTHANA_API_KEY!);
// Browser: pass a File object from a file picker
const characterInput = document.querySelector<HTMLInputElement>("#characterFile");
const characterFile = characterInput?.files?.[0];
if (!characterFile) throw new Error("Select a character file before uploading.");
const result = await client.characters.createFromFile(characterFile, {
auto_rig: true,
front_facing: true,
});
console.log(result.character_id);
console.log(result.auto_rig_confidence); // 0–1.0, higher is better
import { useUthanaCreateCharacter } from "@uthana/react";
function UploadCharacter() {
const creator = useUthanaCreateCharacter();
return (
<input
type="file"
accept=".fbx,.glb,.gltf"
onChange={(e) => {
const file = e.target.files?.[0];
if (file) creator.create({ from: "file", file, auto_rig: true });
}}
/>
);
}
var operations = new
{
query = @"
mutation ($file: Upload!, $name: String!, $auto_rig: Boolean, $auto_rig_front_facing: Boolean) {
create_character(file: $file, name: $name, auto_rig: $auto_rig, auto_rig_front_facing: $auto_rig_front_facing) {
character { id name }
auto_rig_confidence
}
}",
variables = new { file = (object)null, name = "My Character", auto_rig = true, auto_rig_front_facing = true }
};
var map = new { ["0"] = new[] { "variables.file" } };
using var content = new MultipartFormDataContent();
content.Add(new StringContent(JsonSerializer.Serialize(operations), Encoding.UTF8, "application/json"), "operations");
content.Add(new StringContent(JsonSerializer.Serialize(map), Encoding.UTF8, "application/json"), "map");
content.Add(new StreamContent(File.OpenRead("/path/to/character.fbx")), "0", "character.fbx");
var authValue = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{_apiKey}:"));
_httpClient.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", authValue);
var response = await _httpClient.PostAsync("https://uthana.com/graphql", content);
response.EnsureSuccessStatusCode();
Create from a text prompt
Generate a character from a text description. The client handles the two-step flow (generate previews → confirm selection) automatically.
- Shell
- Python
- TypeScript
- React
- C#
Text-to-character requires two GraphQL mutations: create_character_from_text to generate previews, and a second call to confirm the selected preview. See the API reference for the full schema.
async def main():
# One-shot: automatically select the first preview
result = await client.characters.create_from_prompt(
prompt="a knight in shining armor",
name="Knight",
on_previews_ready=lambda previews: previews[0]["key"],
)
print(result.character.get("id"))
# Two-step: inspect previews before confirming
pending = await client.characters.create_from_prompt(prompt="a futuristic soldier")
# pending.previews is a list of {"key": ..., "url": ...}
# Show them to the user, then confirm:
result2 = await client.characters.generate_from_image(pending, pending.previews[0]["key"])
asyncio.run(main())
// One-shot: automatically select the first preview
const { character } = await client.characters.createFromPrompt({
prompt: "a knight in shining armor",
name: "Knight",
onPreviewsReady: (previews) => previews[0].key,
});
console.log(character.id);
// Two-step: inspect previews before confirming
const pending = await client.characters.createFromPrompt({ prompt: "a futuristic soldier" });
// pending.previews — show to user, then confirm:
const { character: confirmed } = await client.characters.generateFromImage(
pending,
pending.previews[0].key,
);
import { useUthanaCreateCharacter } from "@uthana/react";
// Auto-select first preview
function GenerateFromPrompt() {
const creator = useUthanaCreateCharacter();
return (
<button
onClick={() =>
creator.generate({
from: "prompt",
prompt: "a knight in armor",
onPreviewsReady: (previews) => previews[0].key,
})
}
disabled={creator.isPending}
>
Generate
</button>
);
}
// Manual preview selection
function GenerateWithSelection() {
const creator = useUthanaCreateCharacter();
return (
<>
<button
onClick={() => creator.generate({ from: "prompt", prompt: "a knight in armor" })}
disabled={creator.isPending}
>
Generate previews
</button>
{creator.isAwaitingSelection && (
<div>
{creator.previews?.map((p) => (
<img key={p.key} src={p.url} onClick={() => creator.confirm({ image_key: p.key })} />
))}
</div>
)}
</>
);
}
Text-to-character requires two GraphQL mutations: create_character_from_text to generate previews, and a second call to confirm the selected preview. See the API reference for the full schema.
Create from an image
- Shell
- Python
- TypeScript
- React
- C#
Image-to-character uses the create_character_from_image GraphQL mutation with a multipart upload. See the API reference for the full schema.
async def main():
result = await client.characters.create_from_image("path/to/reference.png")
print(result.character.get("id"))
asyncio.run(main())
const imageInput = document.querySelector<HTMLInputElement>("#imageFile");
const imageFile = imageInput?.files?.[0];
if (!imageFile) throw new Error("Select an image file.");
const { character } = await client.characters.createFromImage(imageFile);
console.log(character.id);
function GenerateFromImage() {
const creator = useUthanaCreateCharacter();
return (
<input
type="file"
accept="image/*"
onChange={(e) => {
const file = e.target.files?.[0];
if (file) creator.generate({ from: "image", file });
}}
/>
);
}
Image-to-character uses the create_character_from_image GraphQL mutation with a multipart upload. See the API reference for the full schema.
Step 3: Save the character ID
The response includes the character id. Keep it for generating motions and downloads.
Auto-rigging
By default, Uthana automatically attempts to rig characters that don't have a skeleton. Auto-rigging allows you to use characters that weren't originally rigged for animation. However, there are some cases where the auto-rigging has difficulty:
- Non humanoid: Missing clearly defined head, torso, two arms, or two legs. The character must have a recognizable bipedal humanoid structure.
- Extreme proportions: Head or other body parts so oversized they would interfere with standard humanoid skeletal mapping (slight stylization like cartoon characters is acceptable).
- Limb intersections: Arms, legs, or other body parts visibly intersecting, overlapping, or clipping through the body or each other.
- Flowing clothing: Long skirts, robes, capes, or dresses that obscure or merge the legs into a single volume.
- Large extra appendages: long tails, large wings, large extra limbs, long horns, or any large appendages beyond standard human anatomy. Small appendages are OK, such as cat ears.
The result of the gql mutation will return auto_rig_confidence score. 1.0 indicates high confidence in the result, lower scores indicate less confidence.
This confidence score can be used to filter out characters with one or more of the above difficulties.
Default behavior
auto_rig:true(default) - Automatically rig the character if no skeleton is foundauto_rig_front_facing:true(default) - Make the auto-rigged character face forward
Timing
- Auto-rigging typically takes 30-60 seconds to complete
- The upload job has a timeout of 5 minutes (300 seconds)
- If your character already has a skeleton, no auto-rigging occurs and the upload is faster
Disabling auto-rigging
If your character already has a skeleton rig, you can disable auto-rigging to speed up the upload:
Rerigging a Skeleton
During upload you also have the ability to specify a target skeleton for your character, using the rerig_target parameters.
rerig_target:null(default) - When specified the skeleton of the character will be changed to the desired target. Currently only "r15" (the standard Roblox skeleton) and "ue5" (the standard Unreal Engine 5 skeleton) are supported.
- Shell
- Python
- TypeScript
- React
- C#
curl -X POST https://uthana.com/graphql \
-u $API_KEY: \
-F 'operations={
"query": "mutation ($file: Upload!, $name: String!, $auto_rig: Boolean) { create_character(file: $file, name: $name, auto_rig: $auto_rig) { character { id name } } }",
"variables": { "file": null, "name": "'"$CHARACTER_NAME"'", "auto_rig": false }
}' \
-F 'map={ "0": ["variables.file"] }' \
-F '0=@'"$CHARACTER_FILE"
async def main():
result = await client.characters.create_from_file(
"/path/to/character.fbx",
auto_rig=False,
)
asyncio.run(main())
const result = await client.characters.createFromFile(characterFile, { auto_rig: false });
creator.create({ from: "file", file, auto_rig: false });
variables = new { file = (object)null, name = "My Character", auto_rig = false, auto_rig_front_facing = true }
Character requirements
Your character must have essential joints for retargeting to work:
- Pelvis
- Left and right hips
- Left and right shoulders
If these joints are missing, the upload will fail with an error.
Known errors
Common errors you may encounter:
- "Only FBX and GLB files are supported" - Your file format is not supported. Use
.fbxor.glbfiles only. - "The provided file is too large, it must be less than [size]MiB" - File size exceeds the limit (30MB). The error message will include the actual size limit.
- "Maximum number of characters reached" - Your organization has reached its character limit. Upgrade your plan or delete unused characters.
- "Essential joints missing. Please ensure your character has a basic bone structure (pelvis, shoulders, hips)." - Your character model lacks required skeleton joints.
- "Auto-rigging failed: [error]" - Auto-rigging encountered an error. The character may not be suitable for automatic rigging (e.g., non-humanoid structure, extreme proportions, or other compatibility issues). The error message will include specific details about what went wrong.
- "Auto-rigging job not done after [timeout] seconds" - Auto-rigging exceeded the timeout. Try uploading a smaller or simpler character model. The error message will include the actual timeout value.
- "No rig found in scene. Please upload a rigged character." - No skeleton was found and auto-rigging was disabled or failed.
Related docs
- Asset management for full character and motion workflows