Skip to content

House of cards (mujoco.org)

Many contacts

A four-storey house of 26 thin playing cards stands on friction alone.

0.00 s
The steel preset (the default), recordedon an NVIDIA RTX PRO 6000 Blackwell, sped up 2× (the readout shows sim time).

Run it

Terminal window
cd examples
# the default preset (steel), then the others
uv run shelf play mjorg_house_of_cards
# change solver settings live
uv run shelf tune mjorg_house_of_cards

Source: examples/shelf/02_many_contacts/mjorg_house_of_cards/ · ported from google-deepmind/mujoco model/cards/house_of_cards.xml (MuJoCo 4.0 tour on mujoco.org)

Presets

PresetSolverStatusChecked

Solvers with no row are untested on this example. The full settings are inpresets.toml.

Code

examples/shelf/02_many_contacts/mjorg_house_of_cards/example.py
# SPDX-FileCopyrightText: Copyright (c) 2026 Skild AI and the Steel contributors
# SPDX-License-Identifier: Apache-2.0
###########################################################################
# Example mujoco.org house of cards
#
# A four-storey house of 26 playing cards, 0.7 mm thick, standing on friction alone.
#
# Replicates the "House of Cards" scene of the MuJoCo 4.0 tour on mujoco.org from its
# MJCF (scene.xml, flattened from google-deepmind/mujoco model/cards/house_of_cards.xml).
# See README.md for the gaps.
#
# Command: python -m shelf run mjorg_house_of_cards
#
###########################################################################
from pathlib import Path
import numpy as np
import newton
import newton.examples
from common.steel_scenes import SceneExample
# How far a card may move before the house counts as failing [m].
CARD_SHIFT_MAX = 0.005
class Example(SceneExample):
file = __file__
# MuJoCo's free camera for the scene: azimuth 150, elevation -15 around the statistic centre
camera = {"eye": (0.44, -0.25, 0.24), "target": (0.0, 0.0, 0.1)}
def build(self):
builder = newton.ModelBuilder()
self.preset.apply_model(builder)
builder.add_mjcf(str(Path(__file__).with_name("scene.xml")))
self.start = np.array([list(builder.body_q[b])[:3] for b in range(builder.body_count)])
return builder, {}
def check(self):
"""Every card stays where it was built."""
self.solver.check_constraint_capacity() # dropped contact rows invalidate the run
q = self.state_0.body_q.numpy()
shift = np.linalg.norm(q[:, :3] - self.start, axis=1)
assert shift.max() < CARD_SHIFT_MAX, (
f"{int((shift >= CARD_SHIFT_MAX).sum())} cards moved, up to {shift.max():.4f} m"
)
if __name__ == "__main__":
parser = Example.create_parser()
viewer, args = newton.examples.init(parser)
newton.examples.run(Example(viewer, args), args)

README

Steel replica of “House of Cards” from the MuJoCo 4.0 tour on mujoco.org. 26 playing cards, 0.7 mm thick, make a four-storey house that stands on friction alone.

  • Source: google-deepmind/mujoco model/cards/house_of_cards.xml and its assets/, Apache-2.0 (text in LICENSE). scene.xml is that file compiled with MuJoCo 3.17 and re-emitted flat; the card textures are downscaled to 256 x 256.
  • Check: after 20 s no card has moved more than 5 mm, and no contact rows were dropped.

Known gaps:

  • Steel needs Nesterov momentum with contact warm start (serial solve, immediate response) at 2 ms steps. With the colored solve and propagation response the apex pair slides apart within 2 s at every setting tried.
  • MuJoCo’s contacts are soft and over-damped (solref 0.005 3), with an elliptic cone, impratio 10 and two noslip iterations; Steel’s contacts are hard. Air density and viscosity are not modelled.
  • The tour lets the user drag a card; the shelf scene runs untouched.
  • Rendering uses the shelf viewer, not MuJoCo’s Filament renderer.