Sliding blocks
Basic contacts
Tall blocks slide across a grippy floor and tip over.
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 rodsuv run shelf play rods --solver steel_off# change solver settings liveuv run shelf tune rodsSource: examples/shelf/01_basic_contacts/rods/ · ported from steel-webgpu tools/scenes.py rods() @ ad03e49
Presets
| Preset | Solver | Status | Checked |
|---|---|---|---|
steeldefault | steel | works | 2026-11-10, newton f6fc82a8b, NVIDIA RTX PRO 6000 Blackwell Server Edition |
steel_off | steel variant | 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 Sliding blocks## Coupled friction (friction_mode): tall blocks launched sliding on a high-friction floor.## Ported from steel-webgpu's tools/scenes.py (rods); the scene builds# as it does there and the shelf's preset drives the solver.## Command: python -m shelf run rods############################################################################
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': (0.2, -1.6, 0.6), 'target': (0.0, 0.0, 0.1)}
def build(self): """Coupled friction (friction_mode): tall blocks launched sliding on a high-friction floor.""" b = newton.ModelBuilder() b.add_ground_plane(cfg=cfg(mu=0.9)) twists = {} for i, v in enumerate((1.0, 1.5, 2.0, 2.5, 3.0)): body = b.add_body(xform=tf((-0.6, (i - 2) * 0.18, 0.12))) b.add_shape_box(body, hx=0.03, hy=0.05, hz=0.12, cfg=cfg(mu=0.9, density=600), color=[BLUE, TEAL, GOLD, RED, GREY][i]) twists[body] = (v, 0, 0, 0, 0, 0) return b, twists, "Coupled friction: tall blocks launched at 1 to 3 m/s on a mu 0.9 floor slide and tip; with friction_mode bisection_desaxce (normal target mu |v_t|) sliding blocks hop."
def test_post_step(self): self._zmax = np.maximum(getattr(self, "_zmax", 0.0), self.state_0.body_q.numpy()[:, 2])
def check(self): """Blocks launched at 1-3 m/s slide farther the faster they start, 0.12-0.65 m (16 x 50: 0.17-0.59), stay in their lane and never hop (coupled friction without de Saxce's lift).""" q = self.state_0.body_q.numpy() slide = q[:, 0] + 0.6 assert np.all(np.diff(slide) > 0), f"slides not ordered by launch speed: {np.round(slide, 3)}" assert slide.min() > 0.12 and slide.max() < 0.65, f"slides {np.round(slide, 3)} m outside 0.12-0.65" lane = np.abs(q[:, 1] - (np.arange(5) - 2) * 0.18) assert lane.max() < 0.01, f"block {lane.argmax()} wandered {1e3 * lane.max():.0f} mm out of its lane" assert self._zmax.max() < 0.13, f"a block hopped to z={self._zmax.max():.3f} m"
if __name__ == "__main__": parser = Example.create_parser() viewer, args = newton.examples.init(parser) newton.examples.run(Example(viewer, args), args)example = "rods"source = "steel-webgpu tools/scenes.py rods() @ ad03e49"default = "steel"
[check]# Arguments `python -m shelf check` passes in test mode: 300 frames, 5 s, every block stopped; the example's check() asserts the outcome.args = ["--num-frames", "300"]
[gallery]short = "Tall blocks slide across a grippy floor and tip over."title = "Sliding blocks"frame = 90
[xpbd]status = "untested"
[vbd]status = "untested"
[mujoco]status = "untested"
[featherstone]status = "untested"
[semi_implicit]status = "untested"
[feather_pgs]status = "untested"
[steel]status = "works"# Coupled friction with friction_mode = "coulomb_newton" (the cone solved per contact with its normal), not the# browser's "bisection_desaxce": de Saxce's normal target mu |v_t| lifts the sliding blocks (centres rise up to# 0.18 m), which wander up to 9 cm toward the next lane and slide 0.65 to 0.97 m, also at 16 x 50, where every other# mode agrees on 0.17 to 0.59 m, no hop, tipped by frame 11 to 14 (on mu 0.9 a block tips almost at once: mu > hx/hz).# 2 substeps x 4 iterations: slides within 6 mm of 4 x 12 and 16 x 50; 1 substep slides 15 mm short and the# slowest block turns sideways 2 cm. Point friction (friction_anchor_beta = 0), as coupled modes need.# No joints: limits off.sim = { fps = 60, substeps = 2 }solver = { solve_mode = "matrix_free", iterations = 4, articulated_contact_response = "propagation_colored", friction_mode = "coulomb_newton", friction_anchor_beta = 0.0 }checked = "2026-11-10, newton f6fc82a8b, NVIDIA RTX PRO 6000 Blackwell Server Edition"
[kamino]status = "untested"
[implicit_mpm]status = "untested"
[style3d]status = "untested"
[steel_off]kind = "steel"status = "works"# the browser's "Coupled friction" toggle off: friction_mode = "fixed_normal", everything else as [steel]sim = { fps = 60, substeps = 2 }solver = { solve_mode = "matrix_free", iterations = 4, articulated_contact_response = "propagation_colored", friction_mode = "fixed_normal", friction_anchor_beta = 0.0 }checked = "2026-11-10, newton f6fc82a8b, NVIDIA RTX PRO 6000 Blackwell Server Edition"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.