component
Rug
Procedural striped rug (seeded stripes, no textures).
⌖ Explore the library in 3D → · View as Markdown
Add it
npx @runek/cli add rug
Pulls @runek/core@^0.15.0.
Use it
import { Rug } from './runek/Rug'
<Rug position={[0, 0, 0]} seed={1} />
Or as a node in a world file (worlds as data):
{ "type": "Rug", "props": { "position": [0, 0, 0], "seed": 1 } }
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. |
size |
[number, number] |
[3, 2] |
[width, depth] in units. |
baseColor |
color |
palette fabric |
Defaults to the world palette’s fabric slot. |
borderColor |
color |
palette accent |
Defaults to the world palette’s accent slot. |
accentColor |
color |
"#9c5252" |
|
seed |
number |
1 |
Seed for the deterministic variation: same seed, same result. |
Registry manifest
registry manifest /r/components/rug.json Self-contained JSON: inlined source plus resolved dependencies, exactly whatrunek add rug fetches.
Browse the whole catalog in the gallery →.
Source (Rug.tsx)
import { rng, useWorld, type Vec3 } from '@runek/core'
import { useMemo } from 'react'
export interface RugProps {
position?: Vec3
rotation?: Vec3
/** `[width, depth]` in units. */
size?: [number, number]
/** Defaults to the world palette's `fabric` slot. */
baseColor?: string
/** Defaults to the world palette's `accent` slot. */
borderColor?: string
accentColor?: string
seed?: number
}
interface Stripe {
x: number
width: number
}
/** Decorative — flat, no collider; walk over it. */
export function Rug({
position = [0, 0, 0],
rotation = [0, 0, 0],
size = [3, 2],
baseColor,
borderColor,
accentColor = '#9c5252',
seed = 1,
}: RugProps) {
const { unit, palette } = useWorld()
const baseCol = baseColor ?? palette.fabric
const borderCol = borderColor ?? palette.accent
const w = size[0] * unit
const d = size[1] * unit
const t = 0.02 * unit
const border = 0.12 * unit
const stripes = useMemo<Stripe[]>(() => {
const next = rng(seed)
const span = w - border * 2
const count = 3 + Math.floor(next() * 4)
return Array.from({ length: count }, () => ({
x: (next() - 0.5) * span,
width: (0.04 + next() * 0.08) * unit,
}))
}, [w, border, seed, unit])
return (
<group position={position} rotation={rotation}>
<mesh receiveShadow position={[0, t / 2, 0]}>
<boxGeometry args={[w, t, d]} />
<meshStandardMaterial color={borderCol} />
</mesh>
<mesh position={[0, t + 0.001 * unit, 0]} rotation={[-Math.PI / 2, 0, 0]}>
<planeGeometry args={[w - border * 2, d - border * 2]} />
<meshStandardMaterial color={baseCol} />
</mesh>
{stripes.map((s) => (
<mesh
key={`stripe-${s.x.toFixed(3)}`}
position={[s.x, t + 0.002 * unit, 0]}
rotation={[-Math.PI / 2, 0, 0]}
>
<planeGeometry args={[s.width, d - border * 2]} />
<meshStandardMaterial color={accentColor} />
</mesh>
))}
</group>
)
}