Course outline
Frontend System Design: 3D Data and Maps
In one line
Design a 3D view of a lot of data, such as 100,000 bike rentals on a city map: draw them in one call (points or instancing), pick with the GPU or a spatial index, label only what matters, stream tiles, and know when a 2D map is the better answer.
Think of it like this
A stadium crowd. You don't paint each fan separately; you print one picture of a fan and stamp it 80,000 times in different places. And you only write names under the few people the camera follows.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- instancing
- Drawing one geometry many times in one draw call, each copy with its own transform (and colour).
- GPU picking
- Finding what is under the pointer by rendering ids as colours and reading one pixel back.
- spatial index
- A structure (grid, quadtree, BVH) that finds items near a point without checking them all.
- tile
- One square piece of a large map or dataset at one zoom level, loaded only when visible.
Step by step
01Measure three ways to draw
Run the first example and compare draw calls, triangles and vertex data. The status bar shows the last case: the 1,000 separate meshes.
02Pick with colours
The second example builds two InstancedMeshes with the same boxes: one shown, one in id colours used only for picking. setViewOffset crops the camera to one pixel, so the pick renders 1 × 1 pixels instead of the whole screen.
03Encode ids exactly
Ids are written with setRGB(…, THREE.LinearSRGBColorSpace) so no colour conversion changes them, and the target is read with readRenderTargetPixels. Id 0 means "nothing".
04Decide 2D or 3D
Before building, write the question the view must answer. If height carries no meaning, sketch the 2D version first.
Try it yourself
- 1
Cheaper spheres
In the first example, change
SphereGeometry(0.2, 8, 6)toSphereGeometry(0.2, 6, 4). The instanced line drops totriangles 3600000: 36 per sphere instead of 80. Detail per item multiplies by the item count. - 2
Pick in the gaps
In the second example, add
[0.51, 0.5], [0.49, 0.5]to the list of points. Measured:GPU says 5152, raycaster says 5152for the first, andnothingfrom both for the second, which falls in a gap between boxes. Both methods agree when both sample the pixel's centre: the raycaster usesx + 0.5, becausexalone is the pixel's corner.
Code & diagrams
Seeded positions and a fixed 300 × 150 canvas, so the counts are the same on every run and screen.
Console
Run the scene to see its console.Expected console output
Points draw calls 1 | triangles 0 | points 100000 | vertex data 1.1 MiB
InstancedMesh of spheres draw calls 1 | triangles 8000000 | points 0 | vertex data 6.1 MiB
1,000 separate meshes draw calls 955 | triangles 76400The canvas is a fixed 300 × 150, so the picked ids are the same on every screen.
Console
Run the scene to see its console.Expected console output
at 50% across, 50% down: GPU says 5150, raycaster says 5150
at 30% across, 70% down: GPU says 7421, raycaster says 7421
at 65% across, 60% down: GPU says 6375, raycaster says 6375
at 2% across, 5% down: GPU says nothing, raycaster says nothingBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Let colour management change the ids
In the second example, change THREE.LinearSRGBColorSpace to THREE.SRGBColorSpace in the setColorAt line.
Myth vs fact
Myth
More dimensions always show more.
Fact
3D hides items behind others and distorts size with distance. For many questions about data on a map, a 2D view is clearer, faster and more accessible.
Interview problem
The problem
Ardenvale rentals in 3D
Lumen wants a live 3D map of Ardenvale showing every bike rental of the last 24 hours (about 100,000), with hover details and a time slider.
You're given
- 100,000 items, updating every minute
- Hover shows the rental's details
- Works on a mid phone
- An accessible alternative
The interviewer follows up
The time slider makes the frame rate drop. Why, and what do you change?
How do you hover-pick without reading pixels every frame?
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Picking renders can run only on pointer events, at 1 × 1 pixels: cheap enough for 60 Hz hover. Read back asynchronously where you can (WebGL 2 with a fence, or WebGPU's
mapAsync) so the read doesn't stall the frame. - ▸
With
InstancedMesh, update only the matrices that changed and setinstanceMatrix.needsUpdate = true, or useBatchedMeshwhen items have different geometries (Topic 17.2).
Remember this
- 1
The problem this solves: data scenes break the habits of product scenes. One mesh per item stops working at a few thousand items, labels drown each other, and picking by raycast slows down. This is the second system design round 3D frontend roles ask.
- 2
One draw call for many things. The first example draws 100,000 rentals three ways.
Points: 1 draw call, 100,000 points, 1.1 MiB of vertex data. AnInstancedMeshof small spheres: 1 draw call but 8,000,000 triangles (80 per sphere) and 6.1 MiB, mostly one 4 × 4 matrix per instance. And 1,000 separate meshes, only a hundredth of the data: 955 draw calls (the other 45 were outside the view and culled). Choose points for dots, instancing when each item needs a shape. - 3
Picking. A raycast against 100,000 instances tests each one on the CPU. GPU picking draws every item in a unique "id colour" into a 1 × 1 render target under the pointer (
camera.setViewOffset) and reads that one pixel: 3 bytes give up to 16.7 million ids. The second example picks among 10,000 boxes both ways and gets the same ids. Alternatives: a spatial index (a grid, a quadtree, orthree-mesh-bvh) to test only nearby items. - 4
Labels and tiles. Show labels only for the nearest or most important few (HTML or CSS2D, Topic 8.3), and fade the rest. Large maps stream tiles: square pieces of the map at several zoom levels, loaded as the camera moves and dropped when off-screen (the idea behind map libraries and 3D Tiles).
- 5
When 2D is better. 3D adds occlusion (items hide others), perspective (sizes lie about distance) and harder interaction. If the question is "where are rentals densest?", a 2D heat map or a choropleth answers it faster and more accessibly (the
/data-vizcourse). Use 3D when height or the third dimension carries meaning.
Explain it without notes
How would you draw and pick 100,000 items in a browser?
When would you advise against a 3D data view?
Practice
Colour each rental by its hour of the day in the instanced version, without adding draw calls.
Hide every rental outside a time range chosen on a slider, without rebuilding any buffer.
Done when you can
You can draw 100,000 items in one draw call and say the cost
You can explain and build GPU picking
You know when a 2D view is the better design
You can design labels and tiles for a large scene