Course outline
Frontend System Design: A Product Configurator
In one line
Design a 3D product configurator for thousands of variants: split the product into parts instead of one model per variant, deliver them from a CDN with fingerprinted names, budget per device class, load progressively with fallbacks, and keep it accessible and measurable.
Think of it like this
A sandwich shop doesn't keep a ready-made sandwich for every possible order. It keeps the breads, fillings and sauces, and assembles yours in front of you. A configurator does the same with 3D parts.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- SKU
- Stock keeping unit: one exact sellable variant (this frame, this paint, these wheels…).
- content hash
- A short fingerprint of a file's bytes; a changed file gets a new name, so caches never serve an old one.
- immutable
- A cache header that tells browsers the file at this URL will never change, so they don't ask again.
- device class
- A group of devices with similar power (desktop, mid phone, low-end phone) that share one performance budget.
Step by step
01Count the variants
Multiply the options: 4 × 12 × 3 × 3 × 4 × 2 × 2 = 6,912. Then count part files: 4 + 3 + 3 + 4 + 2 + 2 = 18. The difference is the whole design.
02Fingerprint the files
The second example hashes the real model files with SHA-256 and builds the names and cache headers. Vite does this for files you import; files in public/ keep their names, so a CDN setup for public/data/3d/ needs its own step (Topic 18.6).
03Check the budget
The first example prints the budget table and checks the showroom against it. Change a number and see which class goes over.
04Say the trade-offs
Parts mean more requests (HTTP/2 multiplexes them) and seams where parts meet; one model per variant means huge storage and slow builds. Name both before choosing.
Try it yourself
- 1
Add a colour
In the first example, change
paint: 12topaint: 24. SKUs double to 13,824; part files stay at 18, because paint is a parameter. That is the argument for parts in one line. - 2
Tighten the low-end budget
Set the low-end phone's
callsto 20. It printsOVER on calls: 25 draw calls is more than that. Instancing the spokes and merging static parts (Topic 17.2) is the next step.
Code & diagrams
The budgets are a design choice, not a standard: Phase 17 explains where such limits come from. The part size is an estimate (the 33 KiB bike divided by its 11 parts).
Console
(nothing was printed)Expected console output
possible bikes (SKUs): 6,912
one model per SKU: 6,912 files, 0.2 GiB to build and store
one model per part: 18 files, 0.05 MiB; paint is a material, not a file
a customer's first view: 6 part files, about 18 KiB
desktop budget 200 calls, 1,000,000 triangles, 256 MiB, ratio 2: fits
mid phone budget 100 calls, 300,000 triangles, 64 MiB, ratio 2: fits
low-end phone budget 50 calls, 100,000 triangles, 32 MiB, ratio 1.5: fitsSHA-256 of the real files on this site, via `crypto.subtle`.
Console
(nothing was printed)Expected console output
lumen-bike-draco.f0fcaeb6.glb 33752 bytes -> Cache-Control: public, max-age=31536000, immutable
lumen-bike-meshopt.b28f7baf.glb 47604 bytes -> Cache-Control: public, max-age=31536000, immutable
the page that names them (index.html) -> Cache-Control: no-cache (always asks: is there a newer list of file names?)Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Cache a file that keeps its name
Serve lumen-bike-draco.glb (no hash) with max-age=31536000, immutable, then ship a fixed model under the same name.
Myth vs fact
Myth
A configurator needs one 3D model per product variant.
Fact
Variants multiply; parts add. 6,912 bikes come from 18 part files and a paint parameter.
Interview problem
The problem
Lumen configurator
Design the 3D configurator for Lumen Bikes: customers choose frame, paint, wheels, handlebar, saddle, lights and mudguards, see the bike in 3D, and see the price. It must work on cheap phones and without WebGL.
You're given
- About 7,000 variants, growing
- Low-end phones: first 3D view in under 3 s on 4G
- Screen-reader and keyboard users can configure and buy
- No WebGL: still images and the same price
The interviewer follows up
How would you show a configuration you have no pre-rendered image for, without WebGL?
Two parts from different vendors don't line up. Whose problem is it?
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Keep the configuration in the URL (
?frame=city&paint=blue): shareable, bookmarkable, and the same state can render the HTML summary server-side for search engines and social previews. - ▸
Track loading and frame-time numbers per device class in your analytics (with consent): a budget you don't measure in the field is a guess.
Remember this
- 1
The problem this solves: "Design a configurator" is a common senior frontend round at companies that sell products online. It tests whether you can turn a demo (Lumen's showroom) into a system: data, delivery, performance, failure and accessibility, with numbers.
- 2
Parts, not variants. Lumen's options (4 frames, 12 paints, 3 wheels, 3 handlebars, 4 saddles, 2 lights, 2 mudguards) make 6,912 possible bikes. One model per bike would be 6,912 files, about 0.2 GiB to build and store; one model per part option is 18 files. Paint is a material parameter, not a file. The first view downloads only the 6 default parts.
- 3
Delivery. Put a hash of the file's bytes in its name (
lumen-bike-draco.f0fcaeb6.glb) and serve it withCache-Control: public, max-age=31536000, immutable: browsers and the CDN keep it for a year, and a new version gets a new name. The page that lists the names is servedno-cache, so it always checks for a newer list. Compression: Draco or Meshopt for geometry, KTX2 for textures (Topic 10.5). - 4
A budget per device class. Decide limits before building: draw calls, triangles, texture memory, pixel ratio, per class (desktop, mid phone, low-end phone). The first example checks the showroom's measured numbers (25 draw calls, 8,168 triangles, 5.3 MiB of texture) against such a budget: it fits all three classes with room for more parts. Enforce it in code (
src/systems/budget.js, Phase 17) and in CI (the capstone's test). - 5
Failure and access. Progressive loading (the stand first, then parts, Topic 10.4), a still image when WebGL is missing (Topic 1.5), recovery after context loss (Topic 18.2), a parts list and price as real HTML for screen readers and keyboards (Topics 8.4 and 18.3), and the price always computed and shown in HTML, never only inside the canvas.
Explain it without notes
Why split a configurable product into part models instead of one model per variant?
How do you make sure customers get a new model version immediately, while models stay cached?
Practice
Estimate the first-view download for the showroom: three.js, the 6 default parts, the decoder and one environment map.
A customer shares a link to their bike. What must the URL hold, and what happens when a part in it no longer exists?
Done when you can
You can argue parts against variants with numbers
You can design cache headers for 3D assets
You can set a budget per device class
You can list fallbacks and accessibility for a configurator