PendulumWave
threecn/pendulum-waveFreeComponent
A row of pendulums with stepped periods drifting into snakes, arches and chaos before snapping back in phase, bobs tinted --accent to --primary across the swing.
Install it
npx shadcn@latest add https://threecn.dev/r/pendulum-wave.jsonSource
1"use client"23import * as React from "react"4import { useFrame } from "@react-three/fiber"5import * as THREE from "three"67import {8 SceneContainer,9 type SceneContainerProps,10} from "@/components/threecn/scene-container"11import {12 useShadcnTheme,13 type ThemeMode,14} from "@/components/hooks/use-shadcn-theme"1516/** Total width of the row of pendulums. */17const WIDTH = 618/** Height of the support bar the pendulums hang from. */19const TOP_Y = 1.720/** Seconds for the whole pattern to drift out of phase and realign. */21const CYCLE = 2622/** Oscillations the slowest (longest) pendulum completes per cycle. */23const BASE_SWINGS = 724const UP = new THREE.Vector3(0, 1, 0)2526function Pendulums({27 count,28 speed,29 swing,30 theme,31}: {32 count: number33 speed: number34 swing: number35 theme: ThemeMode36}) {37 const { primaryColor, accentColor, mutedColor } = useShadcnTheme(theme)38 const bobsRef = React.useRef<THREE.InstancedMesh>(null)39 const stringsRef = React.useRef<THREE.InstancedMesh>(null)40 const dummy = React.useMemo(() => new THREE.Object3D(), [])41 const tmpColor = React.useMemo(() => new THREE.Color(), [])42 const tmpDir = React.useMemo(() => new THREE.Vector3(), [])4344 // Pendulum i completes (BASE_SWINGS + i) oscillations per CYCLE, so the row45 // drifts through snakes and chaos before snapping back in phase. Lengths46 // follow real pendulum physics (L ∝ 1/f²), giving the descending cascade.47 const lengths = React.useMemo(48 () =>49 Array.from(50 { length: count },51 (_, i) => 3.1 * (BASE_SWINGS / (BASE_SWINGS + i)) ** 252 ),53 [count]54 )5556 const spacing = count > 1 ? WIDTH / (count - 1) : 057 const bobRadius = Math.min(0.18, Math.max(0.09, spacing * 0.42))5859 useFrame(({ clock }) => {60 const bobs = bobsRef.current61 const strings = stringsRef.current62 if (!bobs || !strings) return63 const t = clock.getElapsedTime() * speed64 const swingRad = (swing * Math.PI) / 1806566 for (let i = 0; i < count; i++) {67 const f = (BASE_SWINGS + i) / CYCLE68 // cos → every pendulum starts released from its full amplitude, so the69 // row begins as one clean diagonal sweep.70 const angle = swingRad * Math.cos(2 * Math.PI * f * t)71 const x = -WIDTH / 2 + i * spacing72 const len = lengths[i]7374 // Swing happens in the Z plane, perpendicular to the row.75 tmpDir.set(0, -Math.cos(angle), Math.sin(angle))76 dummy.position.set(x, TOP_Y, 0).addScaledVector(tmpDir, len)77// … truncated
What it pulls in
npm packages
Files it writes
More from threecn
All 28 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| AuroraRibbonsaurora-ribbons | threecn | Components | Free | 3 deps · 3 files | |
| Boidsboids | threecn | Components | Free | 3 deps · 3 files | |
| Clothcloth | threecn | Components | Free | 3 deps · 3 files | |
| Crystalcrystal | threecn | Components | Free | 3 deps · 3 files | |
| CubeWavecube-wave | threecn | Components | Free | 3 deps · 3 files | |
| CurlFlowcurl-flow | threecn | Components | Free | 3 deps · 3 files | |
| DnaHelixdna-helix | threecn | Components | Free | 3 deps · 3 files | |
| FloatingCard3Dfloating-card-3d | threecn | Components | Free | 3 deps · 3 files |
