component
Bench
Slatted bench with an optional backrest; indoor or outdoor seating.
⌖ Explore the library in 3D → · View as Markdown
Add it
npx @runek/cli add bench
Pulls @react-three/rapier@^2.2.0, @runek/core@^0.15.0.
Use it
import { Bench } from './runek/Bench'
<Bench position={[0, 0, 0]} />
Or as a node in a world file (worlds as data):
{ "type": "Bench", "props": { "position": [0, 0, 0] } }
Props
| Prop | Type | Default | Description |
|---|---|---|---|
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. | |
length |
number |
1.6 |
Length, in units. |
depth |
number |
0.5 |
Depth, in units. |
seatHeight |
number |
0.45 |
|
back |
boolean |
true |
Include a backrest. |
color |
color |
palette wood |
Defaults to the world palette’s wood slot. |
Registry manifest
registry manifest /r/components/bench.json Self-contained JSON: inlined source plus resolved dependencies, exactly whatrunek add bench fetches.
Browse the whole catalog in the gallery →.
Source (Bench.tsx)
import { CuboidCollider, RigidBody } from '@react-three/rapier'
import { useWorld, type Vec3 } from '@runek/core'
export interface BenchProps {
position?: Vec3
rotation?: Vec3
/** Accepted for the component contract; this component has no seeded variation yet. */
seed?: number
length?: number
depth?: number
seatHeight?: number
/** Include a backrest. */
back?: boolean
/** Defaults to the world palette's `wood` slot. */
color?: string
}
/** A slatted bench, with an optional backrest. Indoor or outdoor seating. */
export function Bench({
position = [0, 0, 0],
rotation = [0, 0, 0],
length = 1.6,
depth = 0.5,
seatHeight = 0.45,
back = true,
color,
}: BenchProps) {
const { unit, palette } = useWorld()
const wood = color ?? palette.wood
const L = length * unit
const D = depth * unit
const sh = seatHeight * unit
const slatT = 0.04 * unit
const legW = 0.07 * unit
const SEAT_SLATS = 3
const slatD = (D / SEAT_SLATS) * 0.82
const backH = 0.5 * unit
const lx = L / 2 - legW
const lz = D / 2 - legW
const legs: Vec3[] = [
[-lx, sh / 2, -lz],
[lx, sh / 2, -lz],
[-lx, sh / 2, lz],
[lx, sh / 2, lz],
]
return (
<RigidBody type="fixed" colliders={false} position={position} rotation={rotation}>
<CuboidCollider args={[L / 2, sh / 2, D / 2]} position={[0, sh / 2, 0]} />
{Array.from({ length: SEAT_SLATS }, (_, i) => {
const z = -D / 2 + (i + 0.5) * (D / SEAT_SLATS)
return (
<mesh key={`slat-${i}`} castShadow receiveShadow position={[0, sh, z]}>
<boxGeometry args={[L, slatT, slatD]} />
<meshStandardMaterial color={wood} roughness={0.85} />
</mesh>
)
})}
{legs.map((p) => (
<mesh key={`leg-${p[0].toFixed(3)}:${p[2].toFixed(3)}`} castShadow position={p}>
<boxGeometry args={[legW, sh, legW]} />
<meshStandardMaterial color={wood} roughness={0.85} />
</mesh>
))}
{back && (
<>
{[-1, 1].map((s) => (
<mesh
key={`support-${s}`}
castShadow
position={[s * lx, sh + backH / 2, -D / 2 + legW]}
>
<boxGeometry args={[legW, backH, legW]} />
<meshStandardMaterial color={wood} roughness={0.85} />
</mesh>
))}
{[0.55, 0.9].map((f) => (
<mesh key={`backslat-${f}`} castShadow position={[0, sh + backH * f, -D / 2 + legW]}>
<boxGeometry args={[L, slatT, slatD * 0.8]} />
<meshStandardMaterial color={wood} roughness={0.85} />
</mesh>
))}
</>
)}
</RigidBody>
)
}
Bench.groundSitting = true