Three.js Maps: 3D Terrain and Map Visualization
A 3D map or terrain in Three.js is typically built from a heightmap (a grayscale image where pixel brightness encodes elevation) used to displace the vertices of a plane geometry, either on the CPU (reading pixel data once and setting vertex positions directly) or on the GPU (sampling the heightmap texture in a vertex shader), with the GPU approach scaling to much larger terrains.
Heightmap-driven terrain
The standard technique: start with a PlaneGeometry with enough subdivisions to have meaningful vertex density, then displace each vertex's Y (or Z, depending on orientation) by sampling a heightmap image at that vertex's UV coordinate. Done on the CPU, you read the heightmap's pixel data once (via a canvas getImageData call) and directly set each vertex position. Simple, but limited by how many vertices you can afford to have in the geometry.
Done on the GPU, sampling the heightmap texture directly in a vertex shader (positionNode in TSL, displacing along the normal by a sampled height value), the displacement happens per-vertex on the GPU at render time. This scales to much higher subdivision counts than a CPU-computed mesh, and enables live-adjustable terrain (change a uniform, the whole terrain updates) without regenerating geometry.
Tiling for large terrain
A single large heightmap and mesh works fine for a bounded scene, but genuinely large terrain (an open-world-scale map) is usually split into tiles: separate mesh chunks, each with its own heightmap region, loaded and unloaded based on camera distance. This is the same level-of-detail thinking as instanced grass: don't pay the cost of detail the camera can't currently see or resolve. Combined with LOD per tile (fewer subdivisions for distant tiles), this is how large real-time terrain systems stay performant.
Layering real geographic/map data
For visualizing actual geographic data rather than a fictional landscape, real elevation datasets (SRTM, USGS) can be converted to heightmap images with GIS tools, and real map imagery (satellite photos, street map tiles) can be applied as the terrain's texture, using the same texture loading pipeline as any other material. For a whole-Earth scale visualization rather than a local terrain patch, see the globe guide instead, which uses spherical rather than planar geometry.
See it in production
Browse the full showcase →Code
Learn this properly
TSL for Beginners: Build a Living Planet
Noise: Continents Appear
This lesson builds procedural terrain with TSL noise, directly relevant to heightmap-driven terrain.
Frequently asked questions
How do I create terrain in Three.js?
Displace a PlaneGeometry's vertices using a heightmap image: either read once on the CPU and applied directly to vertex positions, or sampled per-vertex in a shader on the GPU for higher detail and better performance at scale.
How do I make large terrain performant in Three.js?
Split it into tiles loaded/unloaded based on camera distance, combined with per-tile level of detail (fewer subdivisions for distant tiles), the same principle as any large-scene culling strategy, applied to terrain chunks.
Can I use real elevation data for Three.js terrain?
Yes. Real elevation datasets (SRTM, USGS) can be converted to heightmap images using GIS tools, then used the same way as any other heightmap to displace terrain geometry.
Keep reading
Globe
A Three.js globe starts as a textured sphere (a SphereGeometry with an equirectangular Earth texture map), with data poi…
Textures
Three.js loads textures with THREE.TextureLoader for standard image formats, and for production use should compress them…
Grass
Three.js grass is built with InstancedMesh: one blade geometry (usually a couple of triangles or a simple bent-plane sha…
All Three.js Guides
Back to the guides hub.