Components
Loading preview...
import React, { useRef, useMemo, useEffect } from 'react';
import { Canvas, useFrame, useThree } from '@react-three/fiber';
import * as THREE from 'three';
import { Bloom, EffectComposer, Noise, Vignette } from '@react-three/postprocessing';
/**
* CrystallineParticle System
* Features:
* - Interactive magnetic fluid dynamics
* - Hair-thin filaments (connections)
* - Kinetic ripple pulses on click
* - Bioluminescent styling (Cyan/Indigo)
*/
const PARTICLE_COUNT = 800;
const MAX_DISTANCE = 1.2;
const NeuralLattice: React.FC = () => {
const meshRef = useRef<THREE.Points>(null!);
const lineRef = useRef<THREE.LineSegments>(null!);
const mouse = useRef(new THREE.Vector2(0, 0));
const ripple = useRef({ active: false, radius: 0, origin: new THREE.Vector3() });
const { viewport } = useThree();
// Create initial nodes
const nodes = useMemo(() => {
return Array.from({ length: PARTICLE_COUNT }).map(() => ({
position: new THREE.Vector3(
(Math.random() - 0.5) * 10,
(Math.random() - 0.5) * 10,
(Math.random() - 0.5) * 10
),
velocity: new THREE.Vector3(
(Math.random() - 0.5) * 0.005,
(Math.random() - 0.5) * 0.005,
(Math.random() - 0.5) * 0.005
),
basePos: new THREE.Vector3(),
}));
}, []);
// Buffer setups
const [particlePositions, particleColors] = useMemo(() => {
const pos = new Float32Array(PARTICLE_COUNT * 3);
const col = new Float32Array(PARTICLE_COUNT * 3);
const colorA = new THREE.Color('#00f2ff');
const colorB = new THREE.Color('#7000ff');
for (let i = 0; i < PARTICLE_COUNT; i++) {
const mixed = colorA.clone().lerp(colorB, Math.random());
col[i * 3] = mixed.r;
col[i * 3 + 1] = mixed.g;
col[i * 3 + 2] = mixed.b;
}
return [pos, col];
}, []);
useFrame((state) => {
const time = state.clock.getElapsedTime();
const positions = meshRef.current.geometry.attributes.position.array as Float32Array;
const linePositions: number[] = [];
const lineColors: number[] = [];
// Smooth mouse follow
mouse.current.lerp(new THREE.Vector2(state.mouse.x * viewport.width / 2, state.mouse.y * viewport.height / 2), 0.1);
// Update ripple
if (ripple.current.active) {
ripple.current.radius += 0.15;
if (ripple.current.radius > 15) ripple.current.active = false;
}
nodes.forEach((node, i) => {
// 1. Ambient Movement (Harmonic drift)
node.position.x += node.velocity.x + Math.sin(time + i) * 0.001;
node.position.y += node.velocity.y + Math.cos(time + i) * 0.001;
node.position.z += node.velocity.z;
// 2. Magnetic Mouse Interaction
const dx = node.position.x - mouse.current.x;
const dy = node.position.y - mouse.current.y;
const dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 2.5) {
const force = (1 - dist / 2.5) * 0.02;
node.position.x += dx * force;
node.position.y += dy * force;
}
// 3. Pulse interaction
if (ripple.current.active) {
const rDist = node.position.distanceTo(ripple.current.origin);
const rDiff = Math.abs(rDist - ripple.current.radius);
if (rDiff < 0.5) {
const force = (1 - rDiff / 0.5) * 0.1;
node.position.z += force;
}
}
// 4. Boundary Logic
if (Math.abs(node.position.x) > 6) node.velocity.x *= -1;
if (Math.abs(node.position.y) > 6) node.velocity.y *= -1;
if (Math.abs(node.position.z) > 6) node.velocity.z *= -1;
positions[i * 3] = node.position.x;
positions[i * 3 + 1] = node.position.y;
positions[i * 3 + 2] = node.position.z;
// 5. Build Filaments (Lines)
// We skip most comparisons for performance, only check next N neighbors
for (let j = i + 1; j < i + 15; j++) {
const other = nodes[j % PARTICLE_COUNT];
const d = node.position.distanceTo(other.position);
if (d < MAX_DISTANCE) {
linePositions.push(node.position.x, node.position.y, node.position.z);
linePositions.push(other.position.x, other.position.y, other.position.z);
const alpha = (1 - d / MAX_DISTANCE) * 0.4;
const r = 0.2 + alpha;
const g = 0.5 + alpha;
const b = 0.9 + alpha;
lineColors.push(r, g, b, r, g, b);
}
}
});
meshRef.current.geometry.attributes.position.needsUpdate = true;
lineRef.current.geometry.setAttribute('position', new THREE.Float32BufferAttribute(linePositions, 3));
lineRef.current.geometry.setAttribute('color', new THREE.Float32BufferAttribute(lineColors, 3));
// Slow rotation
meshRef.current.rotation.y = time * 0.05;
lineRef.current.rotation.y = time * 0.05;
});
const handlePointerDown = (e: any) => {
ripple.current.active = true;
ripple.current.radius = 0;
ripple.current.origin.set(
(e.clientX / window.innerWidth) * 2 - 1,
-(e.clientY / window.innerHeight) * 2 + 1,
0.5
).unproject(e.camera);
};
return (
<group onPointerDown={handlePointerDown}>
<points ref={meshRef}>
<bufferGeometry>
<bufferAttribute
attach="attributes-position"
count={PARTICLE_COUNT}
array={particlePositions}
itemSize={3}
/>
<bufferAttribute
attach="attributes-color"
count={PARTICLE_COUNT}
array={particleColors}
itemSize={3}
/>
</bufferGeometry>
<pointsMaterial
size={0.015}
vertexColors
transparent
opacity={0.8}
blending={THREE.AdditiveBlending}
sizeAttenuation={true}
/>
</points>
<lineSegments ref={lineRef}>
<bufferGeometry />
<lineBasicMaterial
vertexColors
transparent
opacity={0.3}
blending={THREE.AdditiveBlending}
linewidth={1}
/>
</lineSegments>
</group>
);
};
export default function CrystallineNeuralBackground() {
return (
<div className="fixed inset-0 w-full h-full bg-[#020205] overflow-hidden">
{/* Cinematic Grain Overlay */}
<div
className="absolute inset-0 pointer-events-none opacity-[0.03] z-20"
style={{
backgroundImage: `url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E")`
}}
/>
{/* Glassmorphism Vignette */}
<div className="absolute inset-0 z-10 pointer-events-none bg-[radial-gradient(circle_at_center,transparent_0%,rgba(2,2,5,0.6)_100%)]" />
<Canvas
camera={{ position: [0, 0, 5], fov: 60 }}
dpr={[1, 2]}
>
<color attach="background" args={['#020205']} />
<NeuralLattice />
<EffectComposer disableNormalPass>
<Bloom
luminanceThreshold={0.2}
mipmapBlur
intensity={1.5}
radius={0.4}
/>
<Noise opacity={0.05} />
<Vignette eskil={false} offset={0.1} darkness={1.1} />
</EffectComposer>
</Canvas>
{/* Foreground Example */}
<div className="absolute inset-0 flex flex-col items-center justify-center pointer-events-none z-30 px-6">
<div className="max-w-xl text-center">
<h1 className="text-white/20 text-5xl md:text-7xl font-thin tracking-[1em] uppercase mb-8 select-none">
Void
</h1>
<div className="h-[1px] w-32 bg-gradient-to-r from-transparent via-cyan-500/30 to-transparent mx-auto mb-8" />
<p className="text-cyan-100/30 font-mono text-xs tracking-[0.3em] uppercase leading-loose select-none">
Generative Neural Architecture<br/>
Crystalline Dynamics v.04
</p>
</div>
</div>
</div>
);
}