component

Lamp

Lamp that emits a point light.

⌖ Explore the library in 3D → · View as Markdown

Tune every prop in the workshop →

Add it

npx @runek/cli add lamp

Pulls @react-three/fiber@^9.6.1, @react-three/rapier@^2.2.0, @runek/core@^0.15.0, three@^0.184.0.

Use it

import { Lamp } from './runek/Lamp'

<Lamp position={[0, 0, 0]} />

Or as a node in a world file (worlds as data):

{ "type": "Lamp", "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.
height number 1.6 Height, in units.
color color palette metal Base + pole color. Defaults to the world palette’s metal slot.
shadeColor color "#e9d8a6"
lightColor color "#ffe8c2"
intensity number 18
flicker number 0.08 Candle-like intensity flicker, 0–1; 0 holds the light steady.

Registry manifest

registry manifest /r/components/lamp.json Self-contained JSON: inlined source plus resolved dependencies, exactly what runek add lamp fetches.

Browse the whole catalog in the gallery →.

Source (Lamp.tsx)
import { useFrame } from '@react-three/fiber'
import { CuboidCollider, RigidBody } from '@react-three/rapier'
import { useWorld, type Vec3 } from '@runek/core'
import { useRef } from 'react'
import type { PointLight } from 'three'

export interface LampProps {
  position?: Vec3
  rotation?: Vec3
  /** Accepted for the component contract; this component has no seeded variation yet. */
  seed?: number
  height?: number
  /** Base + pole color. Defaults to the world palette's `metal` slot. */
  color?: string
  shadeColor?: string
  lightColor?: string
  intensity?: number
  /** Candle-like intensity flicker, 0–1; 0 holds the light steady. */
  flicker?: number
}

export function Lamp({
  position = [0, 0, 0],
  rotation = [0, 0, 0],
  height = 1.6,
  color,
  shadeColor = '#e9d8a6',
  lightColor = '#ffe8c2',
  intensity = 18,
  flicker = 0.08,
}: LampProps) {
  const { unit, palette } = useWorld()
  const metalColor = color ?? palette.metal
  const h = height * unit
  const baseR = 0.16 * unit
  const poleR = 0.02 * unit
  const shadeR = 0.19 * unit
  const shadeH = 0.26 * unit
  const poleH = h - shadeH
  const lightRef = useRef<PointLight>(null)

  useFrame(({ clock }) => {
    if (!lightRef.current || flicker <= 0) return
    const t = clock.elapsedTime
    // layered sines read as a candle without any per-frame randomness
    const n = Math.sin(t * 9.3) * 0.5 + Math.sin(t * 23.7) * 0.3 + Math.sin(t * 41.1) * 0.2
    lightRef.current.intensity = intensity * (1 + n * flicker)
  })

  return (
    <RigidBody type="fixed" colliders={false} position={position} rotation={rotation}>
      <CuboidCollider args={[baseR, h / 2, baseR]} position={[0, h / 2, 0]} />

      <mesh castShadow position={[0, 0.025 * unit, 0]}>
        <cylinderGeometry args={[baseR, baseR, 0.05 * unit, 24]} />
        <meshStandardMaterial color={metalColor} metalness={0.4} roughness={0.5} />
      </mesh>
      <mesh castShadow position={[0, poleH / 2, 0]}>
        <cylinderGeometry args={[poleR, poleR, poleH, 12]} />
        <meshStandardMaterial color={metalColor} metalness={0.4} roughness={0.5} />
      </mesh>
      <mesh castShadow position={[0, h - shadeH / 2, 0]}>
        <coneGeometry args={[shadeR, shadeH, 24, 1, true]} />
        <meshStandardMaterial
          color={shadeColor}
          emissive={shadeColor}
          emissiveIntensity={0.4}
          side={2}
        />
      </mesh>

      <pointLight
        ref={lightRef}
        position={[0, h - shadeH, 0]}
        color={lightColor}
        intensity={intensity}
        distance={12 * unit}
        decay={2}
      />
    </RigidBody>
  )
}

Lamp.groundSitting = true