Skip to content

Scenes & Viewer Handles

simview.scene is the main authoring API: SimulationScene builds a model incrementally (terrain, bodies, static objects) and accumulates states, then saves/loads JSON or launches a viewer. ViewerHandle is the non-blocking handle returned by SimulationScene.show().

simview.scene

SimulationScene

SimulationScene(
    batch_size: int,
    scalar_names: list[str],
    dt: float,
    collapse: bool = False,
    terrain: SimViewTerrain | None = None,
    bodies: dict[str, SimViewBody] | None = None,
    static_objects: dict[str, SimViewStaticObject]
    | None = None,
    batch_names: list[str] | None = None,
    metadata: dict[str, Any] | None = None,
)

Initializes the simulation data container. Manages the SimViewModel and the time-series states.

metadata is free-form, JSON-serializable run provenance (e.g. engine name, checkpoint path, git commit, CLI args) with no meaning to the viewer -- it's carried through to simview info and the browser so a saved scene stays self-describing. Can also be set/updated later via self.model.metadata.

add_body_object

add_body_object(body: SimViewBody) -> None

Adds a pre-configured SimViewBody object to the model. See create_body for the meaning of body.parent/body.local_transform.

add_state

add_state(
    time: float,
    body_states: list[SimViewBodyState],
    scalar_values: dict[str, Tensor | ndarray | list]
    | None = None,
) -> None

Adds a new state (snapshot in time) to the simulation data.

add_static_object_instance

add_static_object_instance(
    static_object: SimViewStaticObject,
) -> None

Adds a pre-configured SimViewStaticObject to the model.

add_terrain_object

add_terrain_object(terrain: SimViewTerrain) -> None

Adds a pre-configured SimViewTerrain object to the model.

add_trajectory

add_trajectory(
    times,
    trajectories: list[BodyTrajectory],
    scalar_values: dict[str, Tensor | ndarray | list]
    | None = None,
    binary: bool = True,
) -> None

Append an entire time-series in one call.

Equivalent to looping add_state over T frames, but converts each body's pose/vector tensors once (vectorised) instead of per frame, which is dramatically faster for long trajectories. With binary=True the numeric per-body fields (bodyTransform and any provided vectors) are packed as float32 __b64__ blobs, shrinking the output file and the parse cost; the viewer and :func:merge_simulation_files decode these transparently. Set binary=False to emit plain JSON lists. A body's contacts (if provided on its :class:BodyTrajectory) are ragged and always emitted as plain JSON per frame, using the same encoding as SimViewBodyState / add_state.

Parameters:

Name Type Description Default
times

sequence of length T of snapshot times (seconds).

required
trajectories list[BodyTrajectory]

one :class:BodyTrajectory per body; each body must already exist in the model.

required
scalar_values dict[str, Tensor | ndarray | list] | None

for a scene with scalar_names, maps each name to a (T, B) (or (T,) when B == 1) series.

None

create_body

create_body(
    body_name: str,
    shape_type: BodyShapeType,
    available_attributes: list[
        OptionalBodyStateAttribute | str
    ]
    | None = None,
    parent: str | None = None,
    local_transform: LocalTransformLike | None = None,
    **kwargs,
) -> None

Creates and adds a dynamic body to the simulation model.

parent/local_transform attach this body to another body already in the model, instead of it moving in world space:

  • Rigid attachment (e.g. a wheel bolted to a chassis): pass both parent and local_transform (a constant [x, y, z, w, qx, qy, qz] offset). Never call add_state/add_trajectory for this body afterwards -- its world pose is derived by the viewer every frame from the parent's current pose plus this fixed offset.
  • Articulated attachment (e.g. an arm joint): pass only parent. Keep supplying this body's pose every frame via add_state/ add_trajectory as usual -- it's just interpreted as local to the parent's current-frame pose instead of world space.

create_pointcloud

create_pointcloud(
    body_name: str,
    points: Tensor,
    color: Tensor | None = None,
    embedding: Tensor | None = None,
    **kwargs,
) -> None

Creates and adds a pointcloud body to the simulation model.

Parameters:

Name Type Description Default
body_name str

Unique name for this body.

required
points Tensor

(N, 3) point positions.

required
color Tensor | None

Optional (N, 3) per-point RGB in [0, 1] for static vertex-colored rendering.

None
embedding Tensor | None

Optional (N, K) per-point feature vector (e.g. a reduced-dim PCA projection) enabling the viewer's click-to-similarity color mode.

None

create_static_object_batched

create_static_object_batched(
    name: str,
    shape_type: BodyShapeType,
    shapes_kwargs: list[dict[str, Any]],
) -> None

Creates and adds a batched static object to the simulation model.

create_static_object_singleton

create_static_object_singleton(
    name: str, shape_type: BodyShapeType, **kwargs
) -> None

Creates and adds a singleton static object to the simulation model.

create_terrain

create_terrain(
    heightmap: Tensor,
    normals: Tensor | None = None,
    x_lim: tuple[float, float] | None = None,
    y_lim: tuple[float, float] | None = None,
    grid_res: float | None = None,
    properties: dict[str, Tensor] | None = None,
    embedding_map: Tensor | None = None,
) -> None

Adds terrain to the simulation model.

Parameters:

Name Type Description Default
heightmap Tensor

2D or 3D tensor of terrain heights.

required
normals Tensor | None

3D or 4D tensor of terrain normals. If None, normals are automatically computed from the heightmap gradients.

None
x_lim tuple[float, float] | None

(min, max) coordinates for the X axis.

None
y_lim tuple[float, float] | None

(min, max) coordinates for the Y axis.

None
grid_res float | None

Grid resolution. If x_lim and y_lim are omitted, they will be automatically inferred assuming the grid is centered at 0.

None
properties dict[str, Tensor] | None

Optional arbitrary named per-cell scalar maps (2D or 3D, like heightmap), e.g. {"friction": friction_map, "stiffness": stiffness_map}. Each becomes selectable as a terrain color mode in the viewer automatically, with no further code changes needed.

None
embedding_map Tensor | None

Optional per-cell K-wide feature map (3D channels-first (K, Dy, Dx) or 4D (B, K, Dy, Dx), like normals) enabling the viewer's click-to-similarity "features" color mode.

None

from_dict classmethod

from_dict(d: dict) -> SimulationScene

Reconstruct a SimulationScene from the dict produced by save/to_json (i.e. the parsed {"model": ..., "states": ...} document).

Binary __b64__-encoded fields inside states (e.g. from add_trajectory(binary=True)) are left as-is, matching the on-disk wire format, so a subsequent save() reproduces the same bytes for those fields without a decode/re-encode round trip.

load classmethod

load(path: str | Path) -> SimulationScene

Load a SimulationScene previously written by save.

Transparently reads gzip-compressed files (detected by magic bytes, regardless of extension) as well as plain JSON. Enables round-tripping from Python: SimulationScene.load(p).save(p2).

save

save(filepath: str | Path, compress: bool = False) -> None

Exports the complete simulation data (model and states) to a JSON file. Uses a streaming approach to reduce memory spikes for large simulations.

Parameters:

Name Type Description Default
filepath str | Path

Destination path. If it ends in .gz the output is gzip-compressed regardless of compress.

required
compress bool

If True, gzip-compress the output (useful for large simulations, which can reach 100+ MB as plain JSON). If filepath doesn't already end in .gz, the suffix is appended so the extension reflects the actual file contents.

False

show

show(
    host: str = "127.0.0.1",
    preferred_port: int = 5420,
    open_browser: bool = False,
) -> ViewerHandle

Serve a snapshot of this scene on a background thread and return immediately, instead of blocking like SimViewLauncher/SimViewServer.run.

Intended for Jupyter notebooks and scripts that want to keep running (or keep the cell interactive) while the viewer is up: the returned ViewerHandle renders inline via _repr_html_ when it's a cell's result, and its stop() (or exiting it as a context manager) shuts the server down. The scene itself is left untouched -- unlike SimViewLauncher, show doesn't clear self.states/self.model, so the same scene can still be save()d or shown again afterwards.

Multiple concurrent show() calls (on the same or different scenes) are fine -- each gets its own server thread and port (via find_free_port).

ViewerHandle

ViewerHandle(threaded)

A running, non-blocking SimView server for a snapshot of a scene.

Returned by SimulationScene.show. Holds the background server thread started for that snapshot; stop() (also called automatically on context-manager exit) shuts it down. _repr_html_ lets Jupyter render the viewer inline in an iframe just by evaluating the handle in a cell.

stop

stop() -> None

Stop the background server. Idempotent.