import React, { useRef, useEffect } from 'react'
import * as THREE from 'three'
interface CityscapeBackgroundProps {
className?: string
}
export function CityscapeBackground({ className }: CityscapeBackgroundProps) {
const canvasRef = useRef<HTMLCanvasElement>(null)
const sceneRef = useRef<THREE.Scene>()
const rendererRef = useRef<THREE.WebGLRenderer>()
const cameraRef = useRef<THREE.PerspectiveCamera>()
const animationIdRef = useRef<number>()
const speedLinesRef = useRef<THREE.LineSegments>()
const mouseRef = useRef(new THREE.Vector2())
const startTimeRef = useRef<number>(Date.now())
useEffect(() => {
if (!canvasRef.current) {
console.log('CityscapeBackground: Canvas ref not available')
return
}
console.log('FuturistBackground: Initializing 3D futurist scene')
// Scene setup with futurist art movement atmosphere
const scene = new THREE.Scene()
scene.background = new THREE.Color(0x000000) // Black background
scene.fog = new THREE.Fog(0x000000, 30, 120) // Black fog for depth
// Camera setup for isometric view
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 200)
camera.position.set(30, 25, 30)
camera.lookAt(0, 0, 0)
// Renderer setup
const renderer = new THREE.WebGLRenderer({
canvas: canvasRef.current,
antialias: false, // Disable for better performance
alpha: false // Disable alpha for opaque background
})
renderer.setSize(window.innerWidth, window.innerHeight)
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 1.5)) // Limit pixel ratio for performance
// Store references
sceneRef.current = scene
rendererRef.current = renderer
cameraRef.current = camera
// Futurist art movement lighting - dramatic contrasts inspired by paintings
const ambientLight = new THREE.AmbientLight(0x1a1a1a, 0.4) // Warm grey ambient
scene.add(ambientLight)
// Bold primary light - warm like industrial sunlight
const primaryLight = new THREE.DirectionalLight(0xffaa33, 1.8) // Golden orange
primaryLight.position.set(-25, 35, 20)
scene.add(primaryLight)
// Dynamic red light for energy and motion
const dynamicLight = new THREE.DirectionalLight(0xdd2222, 1.2) // Bold red
dynamicLight.position.set(20, 10, -15)
scene.add(dynamicLight)
// Contrasting blue for depth and drama
const contrastLight = new THREE.PointLight(0x2244bb, 0.8, 50) // Deep blue
contrastLight.position.set(-30, 5, 0)
scene.add(contrastLight)
// Yellow accent for vibrant highlights
const accentLight = new THREE.PointLight(0xdddd22, 0.7, 40) // Yellow
accentLight.position.set(25, 20, -5)
scene.add(accentLight)
// Green industrial light
const industrialLight = new THREE.PointLight(0x22aa22, 0.6, 45) // Industrial green
industrialLight.position.set(0, -5, 30)
scene.add(industrialLight)
// Create speed lines
createSpeedLines()
console.log('FuturistBackground: Dynamic futurist art created, starting animation')
// Reset start time
startTimeRef.current = Date.now()
// Animation loop
const animate = () => {
animationIdRef.current = requestAnimationFrame(animate)
const time = Date.now() * 0.001 // Convert to seconds for smoother animation
// Animate speed lines
animateSpeedLines(time)
// Apply interactive effects
applyFuturistInteractions()
// Dramatic camera movement inspired by futurist dynamism
const cameraSpeed = time * 0.2
camera.position.x = 20 + Math.sin(cameraSpeed * 1.3) * 12 + Math.cos(cameraSpeed * 0.7) * 6
camera.position.y = 15 + Math.cos(cameraSpeed * 0.9) * 8 + Math.sin(cameraSpeed * 1.1) * 4
camera.position.z = 25 + Math.sin(cameraSpeed * 0.6) * 15 + Math.cos(cameraSpeed * 1.4) * 8
// Dynamic look-at point for more dramatic perspective shifts
const lookAtTarget = new THREE.Vector3(
Math.sin(cameraSpeed * 0.8) * 5,
Math.cos(cameraSpeed * 0.5) * 3,
Math.sin(cameraSpeed * 1.2) * 8
)
camera.lookAt(lookAtTarget)
renderer.render(scene, camera)
}
animate()
// Mouse tracking for interactive effects
const handleMouseMove = (event: MouseEvent) => {
// Convert screen coordinates to normalized device coordinates (-1 to +1)
mouseRef.current.set(
(event.clientX / window.innerWidth) * 2 - 1,
-(event.clientY / window.innerHeight) * 2 + 1
)
}
// Handle window resize
const handleResize = () => {
if (camera && renderer) {
camera.aspect = window.innerWidth / window.innerHeight
camera.updateProjectionMatrix()
renderer.setSize(window.innerWidth, window.innerHeight)
}
}
window.addEventListener('mousemove', handleMouseMove)
window.addEventListener('resize', handleResize)
// Cleanup
return () => {
if (animationIdRef.current) {
cancelAnimationFrame(animationIdRef.current)
}
window.removeEventListener('mousemove', handleMouseMove)
window.removeEventListener('resize', handleResize)
// Dispose of Three.js objects
scene.clear()
renderer.dispose()
}
}, [])
const applyFuturistInteractions = () => {
const camera = cameraRef.current!
const raycaster = new THREE.Raycaster()
// Convert mouse coordinates to world position
raycaster.setFromCamera(mouseRef.current, camera)
// Interactive speed line distortion
if (speedLinesRef.current) {
const positions = speedLinesRef.current.geometry.attributes.position.array as Float32Array
for (let i = 0; i < positions.length; i += 6) { // Step by 6 for line pairs
const linePos = new THREE.Vector3(positions[i], positions[i + 1], positions[i + 2])
const mouseWorldPos = new THREE.Vector3()
raycaster.ray.at(linePos.z, mouseWorldPos)
const distance = linePos.distanceTo(mouseWorldPos)
const distortRadius = 15
if (distance < distortRadius) {
const distortStrength = (distortRadius - distance) / distortRadius
// Create dramatic wave distortion around mouse
const waveX = Math.sin(Date.now() * 0.02 + i) * distortStrength * 2.0
const waveY = Math.cos(Date.now() * 0.025 + i) * distortStrength * 1.5
positions[i] += waveX
positions[i + 1] += waveY
positions[i + 3] += waveX * 0.8 // End point follows with slight lag
positions[i + 4] += waveY * 0.8
}
}
speedLinesRef.current.geometry.attributes.position.needsUpdate = true
}
}
const createSpeedLines = () => {
const scene = sceneRef.current!
// Create sharp, dramatic speed lines
const motionVertices = []
const motionColors = []
// High-contrast speed colors
const speedColors = [
{ r: 1.0, g: 1.0, b: 1.0 }, // Brilliant white
{ r: 1.0, g: 0.1, b: 0.1 }, // Sharp red
{ r: 1.0, g: 0.8, b: 0.0 }, // Electric yellow
{ r: 0.0, g: 1.0, b: 1.0 }, // Cyan
{ r: 1.0, g: 0.0, b: 1.0 }, // Magenta
]
// Create 300 sharp speed lines for intense motion
for (let i = 0; i < 300; i++) {
// Start point - spread across space
const startX = (Math.random() - 0.5) * 100
const startY = (Math.random() - 0.5) * 40
const startZ = Math.random() * -200
// End point - long streaks suggesting extreme speed
const endX = startX + (Math.random() - 0.5) * 20
const endY = startY + (Math.random() - 0.5) * 8
const endZ = startZ + 20 + Math.random() * 40 // Longer streaks
motionVertices.push(startX, startY, startZ)
motionVertices.push(endX, endY, endZ)
// High-contrast colors for sharp appearance
const colorIndex = Math.floor(Math.random() * speedColors.length)
const color = speedColors[colorIndex]
motionColors.push(color.r, color.g, color.b)
motionColors.push(color.r, color.g, color.b)
}
const motionGeometry = new THREE.BufferGeometry()
motionGeometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(motionVertices), 3))
motionGeometry.setAttribute('color', new THREE.BufferAttribute(new Float32Array(motionColors), 3))
const motionMaterial = new THREE.LineBasicMaterial({
vertexColors: true,
transparent: true,
opacity: 0.9,
linewidth: 2
})
const speedLines = new THREE.LineSegments(motionGeometry, motionMaterial)
speedLinesRef.current = speedLines
scene.add(speedLines)
}
const animateSpeedLines = (time: number) => {
if (!speedLinesRef.current) return
const positions = speedLinesRef.current.geometry.attributes.position.array as Float32Array
for (let i = 0; i < positions.length; i += 6) { // Step by 6 since we have line pairs
// Extreme speed effect - lines blazing toward camera
positions[i + 2] += 6.0 + Math.sin(time * 2 + i) * 3.0 // High variable speed
positions[i + 5] += 6.0 + Math.sin(time * 2 + i) * 3.0 // End point matches
// Intense lateral movement for speed blur
const lateralMotion = Math.sin(time * 1.5 + i * 0.15) * 0.8
positions[i] += lateralMotion
positions[i + 3] += lateralMotion
// Sharp vertical oscillation for dynamic energy
const verticalMotion = Math.cos(time * 2.2 + i * 0.12) * 0.6
positions[i + 1] += verticalMotion
positions[i + 4] += verticalMotion
// Reset lines that have blazed past camera
if (positions[i + 2] > 100) {
positions[i + 2] = -250 - Math.random() * 100
positions[i + 5] = positions[i + 2] + 20 + Math.random() * 40
positions[i] = (Math.random() - 0.5) * 100
positions[i + 1] = (Math.random() - 0.5) * 40
positions[i + 3] = positions[i] + (Math.random() - 0.5) * 20
positions[i + 4] = positions[i + 1] + (Math.random() - 0.5) * 8
}
}
speedLinesRef.current.geometry.attributes.position.needsUpdate = true
// Rapid rotation for intense motion blur
speedLinesRef.current.rotation.x += 0.008
speedLinesRef.current.rotation.y += 0.012
speedLinesRef.current.rotation.z += 0.025
}
return (
<canvas
ref={canvasRef}
className={className}
style={{
position: 'fixed',
top: 0,
left: 0,
width: '100%',
height: '100%',
zIndex: 0,
pointerEvents: 'auto',
opacity: 1,
visibility: 'visible',
}}
/>
)
}