drag to orbit · tap the ground to send it
Nature · Rolls with distance
Tumbleweed
A seeded tumbleweed: a loose ball of dry curved twigs around a denser tangled core. The whole ball (tumbleweed_ball) is a spin part that rolls with distance along +Z, so it can bowl down a dusty street.
- Size
- 0.77 × 0.74 × 0.76 m
- Triangles
- 1,646
- Kit
- Wild West
Tune it
4 knobs: Size, Density, Colour, Seed.
Specs
- Size
- 0.77 × 0.74 × 0.76 m width × height × depth
- Triangles
- 1,646 of 6,000
- Draw calls
- 1 one vertex-colour material
- Loop clip
- 2.217 m of travel per cycle
- Moving parts
- 1 tumbleweed_ball · spin
- Knobs
- 4 3 presets
- Night lights
- 0 none
Passed the validator: real scale, rests on y = 0, front faces +Z, deterministic.
Use it
Code for the specimen as it is set up above; it follows your knobs.
import { createAsset } from 'https://gaitkit.com/cdn/tumbleweed.mjs';
const tumbleweed = createAsset();
scene.add(tumbleweed); // rests on y = 0, front faces +Z
let dist = 0;
renderer.setAnimationLoop(() => {
dist += speed * clock.getDelta();
tumbleweed.position.z = dist;
tumbleweed.userData.tick(dist); // wheels roll exactly with the distance
renderer.render(scene, camera);
});import { useMemo, useRef } from 'react';
import { useFrame } from '@react-three/fiber';
import { createAsset } from 'https://gaitkit.com/cdn/tumbleweed.mjs';
export function Tumbleweed({ speed = 1 }) {
const model = useMemo(() => createAsset(), []);
const dist = useRef(0);
useFrame((_, dt) => {
dist.current += speed * dt;
model.position.z = dist.current;
model.userData.tick(dist.current); // wheels roll exactly with the distance
});
return <primitive object={model} />;
}# Import tumbleweed.glb (download below). It carries an in-place "Loop" animation.
@onready var anim: AnimationPlayer = $Tumbleweed/AnimationPlayer
func _physics_process(delta):
velocity = transform.basis.z * speed
move_and_slide()
anim.play("Loop")
anim.speed_scale = speed / 2.217 # one Loop = 2.217 m of travel// Import tumbleweed.glb with glTFast (com.unity.cloud.gltfast).
animator.Play("Loop");
animator.speed = speed / 2.217 // one Loop = 2.217 m of travelUse the gaitkit MCP server: get_variant({ "id": "tumbleweed", "want": "default look" })
then place the result and drive it with userData.tick(distance).
# MCP endpoint
https://gaitkit.com/mcpKnobs
| Knob | Type | Values | Default | What it does |
|---|---|---|---|---|
size | range | 0.4 to 1.4, step 0.05 | 0.8 | Diameter in metres. 0.4 is a small clump, 1.4 a big old one taller than a fence rail. |
density | range | 0.3 to 1, step 0.05 | 0.65 | How many twigs: 0.3 is airy and see-through, 1 is a thick tangled ball. |
colour | choice | dry bleached rusty | dry | dry (straw tan), bleached (sun-whitened grey), rusty (reddish autumn thistle). |
seed | range | 1 to 60, step 1 | 9 | Reshapes every twig. Same seed always builds the same tumbleweed. |
Presets: desert drifter {"size":0.7,"colour":"dry","density":0.55} · giant tumbleweed {"size":1.4,"density":0.9,"seed":23} · bleached bones {"colour":"bleached","density":0.4,"seed":31}
Files
| File | What |
|---|---|
| tumbleweed.glb | GLB, default look · 418 KB |
| tumbleweed.mjs | ES module, every knob live |
| tumbleweed.obj | OBJ, static, no colour |
| tumbleweed-params.json | knob schema, presets, night zones |
Licence
Use it in games, apps, renders and client work, commercially and without credit. Do not resell it as an asset or train a 3D generator on it. The full licence.