A soft drifting cloud of cyan, magenta, and violet points that glows like a nebula.
three @react-three/fiber @react-three/drei<Nebula /> inside your own <Canvas>.import { useMemo, useRef } from 'react';
import { useFrame } from '@react-three/fiber';
import * as THREE from 'three';
import type { Points } from 'three';
const N = 2200;
function rand(seed: number) {
const x = Math.sin(seed) * 43758.5453;
return x - Math.floor(x);
}
export default function Nebula({ scale = 1 }: { color?: string; scale?: number }) {
const ref = useRef<Points>(null);
const { positions, colors } = useMemo(() => {
const pos = new Float32Array(N * 3);
const col = new Float32Array(N * 3);
const palette = [new THREE.Color('#22e0ff'), new THREE.Color('#ff2fd0'), new THREE.Color('#8a5cff')];
for (let i = 0; i < N; i++) {
const r = Math.pow(rand(i * 1.1 + 0.3), 0.6);
const th = rand(i * 2.3 + 1.1) * Math.PI * 2;
const ph = Math.acos(2 * rand(i * 3.7 + 2.2) - 1);
// wide, shallow cloud that reads well behind hero text
pos[i * 3] = Math.sin(ph) * Math.cos(th) * r * 3.4;
pos[i * 3 + 1] = Math.sin(ph) * Math.sin(th) * r * 1.9;
pos[i * 3 + 2] = Math.cos(ph) * r * 2.2;
const c = palette[Math.floor(rand(i * 5.9 + 0.7) * palette.length) % palette.length];
col[i * 3] = c.r;
col[i * 3 + 1] = c.g;
col[i * 3 + 2] = c.b;
}
return { positions: pos, colors: col };
}, []);
useFrame((state) => {
if (ref.current) ref.current.rotation.y = state.clock.elapsedTime * 0.04;
});
return (
<points ref={ref} scale={scale}>
<bufferGeometry>
<bufferAttribute attach="attributes-position" args={[positions, 3]} />
<bufferAttribute attach="attributes-color" args={[colors, 3]} />
</bufferGeometry>
<pointsMaterial
size={0.055}
sizeAttenuation
vertexColors
transparent
opacity={0.85}
blending={THREE.AdditiveBlending}
depthWrite={false}
toneMapped={false}
/>
</points>
);
}