ParticleField
threecn/particle-fieldFreeComponent
A drifting cloud of particles colored with your shadcn --primary and --muted-foreground tokens.
Install it
npx shadcn@latest add https://threecn.dev/r/particle-field.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"1516type ParticlesProps = {17 count: number18 speed: number19 size: number20 theme: ThemeMode21}2223type ParticleData = {24 positions: Float32Array25 speeds: Float32Array26 isPrimary: Uint8Array27}2829function seedParticles(count: number): ParticleData {30 const positions = new Float32Array(count * 3)31 const speeds = new Float32Array(count)32 const isPrimary = new Uint8Array(count)33 const radius = 934 for (let i = 0; i < count; i++) {35 const i3 = i * 336 const r = radius * Math.cbrt(Math.random())37 const theta = Math.random() * Math.PI * 238 const phi = Math.acos(2 * Math.random() - 1)39 positions[i3] = r * Math.sin(phi) * Math.cos(theta)40 positions[i3 + 1] = r * Math.sin(phi) * Math.sin(theta) * 0.641 positions[i3 + 2] = r * Math.cos(phi)42 speeds[i] = 0.5 + Math.random()43 // 35% of points are "primary" (foreground), the rest muted.44 isPrimary[i] = Math.random() < 0.35 ? 1 : 045 }46 return { positions, speeds, isPrimary }47}4849function Particles({ count, speed, size, theme }: ParticlesProps) {50 const pointsRef = React.useRef<THREE.Points>(null)51 const materialRef = React.useRef<THREE.PointsMaterial>(null)52 const { primaryColor, mutedColor } = useShadcnTheme(theme)5354 // Stable distribution; only regenerated when the count changes.55 const { positions, speeds, isPrimary } = React.useMemo(56 () => seedParticles(count),57 [count]58 )5960 // A fresh color buffer is built whenever the theme changes.61 const colors = React.useMemo(() => {62 const arr = new Float32Array(count * 3)63 for (let i = 0; i < count; i++) {64 const i3 = i * 365 const c = isPrimary[i] ? primaryColor : mutedColor66 arr[i3] = c.r67 arr[i3 + 1] = c.g68 arr[i3 + 2] = c.b69 }70 return arr71 }, [count, isPrimary, primaryColor, mutedColor])7273 useFrame((state, delta) => {74 const points = pointsRef.current75 if (!points) return76 const t = state.clock.elapsedTime77 points.rotation.y += delta * 0.05 * speed7879 const pos = points.geometry.getAttribute("position") as THREE.BufferAttribute80 const arr = pos.array as Float32Array81 for (let i = 0; i < speeds.length; i++) {82// … 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 |
