Skip to content

Tempo

The TempoSample street sweeper with a semantic label overlay

The TempoSample project: a street sweeper driving the Lower Sector environment, with semantic labels overlaid.

Tempo is a collection of simulation-focused plugins for Unreal Engine. It makes the power of Unreal accessible to simulation and robotics developers, with plugins for client APIs, sensor simulation, agent behaviors, and more.

Tempo is the foundation on which you build a simulator for your application — a set of composable plugins, not a turnkey product.

  • Get running in an hour


    Install the prerequisites, add Tempo to a project, build it, and drive a simulation from a Python REPL.

    Getting Started

  • Understand the model


    Deterministic time, the units and coordinate frame the API speaks, and how actors are named over the wire.

    Concepts

  • Explore the plugins


    Time and the gRPC server, world control, sensors, movement, agents, geography, procedural content.

    Plugins

  • Look up an RPC


    Every service, RPC, message and field — generated from the .proto files on every build.

    gRPC API Reference

Why Tempo

Tempo is for building your own robotics simulator on modern Unreal Engine and driving all of it from code. Among great simulators (CARLA, AirSim/Colosseum, Gazebo, Webots, Isaac Sim, Genesis), here is an honest look at where it fits.

What Tempo is good at

Key feature What it means for your simulator
Modern Unreal + ecosystem Runs on UE 5.7 / 5.8 — Lumen, Nanite, PCG, Niagara, MetaHuman, Chaos, and the huge library of real-time content the engine community ships, all for free.
gRPC API, no ROS required The primary interface is gRPC: language-agnostic, schema-first, with HTTP/2 streaming. Clients connect across machines and platforms (Rust on Linux → sim on a Mac) — a step up from the older msgpack-RPC interfaces some sims use, and not tied to an in-process language.
Code-generated, reflection-based control One set of .proto files generates Python / Rust / C++ clients (sync + async). Spawn any actor and get/set any property over the wire — no engine code needed.
High-fidelity sensors Cameras with multiple lens models (incl. wide-FOV fisheye), semantic + instance segmentation, 2D bounding boxes, and hardware H.264 streaming; lidar with per-beam calibration and material-derived reflectivity.
Deterministic time Pause / play / step and wall-clock vs. fixed-step time, all over the API — built for reproducible runs and data generation.
Native, optional ROS 2 rclcpp runs in-process (no separate bridge process) and is entirely optional.
Runs where you work Linux, Windows, and macOS (unusual among photorealistic engine-based sims). Develop locally on the hardware you already have.
Extensible by design Tempo is built entirely as Unreal plugins (plus a few engine patches). If you can build it in Unreal, you can build it alongside Tempo.

Where another tool may fit better

If you need… Consider
Massive-scale parallel RL (thousands of GPU envs, Gym-style) Isaac Lab, Genesis
Contact-rich / legged-robot physics fidelity Isaac Sim (PhysX), Genesis (differentiable)
To drop in existing robot descriptions (URDF/SDF/USD) Gazebo, Isaac Sim
A turnkey AV stack with prebuilt maps & scenarios CARLA

In short: Tempo is the strongest fit for a photorealistic, deeply customizable simulator on modern Unreal, controlled entirely from code, that runs on the hardware your team already has — especially if you're comfortable in Unreal. It's a younger project with a smaller community and content library than the largest established sims, and it deliberately leaves the last mile — your scenarios, robots, and application — to you. That trade-off is the point.

A taste of the API

Everything below is a client talking to a running Unreal Editor or packaged binary over gRPC.

import tempo_sim.tempo_core as tc
import tempo_sim.tempo_world as tw
import tempo_sim.TempoCore.Time_pb2 as Time

tc.set_time_mode(Time.TM_FIXED_STEP)      # deterministic, reproducible time
tc.set_sim_steps_per_second(10)

rig = tw.spawn_actor(actor_type="BP_SensorRig")
tw.set_float_property(actor=rig.name, component="TempoCamera",
                      property="FOVAngle", value=60.0)

for _ in range(100):
    tc.step()                              # advance exactly 0.1 s
use tempo_sim::{set_server, tempo_core, tempo_world};

fn main() -> Result<(), tempo_sim::TempoError> {
    set_server("localhost", 10001);
    let rig = tempo_world::spawn_actor("BP_SensorRig".into(), /* … */)?;
    tempo_core::step()?;
    Ok(())
}
#include <tempo.h>

int main() {
    tempo::set_server("localhost", 10001);
    auto rig = tempo::tempo_world::spawn_actor("BP_SensorRig");
    tempo::tempo_core::step();
}

Getting help

Not sure where to start? Want guidance from the authors? Find us on Discord.

Something not working as expected, or a key feature missing? Send us an issue. Want to contribute? We'll be happy to review your PR.