Back to Component Explorer
3D Spatial Globe
effectsExpanded high-definition rotating 3D globe with interactive multi-axis dragging, latitude matrix, and beacons.
#globe#3d#interactive#spatial
Live Interactive Preview
360° Multi-Axis Spatial Rotation(20°, 45°)
Component Source Code
Select language in the dropdownThreeDGlobe.tsx
1'use client';23import React, { useState, useEffect, useRef } from 'react';45export default function ThreeDGlobe() {6 const [rotation, setRotation] = useState({ x: 15, y: 45 });7 const [isDragging, setIsDragging] = useState(false);8 const [dragStart, setDragStart] = useState({ x: 0, y: 0 });9 const containerRef = useRef<HTMLDivElement>(null);1011 useEffect(() => {12 if (isDragging) return;13 const interval = setInterval(() => {14 setRotation((prev) => ({ ...prev, y: (prev.y + 0.4) % 360 }));15 }, 25);16 return () => clearInterval(interval);17 }, [isDragging]);1819 const handleMouseDown = (e: React.MouseEvent) => {20 setIsDragging(true);21 setDragStart({ x: e.clientX, y: e.clientY });22 };2324 const handleMouseMove = (e: React.MouseEvent) => {25 if (!isDragging) return;26 const deltaX = e.clientX - dragStart.x;27 const deltaY = e.clientY - dragStart.y;28 setRotation((prev) => ({29 x: Math.max(-75, Math.min(75, prev.x - deltaY * 0.3)),30 y: (prev.y + deltaX * 0.3) % 36031 }));32 setDragStart({ x: e.clientX, y: e.clientY });33 };3435 const handleMouseUp = () => setIsDragging(false);3637 const handleTouchStart = (e: React.TouchEvent) => {38 if (e.touches.length === 1) {39 setIsDragging(true);40 setDragStart({ x: e.touches[0].clientX, y: e.touches[0].clientY });41 }42 };4344 const handleTouchMove = (e: React.TouchEvent) => {45 if (!isDragging || e.touches.length !== 1) return;46 const deltaX = e.touches[0].clientX - dragStart.x;47 const deltaY = e.touches[0].clientY - dragStart.y;48 setRotation((prev) => ({49 x: Math.max(-75, Math.min(75, prev.x - deltaY * 0.3)),50 y: (prev.y + deltaX * 0.3) % 36051 }));52 setDragStart({ x: e.touches[0].clientX, y: e.touches[0].clientY });53 };5455 const meridians = [0, 30, 60, 90, 120, 150];56 const parallels = [-60, -40, -20, 0, 20, 40, 60];5758 return (59 <div60 ref={containerRef}61 onMouseDown={handleMouseDown}62 onMouseMove={handleMouseMove}63 onMouseUp={handleMouseUp}64 onMouseLeave={handleMouseUp}65 onTouchStart={handleTouchStart}66 onTouchMove={handleTouchMove}67 onTouchEnd={handleMouseUp}68 className="relative flex flex-col items-center justify-center p-4 sm:p-6 cursor-grab active:cursor-grabbing select-none w-full"69 >70 <div className="relative h-56 w-56 sm:h-72 sm:w-72 md:h-80 md:w-80 flex items-center justify-center [perspective:1200px]">71 <div className="absolute inset-0 rounded-full bg-blue-500/20 blur-3xl pointer-events-none" />72 <div className="absolute inset-2 rounded-full border-2 border-cyan-500/40 bg-radial from-cyan-950/40 via-slate-950 to-slate-950 shadow-[0_0_50px_rgba(6,182,212,0.25)] pointer-events-none" />7374 <div75 style={{76 transform: `rotateX(${rotation.x}deg) rotateY(${rotation.y}deg)`,77 transformStyle: 'preserve-3d',78 transition: isDragging ? 'none' : 'transform 0.05s linear'79 }}80 className="relative h-48 w-48 sm:h-60 sm:w-60 md:h-64 md:w-64 flex items-center justify-center"81 >82 {meridians.map((deg) => (83 <div84 key={`meridian-${deg}`}85 style={{86 transform: `rotateY(${deg}deg)`,87 transformStyle: 'preserve-3d'88 }}89 className="absolute inset-0 rounded-full border border-cyan-400/60 [backface-visibility:visible]"90 />91 ))}9293 {parallels.map((lat) => {94 const scale = Math.cos((lat * Math.PI) / 180);95 const translateY = -Math.sin((lat * Math.PI) / 180) * 110;96 return (97 <div98 key={`parallel-${lat}`}99 style={{100 transform: `translateY(${translateY}px) rotateX(90deg) scale(${scale})`,101 transformStyle: 'preserve-3d'102 }}103 className="absolute h-48 w-48 sm:h-60 sm:w-60 md:h-64 md:w-64 rounded-full border border-blue-400/50 [backface-visibility:visible]"104 />105 );106 })}107108 <div109 style={{ transform: 'translateZ(115px)' }}110 className="absolute h-3.5 w-3.5 rounded-full bg-cyan-400 shadow-[0_0_15px_#22d3ee] ring-4 ring-white/90 animate-ping"111 />112 <div113 style={{ transform: 'rotateY(110deg) translateZ(115px)' }}114 className="absolute h-3 w-3 rounded-full bg-indigo-400 shadow-[0_0_12px_#818cf8] ring-2 ring-white/70"115 />116 <div117 style={{ transform: 'rotateY(220deg) rotateX(30deg) translateZ(115px)' }}118 className="absolute h-3 w-3 rounded-full bg-emerald-400 shadow-[0_0_12px_#34d399] ring-2 ring-white/70"119 />120 <div121 style={{ transform: 'rotateY(300deg) rotateX(-20deg) translateZ(115px)' }}122 className="absolute h-2.5 w-2.5 rounded-full bg-amber-400 shadow-[0_0_12px_#fbbf24] ring-2 ring-white/70"123 />124 </div>125 </div>126127 <div className="mt-4 flex items-center gap-2 rounded-full bg-slate-900/90 border border-cyan-500/40 px-4 py-1.5 text-xs font-semibold text-cyan-300 backdrop-blur-md shadow-lg">128 <span className="h-2 w-2 rounded-full bg-cyan-400 animate-pulse" />129 <span>Interactive 3D Spatial Globe</span>130 </div>131 </div>132 );133}Installation & Integration Guide
Step 1: Tailwind CSS v4 Setup
Tailwind DocsIf you do not have Tailwind installed yet, run this installation command:
Terminal
npm install tailwindcss @tailwindcss/postcss postcss
Then add the Tailwind directive to your main stylesheet (globals.css):
@import "tailwindcss";
Step 2: Install Icon Dependencies
npm install lucide-react react-icons
Step 3: Add Component File
Create a new file in your project at src/components/effects/ThreeDGlobe.tsx and paste the copied code from the source viewer above.
Step 4: Usage Example
ExamplePage.tsx
1import 3DSpatialGlobe from '@/components/effects/ThreeDGlobe';23export default function ExamplePage() {4 return (5 <main className="p-8 flex items-center justify-center min-h-screen">6 <3DSpatialGlobe />7 </main>8 );9}