Command Palette

Search for a command to run...

Hectal
Course outline
Topic 19.3Advanced ~20 min · step 3 of 5

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.

0/5 · 0%

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.

Count the variantsdiagram
Rendering diagram…

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. 1

    Add a colour

    In the first example, change paint: 12 to paint: 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. 2

    Tighten the low-end budget

    Set the low-end phone's calls to 20. It prints OVER 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 configurator in numbers New tab

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: fits
Fingerprinted names for a year-long cache New tab

SHA-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.

terminal
$ Reload the showroom in a browser that visited before
── what you'll see ──
(the old bike, with no request in the Network panel: "(disk cache)")
Visitors keep the old model for up to a year; only clearing the cache helps.

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

01

How would you show a configuration you have no pre-rendered image for, without WebGL?

02

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. 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. 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. 3

    Delivery. Put a hash of the file's bytes in its name (lumen-bike-draco.f0fcaeb6.glb) and serve it with Cache-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 served no-cache, so it always checks for a newer list. Compression: Draco or Meshopt for geometry, KTX2 for textures (Topic 10.5).

  4. 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. 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

01

Why split a configurable product into part models instead of one model per variant?

02

How do you make sure customers get a new model version immediately, while models stay cached?

Practice

01

Estimate the first-view download for the showroom: three.js, the 6 default parts, the decoder and one environment map.

02

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