Bounce
Basic contacts
Balls bounce on pads from dead to very bouncy.
steel preset (the default), recordedon an NVIDIA RTX PRO 6000 Blackwell.Run it
cd examples# the default preset (steel), then the othersuv run shelf play bounce# change solver settings liveuv run shelf tune bounceSource: examples/shelf/01_basic_contacts/bounce/ · ported from steel-webgpu tools/scenes.py bounce() @ ad03e49
Presets
| Preset | Solver | Status | Checked |
|---|---|---|---|
steeldefault | steel | works | 2026-11-10, newton f6fc82a8b, NVIDIA RTX PRO 6000 Blackwell Server Edition |
Solvers with no row are untested on this example. The full settings are inpresets.toml.
Code
# SPDX-FileCopyrightText: Copyright (c) 2026 Skild AI and the Steel contributors# SPDX-License-Identifier: Apache-2.0# Direct source: steel-webgpu; see presets.toml and THIRD_PARTY.md.# Modified for Steel Shelf: preset-driven execution and shared runner integration.
############################################################################ Example Bounce## Restitution: five balls, each on a pad of the same restitution (0 ... 0.9), dropped from 0.8 m.## Ported from steel-webgpu's tools/scenes.py (bounce); the scene builds# as it does there and the shelf's preset drives the solver.## Command: python -m shelf run bounce############################################################################
import math
import numpy as npimport warp as wp
import newtonimport newton.examplesfrom common.steel_scenes import BLUE, GOLD, GREY, RED, TEAL, SceneExample, cfg, tf
class Example(SceneExample): file = __file__ camera = {'eye': (1.25, -1.6, 1.25), 'target': (0.1, 0.05, 0.15)}
def build(self): """Restitution: five balls, each on a pad of the same restitution (0 ... 0.9), dropped from 0.8 m.""" b = newton.ModelBuilder() b.add_ground_plane() twists = {} for i, e in enumerate((0.0, 0.25, 0.5, 0.75, 0.9)): x = (i - 2) * 0.25 b.add_shape_box(-1, xform=tf((x, 0, 0.02)), hx=0.1, hy=0.1, hz=0.02, cfg=cfg(restitution=e), color=GREY) body = b.add_body(xform=tf((x, 0, 0.8))) b.add_shape_sphere(body, radius=0.04, cfg=cfg(restitution=e, density=1000), color=[BLUE, TEAL, GOLD, RED, BLUE][i]) return b, twists, "Restitution: balls on pads of restitution 0, 0.25, 0.5, 0.75 and 0.9, left to right."
def test_post_step(self): self._z = getattr(self, "_z", []) + [self.state_0.body_q.numpy()[:, 2].copy()]
def check(self): """Each ball's first rebound is e^2 of its 0.72 m drop within 15 mm (16 x 50: 47, 182, 408, 587 mm).""" z = np.array(self._z) for ball, e in enumerate((0.0, 0.25, 0.5, 0.75, 0.9)): up = np.flatnonzero(np.diff(z[:, ball]) > 0) rebound = float(z[up[0] :, ball].max() - 0.08) if len(up) else 0.0 assert abs(rebound - e * e * 0.72) < 0.015, ( f"ball {ball} (e={e}) rebounds {1e3 * rebound:.0f} mm, e^2 h is {720 * e * e:.0f}" )
if __name__ == "__main__": parser = Example.create_parser() viewer, args = newton.examples.init(parser) newton.examples.run(Example(viewer, args), args)example = "bounce"source = "steel-webgpu tools/scenes.py bounce() @ ad03e49"default = "steel"
[check]# Arguments `python -m shelf check` passes in test mode: 70 frames, the first rebounds; the example's check() asserts the outcome.args = ["--num-frames", "70"]
[gallery]short = "Balls bounce on pads from dead to very bouncy."title = "Bounce"
[xpbd]status = "untested"
[vbd]status = "untested"
[mujoco]status = "untested"
[featherstone]status = "untested"
[semi_implicit]status = "untested"
[feather_pgs]status = "untested"
[steel]status = "works"# 4 substeps x 2 iterations: first rebounds of 54, 191, 418 and 598 mm from the 720 mm drop (16 x 50: 47, 182, 408,# 587; e^2 h: 45, 180, 405, 583), the same as the browser's 4 x 12 at two thirds of the cost. The rebound error# grows with the substep (2 substeps: 64, 203, 432, 613; 1 substep: 84 ... 643), and iterations do not change it:# each ball has one contact, so 1 iteration gives the same motion; 2 keep a ball touching pad and floor at once solved.# 8 x 1 comes within 2% of 16 x 50 at twice the cost. No joints: limits off.sim = { fps = 60, substeps = 4 }solver = { solve_mode = "matrix_free", iterations = 2, articulated_contact_response = "propagation_colored" }checked = "2026-11-10, newton f6fc82a8b, NVIDIA RTX PRO 6000 Blackwell Server Edition"
[kamino]status = "untested"
[implicit_mpm]status = "untested"
[style3d]status = "untested"Steel docs and tutorials are licensed under Apache-2.0. Third-party notices. "Skild", "Skild AI" and the Skild logo are trademarks of Skild AI and are not licensed under Apache-2.0.