You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

778 lines
22 KiB

import { useRef, useMemo } from 'react';
import { useFrame } from '@react-three/fiber';
import { SpotLight, Text } from '@react-three/drei';
import * as THREE from 'three';
function DanceFloor() {
return (
<mesh rotation={[-Math.PI / 2, 0, 0]} receiveShadow>
<planeGeometry args={[30, 30]} />
<meshStandardMaterial
color="#8b4513"
roughness={0.6}
metalness={0.0}
/>
</mesh>
);
}
function Balloons() {
const balloons = useMemo(() => {
const positions = [];
for (let i = 0; i < 15; i++) {
positions.push([
(Math.random() - 0.5) * 25,
2 + Math.random() * 8,
(Math.random() - 0.5) * 25
]);
}
return positions;
}, []);
return (
<>
{balloons.map((pos, i) => (
<group key={i} position={pos}>
<mesh>
<sphereGeometry args={[0.3, 16, 16]} />
<meshStandardMaterial
color={['#ff0000', '#00ff00', '#0000ff', '#ffff00', '#ff00ff', '#00ffff'][i % 6]}
roughness={0.3}
metalness={0.1}
/>
</mesh>
<mesh position={[0, -0.3, 0]}>
<cylinderGeometry args={[0.01, 0.01, 1, 8]} />
<meshStandardMaterial color="#333" />
</mesh>
</group>
))}
</>
);
}
function Confetti() {
const confettiRef = useRef();
const count = 250;
const positions = useMemo(() => {
const pos = new Float32Array(count * 3);
for (let i = 0; i < count * 3; i += 3) {
pos[i] = (Math.random() - 0.5) * 30;
pos[i + 1] = Math.random() * 15;
pos[i + 2] = (Math.random() - 0.5) * 30;
}
return pos;
}, []);
const colors = useMemo(() => {
const cols = new Float32Array(count * 3);
const colorArray = [
[1, 0, 0], [0, 1, 0], [0, 0, 1],
[1, 1, 0], [1, 0, 1], [0, 1, 1]
];
for (let i = 0; i < count; i++) {
const color = colorArray[Math.floor(Math.random() * colorArray.length)];
cols[i * 3] = color[0];
cols[i * 3 + 1] = color[1];
cols[i * 3 + 2] = color[2];
}
return cols;
}, []);
useFrame((state) => {
if (confettiRef.current) {
confettiRef.current.rotation.y += 0.001;
const positions = confettiRef.current.geometry.attributes.position.array;
for (let i = 1; i < positions.length; i += 3) {
positions[i] -= 0.01;
if (positions[i] < 0) {
positions[i] = 15;
}
}
confettiRef.current.geometry.attributes.position.needsUpdate = true;
}
});
return (
<points ref={confettiRef}>
<bufferGeometry>
<bufferAttribute
attach="attributes-position"
count={count}
array={positions}
itemSize={3}
/>
<bufferAttribute
attach="attributes-color"
count={count}
array={colors}
itemSize={3}
/>
</bufferGeometry>
<pointsMaterial
size={0.1}
vertexColors
transparent
opacity={0.8}
blending={THREE.AdditiveBlending}
/>
</points>
);
}
function Person({ position, index, lookAt }) {
const groupRef = useRef();
const leftArmRef = useRef();
const rightArmRef = useRef();
// Randomize skin tones
const skinTones = ['#ffdbac', '#f1c27d', '#e0ac69', '#c68642', '#8d5524'];
const skinColor = skinTones[index % skinTones.length];
// More color variety for shirts
const shirtColors = [
'#ff6b6b', '#4ecdc4', '#45b7d1', '#f9ca24', '#6c5ce7',
'#a29bfe', '#fd79a8', '#00b894', '#e17055', '#74b9ff',
'#a29bfe', '#fd79a8', '#fdcb6e', '#e84393', '#00cec9'
];
const shirtColor = shirtColors[index % shirtColors.length];
useFrame(() => {
if (groupRef.current && lookAt) {
// Face the person they're talking to
const dx = lookAt[0] - position[0];
const dz = lookAt[2] - position[2];
groupRef.current.rotation.y = Math.atan2(dx, dz);
// Subtle idle animation - slight head/body movement
const t = Date.now() * 0.005;
const offset = index * 0.3;
groupRef.current.rotation.y += Math.sin(t * 0.05 + offset) * 0.05;
// Subtle arm gestures while talking
if (leftArmRef.current) {
leftArmRef.current.rotation.x = Math.sin(t * 1.7 + offset) * 0.1;
leftArmRef.current.rotation.z = Math.sin(t * 1.5 + offset) * 0.05;
}
if (rightArmRef.current) {
rightArmRef.current.rotation.x = Math.sin(t * 1.5 + offset + 1) * 0.1;
rightArmRef.current.rotation.z = Math.sin(t * 1.5 + offset + 1) * 0.05;
}
}
});
return (
<group ref={groupRef} position={position}>
{/* Head */}
<mesh position={[0, 1.45, 0]}>
<sphereGeometry args={[0.15, 16, 16]} />
<meshStandardMaterial color={skinColor} />
</mesh>
{/* Torso */}
<mesh position={[0, 1.1, 0]}>
<boxGeometry args={[0.35, 0.5, 0.25]} />
<meshStandardMaterial color={shirtColor} />
</mesh>
{/* Left Arm */}
<group ref={leftArmRef} position={[-0.18, 1.1, 0]}>
<mesh position={[-0.15, 0, 0]}>
<boxGeometry args={[0.3, 0.12, 0.12]} />
<meshStandardMaterial color={skinColor} />
</mesh>
</group>
{/* Right Arm */}
<group ref={rightArmRef} position={[0.18, 1.1, 0]}>
<mesh position={[0.15, 0, 0]}>
<boxGeometry args={[0.3, 0.12, 0.12]} />
<meshStandardMaterial color={skinColor} />
</mesh>
</group>
{/* Left Leg - standing still */}
<mesh position={[-0.1, 0.7, 0]}>
<boxGeometry args={[0.12, 0.7, 0.12]} />
<meshStandardMaterial color="#333" />
</mesh>
<mesh position={[-0.1, 0.25, 0.05]}>
<boxGeometry args={[0.15, 0.1, 0.2]} />
<meshStandardMaterial color="#222" />
</mesh>
{/* Right Leg - standing still */}
<mesh position={[0.1, 0.7, 0]}>
<boxGeometry args={[0.12, 0.7, 0.12]} />
<meshStandardMaterial color="#333" />
</mesh>
<mesh position={[0.1, 0.25, 0.05]}>
<boxGeometry args={[0.15, 0.1, 0.2]} />
<meshStandardMaterial color="#222" />
</mesh>
</group>
);
}
function People() {
// Create groups of people talking to each other - 80 total people
const groups = useMemo(() => {
const peopleGroups = [];
const groupCount = 180; // 30 groups
for (let g = 0; g < groupCount; g++) {
const groupAngle = (g / groupCount) * Math.PI * 3;
const groupRadius = 2 + Math.random() * 10; // Spread around the room
const groupCenter = [
Math.cos(groupAngle) * groupRadius,
0,
Math.sin(groupAngle) * groupRadius
];
// Vary group sizes to get close to 80 total (mostly groups of 3, some 2)
const peopleInGroup = g < 25 ? 3 : 2;
const group = [];
for (let p = 0; p < peopleInGroup; p++) {
const personAngle = (p / peopleInGroup) * Math.PI * 2;
const personRadius = 0.8;
const personPos = [
groupCenter[0] + Math.cos(personAngle) * personRadius,
0,
groupCenter[2] + Math.sin(personAngle) * personRadius
];
// Person they're looking at (next person in circle, or center)
const lookAtIndex = (p + 1) % peopleInGroup;
const lookAtPos = [
groupCenter[0] + Math.cos((lookAtIndex / peopleInGroup) * Math.PI * 2) * personRadius,
0,
groupCenter[2] + Math.sin((lookAtIndex / peopleInGroup) * Math.PI * 2) * personRadius
];
group.push({ position: personPos, lookAt: lookAtPos });
}
peopleGroups.push(...group);
}
return peopleGroups;
}, []);
return (
<>
{groups.map((person, i) => (
<Person key={i} position={person.position} index={i} lookAt={person.lookAt} />
))}
</>
);
}
function YouTubeBanner() {
// Create triangle geometry for play button
const triangleShape = useMemo(() => {
const shape = new THREE.Shape();
shape.moveTo(0, 0.4);
shape.lineTo(0.7, 0);
shape.lineTo(0, -0.4);
shape.lineTo(0, 0.4);
return shape;
}, []);
return (
<group position={[0, -1, 0.1]}>
{/* Red background */}
<mesh>
<planeGeometry args={[5, 2]} />
<meshStandardMaterial color="#ff0000" />
</mesh>
{/* White play button triangle */}
<mesh position={[0, 0, 0.01]}>
<shapeGeometry args={[triangleShape]} />
<meshStandardMaterial color="#ffffff" />
</mesh>
{/* YouTube text */}
<Text
position={[0.3, 0, 0.01]}
fontSize={0.35}
color="#ffffff"
anchorX="left"
anchorY="middle"
fontWeight="bold"
>
YouTube
</Text>
</group>
);
}
function DJ() {
const groupRef = useRef();
const leftArmRef = useRef();
const rightArmRef = useRef();
useFrame((state) => {
if (groupRef.current) {
const t = state.clock.elapsedTime;
// Subtle head bobbing to the beat
groupRef.current.position.y = Math.sin(t * 2) * 0.05;
// Arm movements - mixing/scratching
if (leftArmRef.current) {
leftArmRef.current.rotation.x = Math.sin(t * 3) * 0.2;
leftArmRef.current.rotation.z = Math.sin(t * 3) * 0.1;
}
if (rightArmRef.current) {
rightArmRef.current.rotation.x = Math.sin(t * 3 + 1) * 0.2;
rightArmRef.current.rotation.z = Math.sin(t * 3 + 1) * 0.1;
}
}
});
return (
<group ref={groupRef} position={[0, 2, -13.3]}>
{/* Head */}
<mesh position={[0, 1.6, 0]}>
<sphereGeometry args={[0.18, 16, 16]} />
<meshStandardMaterial color="#d4a574" />
</mesh>
{/* Headphones */}
<mesh position={[0, 1.6, 0]} rotation={[0, 0, 0]}>
<torusGeometry args={[0.25, 0.05, 8, 16]} />
<meshStandardMaterial color="#1a1a1a" />
</mesh>
<mesh position={[-0.25, 1.6, 0]}>
<cylinderGeometry args={[0.12, 0.12, 0.15, 16]} />
<meshStandardMaterial color="#1a1a1a" />
</mesh>
<mesh position={[0.25, 1.6, 0]}>
<cylinderGeometry args={[0.12, 0.12, 0.15, 16]} />
<meshStandardMaterial color="#1a1a1a" />
</mesh>
{/* Torso */}
<mesh position={[0, 1.1, 0]}>
<boxGeometry args={[0.35, 0.5, 0.25]} />
<meshStandardMaterial color="#000000" />
</mesh>
{/* Left Arm */}
<group ref={leftArmRef} position={[-0.25, 1.1, 0]}>
<mesh position={[-0.15, 0, 0]}>
<boxGeometry args={[0.3, 0.12, 0.12]} />
<meshStandardMaterial color="#d4a574" />
</mesh>
</group>
{/* Right Arm */}
<group ref={rightArmRef} position={[0.25, 1.1, 0]}>
<mesh position={[0.15, 0, 0]}>
<boxGeometry args={[0.3, 0.12, 0.12]} />
<meshStandardMaterial color="#d4a574" />
</mesh>
</group>
{/* Legs */}
<mesh position={[-0.1, 0.7, 0]}>
<boxGeometry args={[0.12, 0.7, 0.12]} />
<meshStandardMaterial color="#333" />
</mesh>
<mesh position={[0.1, 0.7, 0]}>
<boxGeometry args={[0.12, 0.7, 0.12]} />
<meshStandardMaterial color="#333" />
</mesh>
{/* Shoes */}
<mesh position={[-0.1, 0.25, 0.05]}>
<boxGeometry args={[0.18, 0.1, 0.25]} />
<meshStandardMaterial color="#0a0a0a" />
</mesh>
<mesh position={[0.1, 0.25, 0.05]}>
<boxGeometry args={[0.18, 0.1, 0.25]} />
<meshStandardMaterial color="#0a0a0a" />
</mesh>
</group>
);
}
function DJBooth() {
return (
<group position={[0, 0, -14]}>
{/* Bar base - larger and more prominent */}
<mesh position={[0, 0.5, 0]}>
<boxGeometry args={[6, 1, 2]} />
<meshStandardMaterial color="#2a1a0a" roughness={0.3} metalness={0.2} />
</mesh>
{/* Bar top */}
<mesh position={[0, 1.2, 0]}>
<boxGeometry args={[6.2, 0.2, 2.2]} />
<meshStandardMaterial color="#1a0a0a" roughness={0.1} metalness={0.5} />
</mesh>
{/* DJ equipment section - raised */}
<mesh position={[0, 2, 0.6]}>
<boxGeometry args={[4, 1.5, 0.8]} />
<meshStandardMaterial color="#0a0a0a" />
</mesh>
{/* Equipment panel */}
<mesh position={[0, 2.5, 0.61]}>
<boxGeometry args={[3.5, 0.4, 0.1]} />
<meshStandardMaterial color="#1a1a1a" />
</mesh>
{/* Control buttons - more prominent */}
<mesh position={[-1.2, 2.5, 0.62]}>
<cylinderGeometry args={[0.2, 0.2, 0.08, 16]} />
<meshStandardMaterial color="#ff0000" emissive="#ff0000" emissiveIntensity={0.5} />
</mesh>
<mesh position={[0, 2.5, 0.62]}>
<cylinderGeometry args={[0.2, 0.2, 0.08, 16]} />
<meshStandardMaterial color="#00ff00" emissive="#00ff00" emissiveIntensity={0.5} />
</mesh>
<mesh position={[1.2, 2.5, 0.62]}>
<cylinderGeometry args={[0.2, 0.2, 0.08, 16]} />
<meshStandardMaterial color="#0000ff" emissive="#0000ff" emissiveIntensity={0.5} />
</mesh>
{/* Bar stools */}
<mesh position={[-2.5, 0.6, -1.2]} rotation={[0, 0, 0]}>
<cylinderGeometry args={[0.15, 0.15, 1.2, 16]} />
<meshStandardMaterial color="#654321" />
</mesh>
<mesh position={[-2.5, 1.2, -1.2]}>
<cylinderGeometry args={[0.4, 0.4, 0.1, 16]} />
<meshStandardMaterial color="#8b4513" />
</mesh>
<mesh position={[2.5, 0.6, -1.2]} rotation={[0, 0, 0]}>
<cylinderGeometry args={[0.15, 0.15, 1.2, 16]} />
<meshStandardMaterial color="#654321" />
</mesh>
<mesh position={[2.5, 1.2, -1.2]}>
<cylinderGeometry args={[0.4, 0.4, 0.1, 16]} />
<meshStandardMaterial color="#8b4513" />
</mesh>
{/* Banner hanging from above - bigger and more prominent */}
<group position={[0, 5.5, 0]}>
{/* YouTube logo banner background */}
<YouTubeBanner />
{/* Banner text - bigger */}
<Text
position={[0, -1, 0.12]}
rotation={[0, 0, 0]}
fontSize={0.4}
color="#ffffff"
anchorX="center"
anchorY="middle"
fontWeight="bold"
outlineWidth={0.02}
outlineColor="#000000"
>
1 MILLION SUBSCRIBERS
</Text>
{/* Rope/string to hang it */}
</group>
{/* Spotlight on the bar */}
<pointLight position={[0, 5, -14]} intensity={50} distance={15} color="#ffffff" />
</group>
);
}
function DJBoothSpotlight() {
const spotlightRef = useRef();
const targetRef = useRef();
useFrame(() => {
if (spotlightRef.current && targetRef.current) {
spotlightRef.current.target = targetRef.current;
}
});
return (
<group>
<spotLight
ref={spotlightRef}
position={[0, 10, -14]}
angle={Math.PI / 6}
penumbra={0.3}
intensity={300}
distance={20}
decay={1}
color="#ffffff"
castShadow
/>
<object3D ref={targetRef} position={[0, 2, -14]} />
</group>
);
}
function Speakers() {
return (
<>
<group position={[-14, 0, 0]}>
<mesh position={[0, 1.5, 0]}>
<boxGeometry args={[1, 3, 1]} />
<meshStandardMaterial color="#222" />
</mesh>
<mesh position={[0, 1.5, 0.51]}>
<cylinderGeometry args={[0.4, 0.4, 0.1, 32]} />
<meshStandardMaterial color="#111" />
</mesh>
</group>
<group position={[14, 0, 0]}>
<mesh position={[0, 1.5, 0]}>
<boxGeometry args={[1, 3, 1]} />
<meshStandardMaterial color="#222" />
</mesh>
<mesh position={[0, 1.5, 0.51]}>
<cylinderGeometry args={[0.4, 0.4, 0.1, 32]} />
<meshStandardMaterial color="#111" />
</mesh>
</group>
</>
);
}
function ThePrimeagen() {
const groupRef = useRef();
const leftArmRef = useRef();
const rightArmRef = useRef();
useFrame((state) => {
if (groupRef.current) {
const t = state.clock.elapsedTime;
// Subtle animation
groupRef.current.rotation.y = Math.sin(t * 0.5) * 0.1;
if (leftArmRef.current) {
leftArmRef.current.rotation.x = Math.sin(t * 2) * 0.2;
}
if (rightArmRef.current) {
rightArmRef.current.rotation.x = -Math.sin(t * 2) * 0.2;
}
}
});
return (
<group ref={groupRef} position={[10, 0, -8]}>
{/* Head */}
<mesh position={[0, 1.6, 0]}>
<sphereGeometry args={[0.18, 16, 16]} />
<meshStandardMaterial color="#d4a574" />
</mesh>
{/* Beard */}
<mesh position={[0, 1.45, 0.12]}>
<boxGeometry args={[0.25, 0.2, 0.08]} />
<meshStandardMaterial color="#2a2a2a" />
</mesh>
<mesh position={[-0.08, 1.4, 0.12]}>
<boxGeometry args={[0.1, 0.15, 0.08]} />
<meshStandardMaterial color="#2a2a2a" />
</mesh>
<mesh position={[0.08, 1.4, 0.12]}>
<boxGeometry args={[0.1, 0.15, 0.08]} />
<meshStandardMaterial color="#2a2a2a" />
</mesh>
{/* Glasses */}
<mesh position={[-0.08, 1.58, 0.1]}>
<torusGeometry args={[0.06, 0.01, 8, 16]} />
<meshStandardMaterial color="#1a1a1a" metalness={0.8} roughness={0.2} />
</mesh>
<mesh position={[0.08, 1.58, 0.1]}>
<torusGeometry args={[0.06, 0.01, 8, 16]} />
<meshStandardMaterial color="#1a1a1a" metalness={0.8} roughness={0.2} />
</mesh>
{/* Bridge */}
<mesh position={[0, 1.58, 0.1]} rotation={[0, 0, Math.PI / 2]}>
<boxGeometry args={[0.16, 0.01, 0.01]} />
<meshStandardMaterial color="#1a1a1a" metalness={0.8} roughness={0.2} />
</mesh>
{/* Hoodie */}
<mesh position={[0, 1.1, 0]}>
<boxGeometry args={[0.5, 0.6, 0.3]} />
<meshStandardMaterial color="#1a1a1a" />
</mesh>
{/* Hood */}
<mesh position={[0, 1.5, -0.05]}>
<coneGeometry args={[0.3, 0.3, 8]} />
<meshStandardMaterial color="#1a1a1a" />
</mesh>
{/* Left Arm */}
<group ref={leftArmRef} position={[-0.3, 1.1, 0]}>
<mesh position={[-0.15, 0, 0]}>
<boxGeometry args={[0.3, 0.15, 0.15]} />
<meshStandardMaterial color="#1a1a1a" />
</mesh>
</group>
{/* Right Arm */}
<group ref={rightArmRef} position={[0.3, 1.1, 0]}>
<mesh position={[0.15, 0, 0]}>
<boxGeometry args={[0.3, 0.15, 0.15]} />
<meshStandardMaterial color="#1a1a1a" />
</mesh>
</group>
{/* Legs */}
<mesh position={[-0.1, 0.7, 0]}>
<boxGeometry args={[0.15, 0.7, 0.15]} />
<meshStandardMaterial color="#2a2a2a" />
</mesh>
<mesh position={[0.1, 0.7, 0]}>
<boxGeometry args={[0.15, 0.7, 0.15]} />
<meshStandardMaterial color="#2a2a2a" />
</mesh>
{/* Shoes */}
<mesh position={[-0.1, 0.25, 0.05]}>
<boxGeometry args={[0.18, 0.1, 0.25]} />
<meshStandardMaterial color="#0a0a0a" />
</mesh>
<mesh position={[0.1, 0.25, 0.05]}>
<boxGeometry args={[0.18, 0.1, 0.25]} />
<meshStandardMaterial color="#0a0a0a" />
</mesh>
</group>
);
}
function Scene() {
const spot1Ref = useRef();
const spot2Ref = useRef();
const spot3Ref = useRef();
const spot4Ref = useRef();
const spot5Ref = useRef();
useFrame((state) => {
const t = state.clock.elapsedTime;
if (spot1Ref.current) {
const phase = t * 0.6;
spot1Ref.current.target.position.x = Math.cos(phase) * 3;
spot1Ref.current.target.position.z = Math.sin(phase) * 3;
spot1Ref.current.target.position.y = 2.5 + Math.sin(t * 1.2) * 0.5;
}
if (spot2Ref.current) {
const phase = t * 0.6 + (Math.PI * 2 / 5);
spot2Ref.current.target.position.x = Math.cos(phase) * 3;
spot2Ref.current.target.position.z = Math.sin(phase) * 3;
spot2Ref.current.target.position.y = 2.5 + Math.sin(t * 1.2) * 0.5;
}
if (spot3Ref.current) {
const phase = t * 0.6 + (Math.PI * 4 / 5);
spot3Ref.current.target.position.x = Math.cos(phase) * 3;
spot3Ref.current.target.position.z = Math.sin(phase) * 3;
spot3Ref.current.target.position.y = 2.5 + Math.sin(t * 1.2) * 0.5;
}
if (spot4Ref.current) {
const phase = t * 0.6 + (Math.PI * 6 / 5);
spot4Ref.current.target.position.x = Math.cos(phase) * 3;
spot4Ref.current.target.position.z = Math.sin(phase) * 3;
spot4Ref.current.target.position.y = 2.5 + Math.sin(t * 1.2) * 0.5;
}
if (spot5Ref.current) {
const phase = t * 0.6 + (Math.PI * 8 / 5);
spot5Ref.current.target.position.x = Math.cos(phase) * 3;
spot5Ref.current.target.position.z = Math.sin(phase) * 3;
spot5Ref.current.target.position.y = 2.5 + Math.sin(t * 1.2) * 0.5;
}
});
return (
<>
<DanceFloor />
<Balloons />
<Confetti />
<People />
<ThePrimeagen />
<DJBooth />
<DJ />
<DJBoothSpotlight />
<Speakers />
<SpotLight
ref={spot1Ref}
position={[10, 12, 10]}
angle={Math.PI / 7}
penumbra={0.5}
intensity={250}
distance={60}
decay={1}
color="#ff3366"
castShadow
/>
<SpotLight
ref={spot2Ref}
position={[-10, 12, 10]}
angle={Math.PI / 7}
penumbra={0.5}
intensity={250}
distance={60}
decay={1}
color="#33ffcc"
castShadow
/>
<SpotLight
ref={spot3Ref}
position={[0, 12, -12]}
angle={Math.PI / 7}
penumbra={0.5}
intensity={250}
distance={60}
decay={1}
color="#3366ff"
castShadow
/>
<SpotLight
ref={spot4Ref}
position={[-10, 12, -10]}
angle={Math.PI / 7}
penumbra={0.5}
intensity={250}
distance={60}
decay={1}
color="#ffff00"
castShadow
/>
<SpotLight
ref={spot5Ref}
position={[10, 12, -10]}
angle={Math.PI / 7}
penumbra={0.5}
intensity={250}
distance={60}
decay={1}
color="#ff00ff"
castShadow
/>
</>
);
}
export default Scene;