component
Stool
A round bar stool: a disc seat on three splayed legs; a thin cylinder collider.
⌖ Explore the library in 3D → · View as Markdown
Add it
npx @runek/cli add stool
Pulls @react-three/rapier@^2.2.0, @runek/core@^0.15.0.
Use it
import { Stool } from './runek/Stool'
<Stool position={[0, 0, 0]} />
Or as a node in a world file (worlds as data):
{ "type": "Stool", "props": { "position": [0, 0, 0] } }
Props
| Prop | Type | Default | Description |
|---|---|---|---|
height |
number |
0.95 |
Seat height above the ground, in units. |
radius |
number |
0.24 |
Seat radius, in units. |
color |
color |
Color; defaults to the palette’s wood. |
|
position |
[x, y, z] |
[0, 0, 0] |
Position [x, y, z] in units (1 unit = 1 m, Y-up). |
rotation |
[x, y, z] |
[0, 0, 0] |
Euler rotation [x, y, z] in radians. |
seed |
number |
Accepted for the component contract; this component has no seeded variation yet. |
Registry manifest
registry manifest /r/components/stool.json Self-contained JSON: inlined source plus resolved dependencies, exactly whatrunek add stool fetches.
Browse the whole catalog in the gallery →.
Source (Stool.tsx)
import { CylinderCollider, RigidBody } from '@react-three/rapier'
import { useWorld, type WorldComponentProps } from '@runek/core'
export interface StoolProps extends WorldComponentProps {
/** Seat height above the ground, in units. */
height?: number
/** Seat radius, in units. */
radius?: number
/** Color; defaults to the palette's `wood`. */
color?: string
}
const LEGS = [0, 1, 2].map((i) => (i / 3) * Math.PI * 2)
/** A round bar stool: a disc seat on three splayed legs. A thin cylinder collider. */
export function Stool({
position = [0, 0, 0],
rotation = [0, 0, 0],
height = 0.95,
radius = 0.24,
color,
}: StoolProps) {
const { unit, palette } = useWorld()
const wood = color ?? palette.wood
const H = height * unit
const R = radius * unit
const seatT = 0.07 * unit
const legR = 0.035 * unit
const spread = R * 0.78
return (
<RigidBody type="fixed" colliders={false} position={position} rotation={rotation}>
<CylinderCollider args={[H / 2, R * 0.6]} position={[0, H / 2, 0]} />
{/* seat */}
<mesh position={[0, H - seatT / 2, 0]} castShadow receiveShadow>
<cylinderGeometry args={[R, R, seatT, 16]} />
<meshStandardMaterial color={wood} roughness={0.7} />
</mesh>
{/* legs */}
{LEGS.map((a) => (
<mesh
key={`leg-${a.toFixed(3)}`}
position={[Math.cos(a) * spread, (H - seatT) / 2, Math.sin(a) * spread]}
castShadow
>
<cylinderGeometry args={[legR, legR, H - seatT, 6]} />
<meshStandardMaterial color={wood} roughness={0.8} />
</mesh>
))}
</RigidBody>
)
}
Stool.groundSitting = true