Build a live interactive 3D globe with animated flight arcs, cursor-drag rotation, and WebGL particle trails — all under 200 lines of JavaScript. Complete tutorial with working demo.
Three.js is a JavaScript library that wraps WebGL — the browser's low-level GPU rendering API — into a developer-friendly abstraction layer. Without Three.js, rendering a single 3D sphere would require hundreds of lines of raw GLSL shader code and manual buffer management. Three.js reduces this to about five lines. For globe visualisations specifically, it gives you a SphereGeometry primitive, a texture loader, a camera, lighting, and a render loop — the complete scaffolding needed for a fully interactive 3D Earth.
The alternative approaches — CSS 3D transforms, SVG projections, or canvas-based 2D maps — all break down the moment you need real perspective depth, proper occlusion of continents on the far side of the globe, or smooth 60fps cursor drag rotation. WebGL via Three.js handles all of these natively because the calculations run on the GPU. For aviation dashboards, flight tracking tools, and data-driven globe maps, Three.js is the industry standard precisely because it balances render quality with development velocity. You can read more in our OpenClaw AI real-world use cases guide where interactive geospatial dashboards built on WebGL are a primary deployment pattern.
Every Three.js application begins with three objects: a WebGLRenderer, a PerspectiveCamera, and a Scene. The renderer creates a <canvas> element and communicates with the GPU. The camera defines the viewpoint — for a globe you want a perspective camera with a field of view between 45° and 60° so the sphere feels three-dimensional rather than flat. The scene is the container that holds every object, light, and helper in your 3D world.
Lighting for a globe typically uses two lights: a dim AmbientLight to prevent the night side from going completely black, and a DirectionalLight positioned to simulate the Sun. Setting the directional light at position(5, 3, 5) gives a convincing north-hemisphere illumination. This is the foundational setup that every globe visualisation — from flight trackers to climate data maps — builds on. The renderer.setPixelRatio(window.devicePixelRatio) call is critical for sharp rendering on retina displays; skipping it makes text and coastlines look blurry on high-DPI screens.
// Minimal Three.js scene setup
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true, alpha: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setSize(canvas.clientWidth, canvas.clientHeight);
const camera = new THREE.PerspectiveCamera(50, aspect, 0.1, 1000);
camera.position.z = 2.8;
const scene = new THREE.Scene();
scene.add(new THREE.AmbientLight(0xffffff, 0.4));
const sun = new THREE.DirectionalLight(0xffffff, 1.2);
sun.position.set(5, 3, 5);
scene.add(sun);The globe itself is a SphereGeometry(1, 64, 64) — radius 1 unit, 64 width segments, 64 height segments. Higher segment counts produce smoother silhouettes but increase vertex count. For a typical globe at standard browser window sizes, 64×64 is the sweet spot. The material is a MeshPhongMaterial with your Earth texture map applied — this material responds to the directional light and creates the shaded day/night appearance. For a lightweight build without loading external texture files, you can generate a procedural dotted-grid globe using a canvas texture, which is what the demo on this page uses.
A second, slightly larger transparent sphere with a MeshPhongMaterial set to transparent: true and low opacity creates the atmospheric glow effect — the soft blue halo visible on real globe visualisations. This atmosphere sphere is scaled to 1.02 and uses a blue tint with additive blending. The combination of the textured sphere, the atmosphere sphere, and the directional light gives you the core visual of any professional globe without requiring external assets beyond a single JPEG texture.
Cursor interaction is what transforms a static globe render into an engaging interactive experience. The standard implementation tracks three things: whether the mouse button is pressed (isPointerDown), the previous mouse position (prevX, prevY), and a rotation velocity (rotVelX, rotVelY) for momentum. On pointermove, the delta between the current and previous mouse position maps directly to rotation angles applied to the globe mesh's rotation.x and rotation.y properties. The delta is multiplied by a sensitivity constant (typically 0.005) to make the rotation feel natural.
The momentum effect — where the globe continues spinning after you release the mouse — is implemented with a damping multiplier. Every animation frame, if isPointerDown is false, the velocity values are multiplied by 0.94 (the damping coefficient) and then added to the globe's rotation. This exponential decay naturally brings the rotation to rest, mimicking real physical inertia.
Performance tip: Throttle pointermove events with requestAnimationFrame — do not apply rotation inside the event handler directly. Instead, store the delta and apply it in the render loop. This prevents rotation calculation from blocking the GPU render cycle.
Latitude and longitude are spherical coordinates — they describe positions on a sphere in degrees. Three.js works in Cartesian (x,y,z) coordinates. Converting between the two requires a simple trigonometric formula that converts degrees to radians, then computes the 3D position on the surface of a unit sphere. The y axis in Three.js points up, which corresponds to the north pole. The formula is: x = R * cos(lat) * sin(lon), y = R * sin(lat), z = R * cos(lat) * cos(lon) — where R is the sphere radius and angles are in radians.
For flight path visualisations, this conversion is applied to both the departure and arrival airports. The two resulting 3D points become the start and end of each flight arc. A key detail: longitude in geographic systems runs from -180° to +180° with 0° at Greenwich, but the Three.js sphere's default orientation places 0° longitude at a different position. A rotation offset of the globe mesh (typically Math.PI / 2 around Y) is needed to align the texture's prime meridian with the geographic convention.
| City | Latitude | Longitude | 3D X | 3D Y | 3D Z |
|---|---|---|---|---|---|
| Dubai (DXB) | 25.25°N | 55.36°E | 0.517 | 0.424 | 0.743 |
| New York (JFK) | 40.64°N | -73.78°W | 0.213 | 0.651 | -0.728 |
| London (LHR) | 51.48°N | -0.45°W | -0.006 | 0.782 | 0.624 |
| Singapore (SIN) | 1.36°N | 103.99°E | -0.239 | 0.024 | -0.971 |
| Tokyo (NRT) | 35.77°N | 140.39°E | -0.592 | 0.583 | -0.557 |
| Sydney (SYD) | -33.95°S | 151.18°E | -0.544 | -0.559 | -0.625 |
A flat straight line between two points on a sphere looks wrong — real flight paths follow great circle routes that arc upward away from the globe surface. In Three.js, this is achieved with THREE.CatmullRomCurve3. You generate intermediate points along the arc by interpolating between the start and end 3D positions, and at each step, pushing the midpoint outward away from the sphere centre by a height factor (typically 0.3 to 0.6 radius units). This creates the elegant arcing flight path geometry you see on aviation dashboards.
The animation of the arc — where a glowing dot travels along the path — is implemented using the curve's getPoint(t) method where t runs from 0 to 1 over time. Each frame, a small sphere (the "aircraft" indicator) is repositioned to curve.getPoint(t % 1). Multiple flight arcs each with their own time offset produces the layered animated globe seen in professional data visualisations.
Particle trails give flight path animations a cinematic quality that differentiates them from basic line animations. The implementation uses Three.js Points geometry — a collection of vertices that render as individual sized dots rather than connected lines. Each particle is assigned a position along the flight arc, an initial opacity, and a speed. Every frame, particles move forward along the arc, their opacity decreasing from 1.0 to 0.0 as they age, then reset to the start of the arc when they reach the end. With 20–50 particles per arc, each with a slight random offset in spawn timing, you get a convincing trailing glow effect.
The shader for the particles uses a PointsMaterial with blending: THREE.AdditiveBlending — this causes overlapping particles to add their colours together rather than overwrite each other, producing bright bloom-like hotspots at high particle density points. Combining additive blending with a radial gradient circle texture makes each particle look like a glowing orb rather than a hard dot.
A Three.js globe can easily drop below 30fps on mid-range mobile devices if not carefully optimised. The primary optimisation levers are: geometry complexity (sphere segments), the number of active flight arcs, texture resolution, and shadow map usage. For mobile, reduce sphere segments from 64 to 32, limit concurrent animated arcs to 4–6, use a 1024×512 texture instead of 2048×1024, and disable shadow maps entirely — shadows on a globe visualisation add minimal visual value but double GPU load.
The renderer.setPixelRatio(Math.min(devicePixelRatio, 2)) call is the single most impactful mobile optimisation — it caps the pixel ratio at 2× even on 3× or 4× screens, halving the number of pixels rendered on high-DPI phones while remaining visually crisp. Combining this with renderer.powerPreference: "high-performance" in the renderer constructor tells the browser to use the discrete GPU on devices with dual-GPU setups.
Complete code summary: The full working globe — renderer setup, Earth sphere, atmosphere, lat/lon conversion, CatmullRom arc generation, animated aircraft dots, particle trails, drag rotation with momentum, and scroll zoom — fits in approximately 180 lines of JavaScript. No build system, no bundler, no dependencies beyond Three.js r128 from CDN.
No — Three.js is released under the MIT licence, which allows unlimited commercial use at no cost. You can use it in SaaS products, client projects, and any revenue-generating application without royalty payments or attribution requirements (though credit is appreciated). The MIT licence is one of the most permissive open-source licences available.
Any device with WebGL 1.0 support — which covers virtually every browser and device released after 2013 — can run a Three.js globe. Even integrated Intel graphics handle it smoothly at medium quality settings. The main performance constraint is not raw GPU power but memory bandwidth and the JavaScript engine's overhead. Devices from 2018 onwards typically achieve 60fps with a full-featured globe including 6 animated flight arcs and particle effects.
Use a CDN with a specific pinned version (e.g. cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js) which is typically served from the user's nearest edge node and cached aggressively. Three.js r128 minified is around 580KB but compresses to approximately 145KB over the wire with gzip. For production, use a module bundler like Vite with tree-shaking to import only the Three.js modules you actually use, which can reduce bundle size to under 200KB uncompressed for a simple globe scene.
Yes. The OpenSky Network provides a free REST API returning live aircraft positions globally. AviationStack and FlightAware offer more detailed data with paid tiers. Poll the API every 5–10 seconds, convert the returned lat/lon/altitude to 3D positions using the coordinate conversion formula described in section 5, and update your particle positions accordingly. For WebSocket-based real-time updates without polling, ADS-B Exchange provides a free stream.
iOS Safari has supported WebGL 1.0 since iOS 8 and WebGL 2.0 since iOS 15. The main iOS-specific issues are: memory limits (keep total texture memory under 256MB), the lack of floating-point texture support in some configurations (use half-float as fallback), and the requirement for a user gesture before audio context creation (relevant only if your globe has sound effects). For the visual globe described here, standard Three.js code works on iOS 13+ without modification.
Three.js's built-in OrbitControls provides orbit, pan, and zoom in one component. For a globe specifically, OrbitControls orbits the camera around a target point rather than rotating the globe object itself. Custom drag rotation — rotating the globe mesh directly — gives you more precise control over the feel, allows you to add momentum/inertia easily, and avoids gimbal lock issues at the poles that OrbitControls can exhibit.
Country borders use GeoJSON data (Natural Earth provides free high-quality files) converted to 3D line segments using the lat/lon-to-3D conversion. The d3-geo library's path generator combined with Three.js LineSegments is the standard approach. City labels require CSS2DRenderer or CSS3DRenderer which overlay HTML elements on top of the WebGL canvas and project their positions using the camera's view matrix.
For React applications, React Three Fiber (R3F) is now the standard — it provides a declarative component model for Three.js scenes that integrates naturally with React state management. For vanilla JS or non-React frameworks, Three.js directly is still the correct choice. Babylon.js is a full game engine more appropriate when you need physics simulation or skeletal animation. For globe visualisations specifically, the Three.js ecosystem has the most community examples, tutorials, and library support in 2026.