Engineering wiki
A three-star kitchen, built like a factory twin
This site is a digital twin of The French Laundry's kitchen in Yountville, California, running a full dinner service. It is modelled the way a manufacturing plant is: covers booked are the build plan, every recipe is a bill of materials with a routing, stations are work centers, and a discrete-event simulation drives the brigade, the machines and the stock you see in 3D. Everything runs in your browser from a static GitHub Pages site.
The numbers below are read live from the same /data files the twin runs on.
Architecture
The system is a pipeline with one source of truth. Sourced JSON in /data is turned into a production plan and into the simulation's input; the simulation owns the clock; the 3D scene, the dashboard and Studio only read its state and send it commands.
index.html wires all of them; studio.html adds the Studio layer; dashboard.html mounts the dashboard on its own.The frame loop
The simulation owns time. Each animation frame advances it by the wall-clock delta times the chosen speed, then the scene and the dashboard mirror the new state. Because every view reads the same state, the 3D cooks, the Gantt chart and the HUD never disagree.
src/main.js. Cooks and plates are tweened between syncs, so motion stays smooth at 60 fps while the scene only re-reads the simulation six times a second.Stack
The twin is deliberately small: one runtime dependency, no framework, no backend and no binary assets.
| Layer | Choice | Why |
|---|---|---|
| Language | Modern JavaScript (ES2022 modules), CSS | The same simulation and planning code runs in the browser and in Node without a compile step. |
| 3D rendering | Three.js 0.186 (WebGL 2), with its post-processing addons: GTAO, Unreal bloom, bokeh depth of field, FXAA, output pass | Physically based materials, real-time shadows and a mature post-processing chain, in the browser. |
| UI | Plain DOM and Canvas 2D charts (src/dashboard/charts.js) | No framework to download; charts draw thousands of Gantt bars cheaply on a canvas. |
| Simulation | Hand-written discrete-event engine with a binary-heap event queue and a seeded PRNG (mulberry32) | Deterministic, dependency-free and fast enough to run a whole night in about a tenth of a second. |
| Build | Vite 8, multi-page build (four HTML entries) | Bundles JSON imports, splits shared chunks, and serves the dev site with hot reload. |
| Tests | Node 22's built-in test runner (node --test) | No test framework needed; the engine and planner are pure modules. |
| CI and hosting | GitHub Actions; GitHub Pages via actions/deploy-pages | Every pull request is validated, tested and built; every merge to main publishes. |
| Interchange | OpenUSD (.usda) written in the browser | Opens in usdview, NVIDIA Omniverse and Isaac Sim with prims named by the data ids. |
Repository layout
Each top-level folder under src/ was owned by one workstream and talks to the others only through the data ids and small APIs.
The research story
Nobody handed over blueprints. The French Laundry does not publish its floor plan, its brigade or its prep lists. Everything in this twin was pieced together from what the restaurant, its architects, the trade press, its cookbook and its diners have put in public, and each fact is tagged with where it came from.
Why this kitchen
The brief was a three-star kitchen, as realistic as possible. That made the choice a research question: which three-star kitchen has the most published detail? The French Laundry won clearly. Its 2014 to 2017 rebuild was covered by the architects (Snøhetta), by Architectural Record, and by Foodservice Equipment & Supplies, whose article lists the full equipment schedule down to the make of the slicer. Thomas Keller's 1999 cookbook gives exact quantities for the signature dishes. And diners have written course-by-course accounts of the tasting menu, with timings.
Five kinds of source, five parts of the twin
| Source | What it gave the twin | Where it lives |
|---|---|---|
| Architecture press Architectural Record, Snøhetta | The building: the vaulted ventilated ceiling, ribbon window, terrazzo floor, white surfaces, the annex's butchery and produce rooms | The plant, layout and 3D scene |
| Trade press FE&S "Clean Slate" | Kitchen and annex areas, the two Bonnet island suites, every named appliance, and the decision to have no walk-in coolers | Equipment, storage, work centers |
| The cookbook, via reproductions The French Laundry Cookbook (1999) | Exact quantities, temperatures and times for four dishes | Bills of materials and routings |
| Diner reviews The Chihuo, Tiny Urban Kitchen | The course sequence, supplements, how long the meal lasts, and a headcount of the kitchen at service | Menu, pacing, brigade size |
| Journalism and reference Press Democrat, AP, Wikipedia, Tasting Table | Seats, stars, price, the garden's share of the produce, the cellar | Restaurant facts, demand |
Things we learned along the way
From an article to a moving kitchen
- Read and extract. Each source was read for facts the model could use: a dimension, an appliance, a quantity, a time. Each fact became a field in a JSON file with a
srclist naming where it came from. - Translate into factory terms. Recipes became bills of materials, method steps became routings with a station, appliance, cook and time, and the brigade became a labor pool with skills. See Data and sources.
- Fill the gaps honestly. Where nothing was published (portions for dishes without a recipe, most operation times, positions on the floor, ingredient prices), a typical professional value went in, tagged
est. - Check it. A validator checks every cross-reference, and a test checks that planning four servings of Oysters and Pearls reproduces the cookbook's quantities.
- Run it. The planner explodes covers into grams and minutes, the simulation plays the night, and the 3D scene draws what the simulation is doing.
The research, the data model and the planner were built by one Claude session in the first hours of the project, and handed to the 3D, simulation and Studio sessions as a shared contract of ids. See How it was built.
Sourced versus estimated
Counted live from /data. A record is "sourced" when it cites a publication, "estimated" when its only tag is est; many cite a publication for some fields and estimate others.
The main estimates are the brigade size and shifts, where equipment stands, portions for courses with no published recipe, and ingredient prices. The full list is in Limits and estimates, and the bibliography with how often each source is cited is under Data and sources.
Data and sources
The French Laundry was chosen because it is the three-star kitchen with the most published detail: a 2017 rebuild documented by its architects and by the trade press, a published equipment schedule, a cookbook, and course-by-course reviews. Every record in /data has a src list that points into sources.json, or est where it is a modelling estimate.
Kitchen concepts as manufacturing master data
| Kitchen | Manufacturing | Where |
|---|---|---|
| The restaurant and its kitchen | Plant | restaurant.json |
| Tasting menu, choices, supplements | Product catalogue, configure-to-order options | menu.json |
| Covers booked tonight | Master production schedule | service.json |
| Ingredients | Purchased raw materials (item master) | items.json RM-* |
| Mise en place | Sub-assemblies, work in process | products.json INT-* |
| Plated courses | Finished goods | products.json FG-* |
| Recipe quantities | Bill of materials | bom in each product |
| Method, station, appliance, timings | Routing with setup and run times | routing in each product |
| Stations | Work centers | resources.json WC-* |
| Ranges, combis, hearth, PacoJet | Machines with parallel slots | resources.json EQ-* |
| Reach-ins, humidor, aging cooler | Storage locations | resources.json SL-* |
| The brigade | Labor pool, skills matrix, shifts | resources.json roles, staff |
| Floor plan | Plant layout | layout.json |
The ids are the contract between every part of the system. The simulation keys its machines by EQ-*, the scene places a model at each EQ-* position, the dashboard names constraints by role and machine, and the USD export writes a prim per id. npm run validate checks every cross-reference before CI will build.
Bibliography
Rendered from data/sources.json, with how many ingredient, recipe, equipment and station records cite each one.
Four recipes reproduce Thomas Keller's published quantities exactly (Oysters and Pearls, the salmon cornet batter, Coffee and Doughnuts, butter-poached lobster), and a test checks the planner reproduces the Oysters and Pearls numbers. The rest of the menu uses typical fine-dining portions, marked est. See Limits and estimates.
Production planning (MRP)
src/model/mrp.js runs a classic material requirements planning explosion, the same code in the browser drawer and in npm run plan.
demandForCovers() → explode() → loadSummary(), plus leadTimeMinutes() for items that take days (corned tongue about 76 hours, foie gras torchon about 49).- Option shares (beef or veal tongue) and supplement uptake (caviar, foie gras) are applied as expected values and rounded up.
- Sub-assemblies are made in whole batches, so 31 lobsters' worth of demand blanches two batches of 20.
- Raw materials are grossed up by
yieldPct: 900 g of shelled corn needs 2 kg on the cob. - Each routing step becomes labor minutes for a role, scaled by
laborFractionbecause a cook is not hands-on while the combi runs, and machine minutes for a piece of equipment.
At 62 covers every station has headroom; at 120 the fish commis needs more prep time than the shift allows. Those are the constraints the simulation then shows live.
Simulation engine
src/sim/engine.js is a discrete-event simulation of the service as a job shop. Time jumps from event to event on a binary heap; between events nothing is computed, which is why a full night of prep and service simulates in about 0.1 seconds.
What flows through it
- Reservations [demand]: covers split into parties of one to six, arriving on the chosen seating curve, with no-shows and a finite set of tables. A party that waits longer than its patience walks away.
- Prep [pre-production]: the menu is exploded into prep batches with a safety factor. Prep tasks consume raw stock and produce components that service tasks then consume. Items whose lead time cannot fit the day (veal stock, corned tongue, foie torchon) are carry-over stock from a previous day.
- Tickets [work orders]: one per table per course. Expo fires each ticket early by the planned lead time of its slowest dish, and staggers the other dishes so they all finish together (just-in-time offset scheduling).
- Tasks [routed operations]: each dish is a chain of steps with a station, an appliance type, a role, a duration and a hands-on time, so a combi stays busy after the cook walks away.
- Quality gate [final inspection]: QC rejects and plates held too long at the pass are re-fired; waste and food cost are tracked.
- Guests [a second production line]: parties are seated, eat each course at their own pace, and leave; every stage has a queue and a KPI.
Dispatching
Whenever something changes, ready tasks are sorted by class (re-fires first, then service, then prep, then pot wash) and by due date, with prep ordered by latest start. Each task then needs, in order: its inputs in stock (or still being prepped, in which case it waits), a free position at its station, a free slot on an appliance of the right type (the least-loaded unit wins), and a cook with that role or skill. Whatever blocks a task is logged, and the blocker with the most waiting in the last half hour is named on the HUD as the constraint, in theory-of-constraints terms.
Randomness and determinism
Operation times are means with lognormal spread (more for hands-on work, less for machine cycles), and appliances break down on an MTBF/MTTR model. All randomness comes from three seeded mulberry32 streams, so the same covers, seating and seed replay the same night to the second. Seeking backwards rebuilds the engine and replays from the seed; that is what makes share links exact.
KPIs
Ticket time p50, p90 and p95 per course; guest wait and course gap against target; throughput and work in process; utilization of cooks, stations and appliances on a 15-minute rolling window; OEE (availability × performance × quality) per appliance; first-pass yield; stockouts, substitutions and comps; food cost, waste and energy; and the bottleneck read-out.
API
import { createSim } from './src/sim/sim.js';
const sim = createSim({ data, covers: 62, seed: 1, seating: 'tfl-staggered' });
sim.play(); sim.setSpeed(60);
twin.onFrame((dt) => sim.tick(dt)); // the sim owns the clock
sim.seek(t); sim.stateAt(t); // backwards seeks replay from the seed
sim.reset({ covers: 120, staffOut: [...] }); // re-plan the night
sim.on('ticket.fired' | 'op.start' | 'op.end' | 'staff.move' | 'machine.state'
| 'inventory.change' | 'plate.passed' | 'plate.refire', cb);
sim.events; sim.taskLog; sim.forecast; // event log, Gantt intervals, full-run result
The same modules run headless in Node: npm run sim:report -- 124 prints the KPIs for 124 covers.
Scenarios and optimizer
Every what-if is a config patch applied to the same seed, so a comparison isolates the change. src/sim/scenarios.js runs a whole service headlessly with runHeadless(), compares two runs on every KPI with compare(), sweeps covers from 20 to 200 to find rated capacity, and writes the shift report as CSV.
| What-if | Patch |
|---|---|
| Full house | Double the covers |
| Short-staffed | One cook removed from the schedule (staffOut) |
| Combi ovens down | Forced breakdown of both combis at 20:15 for 30 minutes (faults) |
| Supply shock | Lobster delivered 40% short (supply) |
| Rush seating | Everyone arrives within 30 minutes |
The capacity sweep marks the knee as the first cover count where guest wait p90 passes 10 minutes, and names the resource that blocked work the most at that load.
The optimizer
A tasting menu has a target length (195 minutes here). When guests sit longer than that, the overrun is the twin's equivalent of missed takt time. src/sim/optimize.js searches two levers that shorten it:
- An extra cook, cloned from one of the brigade with the same roles, station and shift (one candidate per line role). In the engine this is
cfg.extraStaff: [{ like: staffId }]. - A re-sequenced fire: expo calls each course earlier, by a larger share of its slowest dish's lead time (
fireLead, 1× to 1.5×, today 0.9×) plus a longer fixed buffer (leadSec, 75 to 225 s), so plates reach the pass as guests are ready.
Every candidate re-runs the whole night on five seeds and is ranked on the average, so one lucky night cannot win. The two best cooks and the two best fire settings are then combined, since the levers are independent and often stack. Guards flag any plan that wins on meal time by hurting something else: first-pass yield falling more than a point, ticket p90 or table-wait p90 rising more than two minutes, or more guests walking away. The recommendation is the least invasive plan that passes the guards and meets the target: re-sequencing before extra labor, smaller changes first.
Precomputed scenarios
Optimizing one night is a few hundred full-service runs, so the site ships the answers. npm run precompute (scripts/precompute.mjs) optimizes 40, 62, 90 and 120 guests and writes src/sim/precomputed.data.js: each night's KPIs, every candidate ranked, and the recommended plan. src/sim/precomputed.js keys it by a hash of the simulation input, so if /data or the engine changes the site falls back to live runs, and tests/optimizer.test.mjs fails until the file is regenerated. The same file holds each precomputed night's end time, so createSim({ cache }) skips the forecast run when one of them is loaded.
The table below is read from the precomputed file shipped with this site (average of five seeds).
Where you see it
The Service scenarios and optimizer panel (src/dashboard/optimizer.js) sits in the dashboard, on dashboard.html and in the Operations drawer of the 3D twin. The Scenarios button in the dock opens the drawer at that panel. Pick a guest count to see its ranked plans and load one onto the twin; a night that is not precomputed can be optimized live, on fewer seeds. A loaded plan is part of the share link (cook, fire, lead).
3D scene
src/scene/ builds a photoreal model of the 2017 kitchen (the curved skylit vault, terrazzo floor, the fritted ribbon window onto the garden), the support annex, the culinary garden and a glimpse of the dining room. There are no downloaded models or textures: geometry is generated from /data at load, and every material's texture maps are painted on canvases at runtime from a seeded generator, so they look identical on every load.
| File | Builds |
|---|---|
index.js | Renderer, camera, controls, lighting rig, post-processing, the public API and the render loop |
plan.js | Turns layout.json and resources.json into positions for every station, machine, cabinet and cook spot |
room.js | The building, vault, annex, dining glimpse, courtyard and garden |
equipment.js, appliances.js | The equipment schedule at real dimensions: Bonnet suites, Hestan range, stock pot ranges, RATIONAL combis, Rotisol rotisserie, hearth, reach-ins and more, each with live flames, glowing elements, steam, door state and a status LED |
materials.js | Procedural PBR: brushed and polished steel with anisotropy, enamel, cast iron, brass, copper, terrazzo, tile, wood, glass |
ingredients.js | A container for every RM-* and INT-* in its storage location, whose level drains as the simulation draws stock |
cooks.js | Articulated brigade figures in whites that walk, face their work and gesture while busy |
nav.js | A walkable-floor grid with A* path finding, so cooks walk the aisles around equipment the way an AGV moves on a factory floor |
items.js | Plates, pans, trays and pots carried between stations and run to the dining room |
sky.js, clock.js, tour.js | The dusk sky, the "Sense of Urgency" clock over the pass, and the camera shots for the tour |
bind.js | Mirrors simulation state into the scene: cooks walk to their task's station, appliances follow their state, plates move, shelves drain |
quality.js, usd.js | Quality levels and the Pro Max accumulator; the OpenUSD exporter |
The steel reflects the kitchen itself: a cube-camera reflection probe is captured from the aisle once the scene is built. Static geometry is merged into a few draw calls per material, and the cooks' limbs are batched per figure.
Scene API
Everything is driven through ids from the data. The simulation, Studio and the dashboard use the same calls.
const twin = await createKitchenScene(el, { quality: 'auto' });
twin.setApplianceState('EQ-HEARTH', 'busy', { tempC: 420, slotsBusy: 3 });
twin.addCook(id, { name, role, stationId }); await twin.moveCook(id, 'WC-PASS');
twin.addItem(id, { kind: 'plate', dishId }, at); twin.moveItem(id, to, { carriedBy });
twin.setInventoryLevel('RM-LOBSTER', 0.4);
twin.setOverlay('utilization' | 'traffic' | 'both'); twin.highlight(ids);
twin.flyTo('EQ-COMBI-KITCHEN'); twin.startTour(); twin.followNextPlate();
twin.setLook({ sun, sky, interior, accent, exposure, haze, season }); // Studio
twin.setQuality('auto' | 'mobile' | 'balanced' | 'high' | 'ultra');
twin.exportUSD();
Quality modes
Every level shows the same picture: the same materials, in-kitchen reflections, filmic tone mapping, glow on flames and lamps, and soft shadows from the evening sun and the vault lights. Higher levels add sharpness and depth, not a different look.
| Level | For | Pixels | Anti-aliasing | Extras |
|---|---|---|---|---|
Low mobile | Phones, tablets, older laptops | 1× | FXAA | Bloom, 2K shadows |
Medium balanced | Everyday laptops, integrated GPUs | up to 1.5× | 4× MSAA | Bloom, 2K shadows |
High high | Apple Pro and Max chips, discrete GPUs | up to 2× | 4× MSAA | Ground-truth ambient occlusion, 4K shadows |
Pro Max ultra | Stills | up to 2.5× | 4× MSAA + accumulation | GTAO, 4K shadows, progressive supersampling, soft area shadows, depth of field |
Auto
Auto reads the GPU name the browser reports and the device memory. Software renderers, coarse-pointer devices and older Intel graphics start on Low; Apple Pro, Max and Ultra chips and discrete cards start on High; everything else starts on Medium. While the view is in use, if frames stay under about 28 fps for three seconds, Auto steps down one level and says so in a caption. It never picks Pro Max and never overrides a level you chose. Your choice is remembered in local storage.
How Pro Max works
While the camera rests, each frame is rendered with a sub-pixel camera offset and with the sun and key light nudged across a disc, following Halton sequences. An accumulation pass blends each frame into a half-float history buffer with a weight of 1/(n+1), so after about 96 frames the image converges to a supersampled still with soft area shadows, like an offline render. Any camera move resets the history.
Views and controls
3D twin (index.html)
| Control | What it does |
|---|---|
| HUD | Service clock, phase (prep or service), covers seated, tickets on the rail, ticket p90 and the current constraint; play or pause and a speed from 1× to 300× |
| Opening | An 11-second crane shot from the garden into the kitchen, then, on a first visit, one card offering the site tour (skip both with ?intro=0) |
| Dock | Four buttons: Site tour, Kitchen tour, Follow a plate and Operations. Everything else lives in the menu |
| Menu ☰ m | A collapsible left sidebar: the tours, both operations layouts, scenarios, labels, overlay, quality, Studio, this wiki, the research story, Share and OpenUSD |
| Site tour s | An 11-chapter guided story of the whole twin. It moves the camera, opens the dashboard split or full, flips the factory vocabulary, the utilization and foot-traffic overlays, follows a plate and opens the optimizer, with one caption card per chapter. Chapters auto-advance; ← → step, Space pauses, Esc ends it. ?tour=site starts it |
| Kitchen tour t | A camera tour through 15 shots: the suites, hearth, rotisserie, combis, reach-ins, the pass, the clock, pastry, the garden window, the annex and the plant layout from above |
| Bird's-eye view o | The whole plant from above |
| Follow a plate f | The camera follows the next plate from its station to the pass and out to the dining room, and its genealogy card opens: ticket (work order), every routing step with its cook, machine, times and queue wait, the bill of materials with the prep batch (lot) each component came from, food cost and lead time. Click any plate in the kitchen to follow it, or a routing step to fly there. Built by src/sim/genealogy.js from the engine's own records |
| Labels | Station labels, station and machine labels, or none |
| Overlay | Utilization heat on stations and machines, foot-traffic trails, or both |
| Quality | Auto, Low, Medium, High or Pro Max |
| Click anything | Picks a machine, ingredient or plate: highlights it, shows its details and selects it in the dashboard |
| Share, OpenUSD, Studio | See Share links, OpenUSD export and Studio |
| Scenarios | Opens the dashboard at the scenarios and optimizer panel |
| Operations | The live dashboard and the production plan in a drawer. Split keeps the 3D twin beside it; Metrics only gives the dashboard the whole screen. The last choice is remembered, and ?ops=split or ?ops=full opens it |
| Space | Play or pause |
Studio cameras
Dining room, Kitchen, Garden window and Whole restaurant.
Operations dashboard
src/dashboard/ is a control room for the service, drawn with plain DOM and Canvas 2D. It runs full screen at dashboard.html, in the drawer of the 3D twin, and over Studio, always on the same simulation object, so selecting a machine in the dashboard flies the 3D camera to it and vice versa.
| Panel | Shows |
|---|---|
| Top bar | Clock, phase, transport (doors, play, +5 min, speed, scrubber), re-plan by covers, seating and seed, and the Kitchen or Factory vocabulary toggle |
| KPI tiles and banner | Headline KPIs and the named constraint |
| The kitchen, live | A floor-plan view with every cook and station state |
| Ticket rail | Open tickets per table and course, with their age |
| Throughput and WIP | Plates per hour on a 15-minute rolling window, open tickets and queued tasks |
| Ticket time by course | p50 and p90 against the 15-minute target |
| Station Gantt | The last 75 minutes of hands-on work for cooks or machines, colored by course group, prep and pot wash. Click a bar to fly the 3D camera to that machine or station; a bar that cooked a plate also opens the plate's genealogy |
| Inventory | Service stock and the pantry, draining live |
| Appliances | OEE = availability × performance × quality, and load |
| Brigade | Utilization by station |
| Plan the night | Guests, orders, stock and timing tabs that build a config patch and re-run the night from the same seed |
| Guests: the dining-room line | In the house, waiting and walked away; course gaps against target |
| What if… | One-click scenarios with before and after on every KPI. Pin this night as the baseline keeps a run; every later night (a scenario, a re-plan or an optimizer plan) is then compared against the pinned one, KPI by KPI |
| Service scenarios and optimizer | Precomputed nights for 40 to 120 guests, the optimizer's ranked plans, and a button to load one onto the twin |
| Rated capacity | The 20 to 200 cover sweep and its knee |
| Mise en place and ordering | The MRP table and the shift report as CSV |
| Kitchen log | Every ticket fired, seating, stockout and breakdown, in plain words |
The Kitchen or Factory toggle swaps every panel title between kitchen language and manufacturing language from data/terms.json: ticket rail becomes work-order queue, brigade becomes labor, mise en place becomes MRP.
Studio
Studio is where you shape the restaurant behind the twin. Every change other than lighting recompiles the simulation input, re-runs the whole service headlessly in about a tenth of a second, and shows the impact against the house setup for the same season.
The 3D stage fills the screen. Customize e floats the editing panel over it on the left; Run the night r puts the panel away, frames the whole restaurant and plays the full service on your setup from just before the doors open, with its results across the top and the operations dashboard one click away. ?mode=run opens Studio in Run mode.
| Tab | Controls | Flows into |
|---|---|---|
| Dining room | Presets, counts and shapes of two- to eight-tops, covers, seating curve, doors and last seating, eating pace, patience, no-shows, seed | 3D tables and chairs; the sim's tables, reservations and guest behaviour |
| Menu | Tasting or à la carte; courses, dishes and guest-choice shares; prices | The sim's courses, dishes, prep and ingredients; check average and food cost |
| Season | Spring, summer, fall, winter | Out-of-season dishes swap; produce is repriced and delivered short; sun, sky, foliage, produce and flowers |
| Light and mood | Six moods, time of day, colour temperature, brightness, exposure, haze, weather, candles | The 3D light rig only |
compile.js is a pure function from the Studio state and the adapted base model to the simulation's data and config. look.js places the sun with the NOAA solar-position formulas for Yountville (38.4° N) and converts colour temperatures with a black-body fit. stage.js layers the dining room, candlelight, flowers, seated guests and weather onto the same photoreal twin.
OpenUSD export
The USD button writes the kitchen as an OpenUSD stage (.usda) in the browser and downloads it; nothing is uploaded. Prims are named after the data ids, so EQ-COMBI-KITCHEN in the data is /TheFrenchLaundry/Equipment/EQ_COMBI_KITCHEN in Omniverse.
/TheFrenchLaundry Y-up, metres
/Looks UsdPreviewSurface materials
/Building shell, benches, annex, garden
/Equipment/EQ_HEARTH ... one Xform per machine with twin:* metadata,
render meshes and a box collider
/WorkCenters/WC_FISH ... stations with cook positions
/Brigade/... cooks at their current positions
/PhysicsScene so Isaac Sim can drop robots or people in
It opens in usdview, NVIDIA Omniverse and Isaac Sim. It carries geometry, transforms, materials and colliders, not the simulation. The repository's NVIDIA path explains how the twin would move into Omniverse, Isaac Sim and cuOpt, and is explicit about what is roadmap.
Build, test and deploy
npm install
npm run dev # dev server at http://localhost:5173 (also /studio.html, /dashboard.html, /wiki.html)
npm run validate # every id reference in /data resolves; option shares sum to 1
npm test # planner, engine and Studio tests (node --test)
npm run plan -- 62 # production plan for 62 covers in the terminal
npm run sim:report -- 124 # simulated KPIs for 124 covers
npm run precompute # re-run the optimizer and rewrite src/sim/precomputed.data.js
npm run build # static site in dist/
Vite builds four HTML entries and serves them under /michelin-kitchen-twin/, the repository's Pages path. The tests check that the planner reproduces the published Oysters and Pearls quantities, that the engine is deterministic for a seed, replays identically after a backwards seek, serves every guest and keeps inventory balanced, that each what-if moves the KPIs it should, and that Studio's compile, seasons, share links and sun position behave.
Performance
- No asset downloads. Geometry and textures are generated at load, so the whole site is a few hundred kilobytes of compressed JavaScript and CSS.
- Fewer draw calls. Static meshes are merged per material and the brigade's limbs are batched, so the kitchen renders in a small number of draw calls.
- Event-driven simulation. The engine only computes when something happens, and the scene re-reads it at 6 Hz while tweening in between.
- Adaptive quality. Auto starts from the GPU and steps down if the frame rate stays low; Pro Max only accumulates while the camera rests.
- Precomputed answers. The optimizer's results for four guest counts ship with the site, and cached nights skip their forecast run.
- Measured in the page.
src/perf.jstimes the first 3D frame and the live frame rate; the menu's View section shows them, andnpm run perfrecords them at each quality level on a laptop and a phone viewport. Results and method: docs/performance.md. - Headless re-runs. Studio, what-ifs and the capacity sweep re-run the full night without rendering, yielding to the UI between runs.
How it was built
The twin was built by Claude: several sessions working in parallel in one project, each in its own thread, each owning one part of the repository and merging its own pull requests once CI was green. A person set the goal and the bar and steered the work.
| Workstream | Owned |
|---|---|
| Product | The PR-FAQ and PRD: who the twin is for, the two-minute demo, what "realistic" means |
| Research and data model | data/, the sources, the MRP explosion, the site shell and the Pages deploy |
| Simulation | src/sim and src/dashboard: the engine, the guest line, what-ifs, capacity and the dashboard |
| Scenarios and optimizer | optimize.js, the precomputed library and the dashboard panel |
| Realistic 3D kitchen | src/scene: materials, the building, cooks, quality levels, OpenUSD export |
| Studio | src/studio: dining room, menus, seasons, light and mood |
The threads agreed on contracts rather than sharing files: one set of ids in the data, and small APIs (createSim, createKitchenScene, bindSim, mountDashboard, compileStudio). When one thread needed something from another, it asked, and the owner made the change. The full story is in docs/how-it-was-built.md.
Limits and estimates
- The menu is a composite of documented courses; the real Chef's Tasting Menu changes daily.
- Portions for courses without a published recipe, operation times not stated in a recipe, brigade size and shifts, equipment positions and slot counts, ingredient prices and eating times are estimates, marked
estin the data. - The layout follows the published concept and dimensions but is a reconstruction, not a survey.
- Labor load counts routed operations only; tasting, cleaning down, family meal and the chef's walkthrough are not modelled, so real utilization is higher.
- Dispatching uses simple priority rules. The optimizer searches staffing and fire-timing levers over whole nights; it does not solve for an optimal schedule of every operation.
An independent, non-commercial portfolio project built from public information. Not affiliated with or endorsed by The French Laundry, Thomas Keller Restaurant Group, the Michelin Guide or NVIDIA.