In the rapidly evolving landscape of 3D simulation, spatial computing, and digital twins, developers need more than just a graphics renderer. They need an extensible, robust engine capable of processing complex physical scenes, orchestrating workflows, and rendering photorealistic environments in real-time.

Enter the NVIDIA Omniverse Kit SDK.

Omniverse Kit is the foundational toolkit used to build all official NVIDIA Omniverse applications (such as USD Composer, USD Explorer, and Isaac Sim). It is designed to be a highly modular, runtime-extensible framework that makes it easy to construct specialized tools for manipulating OpenUSD (Universal Scene Description) environments.

In this comprehensive guide, we will dive deep into the architecture of the Omniverse Kit SDK, explore how to define custom applications using .kit files, compare the predefined application templates, and walk through creating and registering a custom Python extension.


📸 SCREENSHOT PLACEHOLDER 1: Omniverse Kit Architecture Diagram Insert a graphic or diagram depicting the Omniverse Kit stack: the hardware layer at the bottom, the Kit Kernel runtime, the core C++/Python extensions (omni.ui, omni.usd, omni.kit.commands) in the middle, and custom applications/extensions sitting on top.


What is the Omniverse Kit SDK?

At its core, Omniverse Kit is a lightweight runtime engine that does almost nothing by itself. Instead, it relies on a micro-frontend/micro-service style architecture of Extensions.

An Extension is the basic building block in Kit. Everything—from the viewport renderer, file browsers, and physics engine to the main menu bar and window layouts—is implemented as an individual extension. By combining different subsets of extensions, you can build anything from a headless REST microservice that processes USD files on a server to a fully featured CAD application.

Core Runtime Principles:

  1. The Kit Kernel: Written in C++ for maximum performance, the Kernel handles the core application loop, plugin loading, python scripting integration, and event dispatching.
  2. The Update Loop: Kit runs on a standard engine update loop. Extensions can register callbacks at different stages of the frame (e.g., Early, Physics, Rendering, Present) to coordinate logic execution.
  3. OpenUSD Integration: Kit is built from the ground up for OpenUSD. The scene state is represented as a USD Stage, and changes are driven asynchronously by extensions communicating through events.

Options for Defining Kit Applications

To build a custom application, you do not write a monolithic C++ main function. Instead, you create a declarative application manifest using a .kit file.

A .kit file is written in TOML format. It instructs the Kit Kernel which extensions to download and load, what directories to search, and what settings to apply on startup.

The Structure of a .kit File:

A typical .kit file contains three primary sections:

  1. [package]: Contains metadata like the application name, version, author, and description.
  2. [dependencies]: Declares the list of extensions that the application requires. When the app starts, Kit automatically resolves, downloads, and loads these extensions and their transient dependencies.
  3. [settings]: A global configuration registry. Here, you can configure window sizes, layout options, default USD renderers, search paths for assets, or override settings for specific extensions.

Here is a simplified example of a custom .kit application definition:

[package]
title = "My Custom USD Layout Editor"
version = "1.0.0"
description = "A custom lightweight editor for organizing USD scenes."

[dependencies]
# Core Kit UI and Windowing
"omni.kit.uiapp" = {}
"omni.kit.renderer.init" = {}
"omni.kit.viewport.window" = {}

# Menu and Stage navigation
"omni.kit.menu.utils" = {}
"omni.usd" = {}

# Our custom extension (more on this below!)
"company.usd.layout_helper" = {}

[settings]
# Configure default window sizes
app.window.width = 1280
app.window.height = 720

# Configure render settings
rtx.post.aa.op = 3 # Enable FXAA

To run this application, you pass it to the Kit executable:

./kit/omni.app.exe path/to/my_app.kit

Predefined Application Templates

Starting a new project from scratch can be daunting. To simplify setup, NVIDIA provides the Omniverse Kit App Template (kit-app-template) repository.

By running the interactive wizard via ./repo.sh template new (or repo.bat on Windows), you can initialize a template project. The SDK provides several pre-built configurations tailored for different workflows:


📸 SCREENSHOT PLACEHOLDER 2: Interactive Template Wizard Insert a screenshot of the command-line interface running ./repo.sh template new. Highlight the interactive prompt showing the available template types (Kit Service, Kit Base Editor, USD Composer, etc.) to illustrate the project startup process.


Template Name Graphical UI? Primary Target Use Case Key Included Extensions
Kit Service Headless (No) Microservices, headless USD automation, data ingestion pipelines, and HTTP/REST endpoints. omni.services.core, omni.services.transport.http
Kit Base Editor Yes Lightweight, custom CAD/3D editors, viewport tooling, and minimal USD layouts. omni.ui, omni.kit.viewport.window, omni.kit.menu.utils
USD Composer Yes Complex USD layout editing, high-fidelity lighting setups, physics/particle simulations, and rendering. omni.physx, omni.rtx.shadercache, omni.kit.tool.asset_importer
USD Explorer Yes Presentation, design reviews, lightweight layout inspection, and multi-user collaboration. omni.kit.collaboration.presence, omni.kit.search.files
USD Viewer Yes (Minimal) Viewport-only streaming, remote rendering, and high-performance WebRTC browser interaction. omni.services.stream.webrtc, omni.kit.livestream.native

Step-by-Step: Adding a Custom Extension to a Kit App

Let’s walk through how to build a custom Python extension and integrate it into your custom Kit application. We will create a simple extension that adds a custom panel with a button to our UI.

Step 1: Generate the Extension Scaffold

Navigate to your cloned kit-app-template directory and run the wizard:

# On Linux
./repo.sh template new
# On Windows
.\repo.bat template new
  1. Select Extension when prompted.
  2. Enter the extension name: company.usd.layout_helper.
  3. Provide a display name: USD Layout Helper.
  4. The wizard will create a scaffold directory under source/extensions/company.usd.layout_helper/.

📸 SCREENSHOT PLACEHOLDER 3: Extension Directory Layout Insert a screenshot of an IDE (e.g., VS Code) showing the file explorer containing the newly created extension folder. Highlight the config/extension.toml and company/usd/layout_helper/extension.py files.


Step 2: Configure the Extension Manifest (config/extension.toml)

Inside the extension’s configuration folder, you will find extension.toml. This file defines metadata and dependencies specifically for this extension.

[package]
title = "USD Layout Helper"
description = "Adds simple alignment and layout shortcuts to the viewport."
version = "1.0.0"
authors = ["Your Name"]

# Define dependencies on other extensions
[dependencies]
"omni.ui" = {}
"omni.usd" = {}

# Define Python Module to load
[[python.module]]
name = "company.usd.layout_helper"

Step 3: Implement the Extension Logic (extension.py)

Open the entrypoint python file (e.g., source/extensions/company.usd.layout_helper/company/usd/layout_helper/extension.py). Every Kit extension implements a lifecycle class that inherits from omni.ext.IExt.

We will implement on_startup to build a UI window and on_shutdown to clean it up.

import omni.ext
import omni.ui as ui
import omni.usd

class LayoutHelperExtension(omni.ext.IExt):
    # This is called when the extension is loaded/activated
    def on_startup(self, ext_id):
        print("[company.usd.layout_helper] Starting Layout Helper Extension")

        # Create a UI Window
        self._window = ui.Window("Layout Helper Panel", width=300, height=200)
        with self._window.frame:
            with ui.VStack(spacing=10):
                ui.Label("USD Scene Tools", style={"font_size": 18}, alignment=ui.Alignment.CENTER)
                
                # Add a button
                ui.Button("Center Selected Asset", clicked_fn=self._on_center_button_clicked)

    def _on_center_button_clicked(self):
        # Get active USD stage
        ctx = omni.usd.get_context()
        stage = ctx.get_stage()
        selection = ctx.get_selection().get_selected_prim_paths()

        if not selection:
            print("No prim selected to center!")
            return
            
        print(f"Centering selection: {selection}")
        # Custom logic to modify USD transform attributes would go here...

    # This is called when the extension is disabled or application exits
    def on_shutdown(self):
        print("[company.usd.layout_helper] Shutting down Layout Helper Extension")
        if self._window:
            self._window.destroy()
            self._window = None

Step 4: Register the Extension in Your .kit App

To load your extension automatically when your application starts, open your application’s .kit file (e.g., apps/my_app.kit) and add your extension under [dependencies]:

[dependencies]
# ... other core dependencies
"company.usd.layout_helper" = {}

Step 5: Build and Launch

Sync the project dependencies and run your custom application:

# Compile and build extensions
./repo.sh build

# Launch the app
./repo.sh launch

Your application will open, loading your custom layout helper extension and displaying your custom panel with the button inside the layout!


📸 SCREENSHOT PLACEHOLDER 4: Custom UI Panel in the Viewport Insert a screenshot of the launched Kit Base Editor showing the running application interface. Place a red box/callout highlighting the “Layout Helper Panel” window with the “Center Selected Asset” button, showcasing how the custom python extension renders inside the UI.


Packaging and Distribution

Once your custom application is complete, you need to distribute it. Omniverse Kit SDK provides packaging scripts that bundle all dependencies and the runtime environment.

When building a distribution bundle via ./repo.sh package, you can choose between two main modes:

  1. Fat Packages:
    • Contains all dependencies, external Python packages, and asset files embedded inside the folder.
    • Pros: Completely self-contained. Can run in offline/air-gapped environments without downloading external assets.
    • Cons: Massive file size (often several gigabytes).
  2. Thin Packages:
    • Contains only your custom application configs and custom extensions. All external NVIDIA dependencies and libraries are downloaded dynamically on first run.
    • Pros: Small distribution size, quick to upload/download.
    • Cons: Requires active internet connection during initialization.

Wrap Up & Next Steps

The NVIDIA Omniverse Kit SDK represents a paradigm shift in 3D tool development. By moving away from rigid, monolithic software designs and embracing a modular, extension-based framework, Kit allows developers to assemble tailored OpenUSD tools with speed and precision.

To take your learning further:


This post is part of the Omniverse Guides series. Got questions about building custom USD extensions? Leave a comment below!