Learning OpenUSD — From Curious Questions to Real Understanding
19th March 2026
Written as I explored OpenUSD before my exam. These are real questions I asked, and the answers that actually made things click for me.
1. Overview — What is OpenUSD?
OpenUSD (Universal Scene Description) is an open-source framework developed by Pixar for describing, composing, and simulating 3D scenes. It is now the industry standard for film, VFX, games, robotics, and simulation.
Think of it like a file format + scene graph + composition engine all in one. It lets multiple departments (modelling, animation, lighting, FX) work on the same scene simultaneously without stepping on each other.
2. Stage — The Container of Everything
The Stage is the entry point to any USD scene. It is the root container that holds all objects (prims), layers, and time settings.
from pxr import Usd
stage = Usd.Stage.CreateNew("scene.usda")
stage.Save()
Think of the Stage like a theatre stage — a space where everything exists. Without a stage, there is nowhere to put your actors (prims).
Key things the stage controls:
- Which layers are loaded
- Time settings (start frame, end frame, fps)
- The entire prim hierarchy
3. Prims — Objects in the Stage
Prims (short for Primitives) are the objects that live on the stage. Everything you see in a USD scene is a prim — a sphere, a cube, a camera, a light, even an empty group.
from pxr import Usd, UsdGeom
sphere = UsdGeom.Sphere.Define(stage, "/World/MySphere")
cube = UsdGeom.Cube.Define(stage, "/World/MyCube")
Prims are organised in a hierarchy — exactly like folders on your computer:
/World ← parent prim (like a folder)
├── /World/Room ← child prim
│ ├── /World/Room/Chair ← grandchild prim
│ └── /World/Room/Table ← grandchild prim
└── /World/MySphere ← another child
If you move /World, everything inside moves with it.
4. Properties — The Data Inside a Prim
Properties are the actual data stored inside a prim. If a prim is like a file, properties are the content of that file.
sphere.GetRadiusAttr().Set(1.0)
sphere.GetDisplayColorAttr().Set([(1,0,0)]) # red color
There are two types of properties:
| Type | What | Example |
|---|---|---|
| Attribute | A value on the prim | radius, color, translate |
| Relationship | A pointer to another prim | material binding → /Materials/Red |
Properties answer the question: “What IS this object?” (its shape, color, position, size)
5. TimeCode — The Frame Number
A TimeCode is a unitless number representing a point in time — like a frame number. It has no inherent unit until the stage gives it meaning.
stage.SetStartTimeCode(1)
stage.SetEndTimeCode(60)
stage.SetMetadata("timeCodesPerSecond", 24) # 24 frames = 1 second
With timeCodesPerSecond = 24, timeCode 48 = 2 seconds of real time.
Think of timeCode as the X-axis on a graph — it is just a position on the timeline, not a value itself.
6. TimeSamples — Animation Keyframes
TimeSamples are values pinned to specific timeCodes on an attribute. This is how you animate things in USD.
sphere.AddTranslateOp().Set(Gf.Vec3d(0, 5, 0), time=1) # frame 1 → Y=5 (top)
sphere.AddTranslateOp().Set(Gf.Vec3d(0, 0, 0), time=30) # frame 30 → Y=0 (bottom)
sphere.AddTranslateOp().Set(Gf.Vec3d(0, 5, 0), time=60) # frame 60 → Y=5 (top)
USD linearly interpolates between timeSamples automatically:
Frame: 1 15 30 45 60
Y pos: 5 2.5 0 2.5 5
▲ ▲ ▲
keyframe keyframe keyframe
(yours) (yours) (yours)
You author 3 keyframes — USD fills in all 60 frames. That is the bounce you see in usdview.
TimeSeries vs TimeSamples:
- TimeSeries = the full animation from start to end (all 60 frames)
- TimeSamples = the keyframes you author (just 3 snapshots)
You can put a timeSample on every frame if needed (e.g. physics simulation, motion capture) but for simple animation, fewer keyframes is better — smaller file size and USD handles the smooth interpolation.
7. Prim and Property Paths
Every prim and property in USD has a path — a unique address to find it, just like a file path on your computer.
/World/Room/Chair ← prim path (address of the object)
/World/Room/Chair.size ← property path (address of the data inside)
from pxr import Sdf
# Get a prim by its path
chair = stage.GetPrimAtPath("/World/Room/Chair")
# Build paths programmatically
base = Sdf.Path("/World/Room")
path = base.AppendChild("Chair") # /World/Room/Chair
prop = path.AppendProperty("size") # /World/Room/Chair.size
# Check if a prim exists
chair.IsValid() # True
sofa = stage.GetPrimAtPath("/World/Room/Sofa")
sofa.IsValid() # False — doesn't exist
Path = where to find it. Properties = the actual data stored inside.
8. OpenUSD File Format
USD scenes are saved as text files you can open and read directly.
#usda 1.0
def Sphere "BouncingSphere"
{
double radius = 1.0
color3f[] displayColor = [(1, 0, 0)]
double3 xformOp:translate.timeSamples = {
1: (0, 5, 0),
30: (0, 0, 0),
60: (0, 5, 0),
}
}
Common file formats:
| Format | Type | Use |
|---|---|---|
.usda | Text (ASCII) | Human readable, good for learning |
.usdc | Binary (crate) | Compact, fast, used in production |
.usdz | Zip archive | Packages all assets together (AR, iOS) |
USD also supports plugins for other formats like .abc (Alembic) and .fbx.
9. OpenUSD Modules
USD is organised into modules — like Python packages. You import only what you need.
from pxr import Usd, UsdGeom, Sdf, Gf, UsdShade, UsdPhysics
| Module | Full Name | What it does |
|---|---|---|
Usd | Universal Scene Description | Stage, prims, properties — the main engine |
Sdf | Scene Description Foundation | Layers, file format, paths |
Gf | Graphics Foundation | Math — Vec3d, Matrix4d, colors |
UsdGeom | USD Geometry | Sphere, Cube, Mesh, Xform |
UsdShade | USD Shading | Materials and shaders |
UsdPhysics | USD Physics | Physics simulation |
pxr is the top-level package (installed on your machine). All modules live inside it.
Custom schemas — you can also define your own prim types by extending:
UsdTyped— when your prim IS a thing (e.g.RobotArm,SO101Joint)UsdAPISchemaBase— when your schema ADDS behaviour to any prim (like a mixin)usdGenSchema— the tool that generates boilerplate code for your custom schema
10. Metadata — Info About the Object
Metadata is extra information attached to a stage, prim, or property. It is not geometry data — it describes context around the object.
# Stage metadata
stage.SetMetadata("timeCodesPerSecond", 24)
# Prim metadata — who made it, version, notes
sphere.GetPrim().SetMetadata("assetInfo", {
"author": "gajanan",
"version": "1.0",
"approved": True
})
# Property metadata — document what an attribute does
sphere.GetRadiusAttr().SetMetadata("documentation", "radius of the sphere in cm")
Metadata vs Attributes:
| Metadata | Attribute | |
|---|---|---|
| Answers | What do we KNOW about it? | What IS it? |
| Example | "author: gajanan" | radius = 1.0 |
| Animatable? | No | Yes (timeSamples) |
| Like | EXIF data on a photo | The actual pixels |
Standard metadata keys:
assetInfo— asset name, version, authorcustomData— your own project-specific notesdocumentation— describe what a property does
In a real studio pipeline with hundreds of assets, metadata is how you track, annotate, and manage everything without touching the geometry.
Quick Reference Cheatsheet
from pxr import Usd, UsdGeom, Sdf, Gf
# Stage
stage = Usd.Stage.CreateNew("scene.usda")
stage.SetStartTimeCode(1)
stage.SetEndTimeCode(60)
stage.SetMetadata("timeCodesPerSecond", 24)
# Prims
sphere = UsdGeom.Sphere.Define(stage, "/World/Sphere")
# Properties
sphere.GetRadiusAttr().Set(1.0)
# TimeSamples (animation)
sphere.AddTranslateOp().Set(Gf.Vec3d(0, 5, 0), time=1)
sphere.AddTranslateOp().Set(Gf.Vec3d(0, 0, 0), time=30)
# Paths
prim = stage.GetPrimAtPath("/World/Sphere")
path = Sdf.Path("/World").AppendChild("Sphere")
# Metadata
prim.SetMetadata("customData", {"author": "gajanan"})
stage.Save()
More recent articles
- OpenUSD: Advanced Patterns and Common Gotchas. - 28th March 2026
- OpenUSD Mastery: From Composition to Pipeline — A SO-101 Arm Journey - 25th March 2026