Clinical skills simulation · WebXR-first

OpenClinXR

Timed clinical skills stations—from case blueprint to WebXR runtime— with faculty review, durable traces, and promotion bound to the reviewed revision.

Inspired by Step 2 CS-style multi-station flow. Built as an encounter factory, not a pile of one-off scenes. Not an exam-equivalence or clinical-validity product.

An ED stroke-alert station rendered in the browser: three clothed actors with contact shadows, a procedurally generated wall clock, and a generated bedside monitor on its parametric stand
Who it’s for Simulation programs & clinical education teams

Authors define stations. Learners run timed encounters. Faculty review traces and packets. Admins hold the gates.

What we prove today Reviewed blueprint → pinned runtime → replay

Authored content identity now follows an encounter through faculty promotion, a pinned runtime bundle, timed learner phases, actor execution, and review.

Trust line No clinical scoring or Quest readiness claims

Promotion and readiness flags stay false until hardware and policy say otherwise. We document limits in public.

Cleared movement evidence · 2026-09-04

The rig moves. The license boundary moves with it.

This six-second browser capture shows skeletal motion reaching the head, torso, shoulders, arms, and hands. It is a rig-deformation proof, not a polished performance.

The visible body, hair, shirt, and motion sources cleared the project’s publication review. The lower-body crop is deliberate: the source trousers have an unresolved per-item license conflict, so that geometry is not published here. Viseme footage is also withheld until its separate source provenance is resolved.

Machine-readable provenance · license review

notEvidenceFor: production animation, cloth behavior, speech or viseme synchronization, Quest readiness, clinical validity, or exam equivalence.

Speech · emotion · hair · skin · 2026-09-19

The factory nurse speaks, with sound — PP sealed, aa graded.

Thirty-four seconds of the factory scratch nurse: three spoken lines, an authored emotion arc, blinking, strand-textured hair, and pore-level skin. The file includes an audio track. Turn the sound on. CEO grade: sil sealed; PP sealed (first time); aa open with upper tooth row; lower cavity still a dark hole.

Progress log entry 109 · scratch bake, not the shipped GLB

notEvidenceFor: production TTS, Quest readiness, clinical validity, exam equivalence, gums/cavity detail, the shipped GLB, or a live station-reply capture.

Platform

Four pieces. One factory.

Exam surface

Author, assemble, run, and replay timed stations for learners, faculty, and admins— with traces, actor turns, and review packets in the loop.

Encounter factory

Reviewed case definitions drive scenes, actors, dialogue posture, emotion timelines, asset needs, and persistence—so work scales past hand-built demos.

Asset commons

Rooms, humanoids, clothing, equipment, and provenance intended for reuse across encounters— with cage-match style comparison before anything is treated as “ready.”

Capability arena

Sidecars for humanoid generation, voice, IWSDK spikes, and providers stay isolated until evidence supports a promotion decision. Experiments don’t become product by accident.

Current state · 10 September 2026

A reviewed encounter can now become a resumable, replayable run.

Author and promote

  • Revision-bound review: a scenario edit invalidates stale approval; API promotion refuses missing or stale authored-content identity. Qualified 2026-09-10: that held for authored scenario text and did not hold for the frozen scene — SC-06 measured the accepted plan carrying 0 of 14 identity fields, with a stale acceptance silently reused. It now refuses; see the measurements below.
  • Immutable encounter bundles: reviewed factory outputs are promoted together, with the room, actors, equipment, and dialogue configuration pinned to the approved revision.
  • Faculty-visible delta: Admin can preview what an authored change will alter at runtime before promotion.
  • Scenario bank freeze: approved revisions and promoted station-bundle pins survive later authoring changes.

Run and resume

  • Bundle-driven boot: the learner runtime mounts the promoted room and starts from the pinned encounter bundle, failing closed when compiled-room readiness is incomplete.
  • Assembled exam clock: encounter, note, and timed-break phases emit monotonic, replayable events with exactly-once timeout behavior.
  • Restart-safe progress: API and MongoDB adapters rebuild an assembled run from its durable ledger; incompatible resume state is rejected.
  • End-to-end acceptance: repository tests exercise learner progress through faculty review across a restart boundary.

Speak, move, replay

  • One frozen actor turn: voice, visemes, affect, facial expression, and motion start from an identity-bound plan rather than loosely interpreting dialogue text.
  • Audio-clock synchronization: visible and audible modalities follow the same playback clock.
  • Replayable interruption: learner barge-in is recorded across turn modalities without rewriting the approved plan.
  • Plan versus execution: the immutable intent and observed execution are persisted separately and exposed in faculty replay.

Evidence and boundaries

  • Committed proof: the claims above are backed by runtime and package tests in the repository, including restart-safe learner-to-faculty acceptance. Nine independently graded evidence reports are now published — see the section below.
  • Visual proof remains bounded: the screenshots below demonstrate browser-rendered factory, actor, garment, room, and equipment work. They do not prove the entire new lifecycle as one polished user journey.
  • No clinical validity, scoring validity, licensure, or exam-equivalence claim.
  • No Quest readiness claim: browser/WebXR and emulation evidence do not substitute for worn-headset validation.
  • No production deployment claim: development remains local-first, with external providers and promotion paths gated.

Source of truth for status is the repo ledger (PROJECT_STATUS.md), not this page. Public copy here is a snapshot for humans—updated when the product story actually moves.

Simplified seed 7 room: a wood-grain casing frames the doorway in a blue-grey plaster wall. Native 1280 by 720.
22 September 2026, after simplify. Skirting and openings 162,648 to 101,702 triangles, walls and floors 6,336 to 3,205. The clinic file a learner loads was not replaced. Progress log entry 112 has the lighting pair and the triangle counts.

Measured, not asserted · 2026-09-10

Nine graded reports, and the numbers that did not clear.

Nine cards landed against a frozen acceptance contract. Each carries an evidence report graded by its own verifier against retained artifacts, and each was re-measured by the owner against the tree rather than accepted from its report. Three of nine were rejected at least once; one was rejected three times. The rejections and the failures are published alongside the passes, because a report that only lists passes is not evidence.

The bedside approach, measured in a browser on an M1 Max over 373 skeleton samples.
MetricMeasuredThresholdOutcome
Arrival error0.03597 m≤ 0.05 mpass
Settled heading error0.000°≤ 10°pass
Stopped duration4.816 s≥ 2 spass
Root travel while stopped0.000000 m0 mpass
Deepest floor penetration0.00179 m≤ 0.005 mpass
Foot slide8.00 Hz capture~132 Hz needednot gradeable

The last row is the useful one. Identifying a foot-contact window at the rubric's 0.005 m per-frame allowance needs roughly 132 Hz; four skinned humanoids and a compiled room through software rendering gave 8.00 Hz. Rather than grade an aliased stream, the instrument refuses and reports the number. An earlier revision of that report graded it as failing from an assumed 60 Hz that was never measured; that verdict was wrong in the same way a passing one would have been, and is retracted in place in the published report.

Other findings worth naming: the shipped locomotion clip fails the frozen rubric, and no clip this pipeline has produced meets the foot-plant threshold. A learned-motion candidate was screened and held, not adopted — its text encoder depends transitively on a gated model licence this project does not hold, and the host has no CUDA device. Twenty-eight open items are declared across the nine reports.

Reports: SC-00 rubric · SC-01 · SC-01S · SC-02 · SC-03 · SC-04 rights · SC-05 approach · SC-06 replay · SC-10 research hold

notEvidenceFor: clinical validity, scoring validity, exam equivalence, and worn-headset readiness are not claimed and have no evidence here. Three of twelve acceptance rows — an uninterrupted workflow recording, a website demonstration, and independent final acceptance — are unstarted, so this package is not closed. No humanoid imagery from these cards is published: their public render rights are blocked pending a named upstream licence resolution.

Evidence you can look at · 2026-08-31

The case is a compile graph faculty can edit.

These screenshots were captured from commit 05120405, where BothyBoard children W1–W19 (plus W14a/W14b/W11s) had landed. Faculty author the encounter as a Scenario, then review a World Compile Graph of baker families. Add node / Remove node mutate the graph; the lock table stays the lock write path. Chromium Playwright of the running admin app (ui-admin + local API), not a schematic. Since this capture, authored revision identity now invalidates stale approval, and Admin can preview the runtime delta before a reviewed revision is promoted into an immutable encounter bundle.

OpenClinXR Admin Encounter Case Authoring: load ED Chest Pain example, scenario id ed_chest_pain_priority_v1, title ED Chest Pain With Nurse Interruption And Family Pressure, claim boundary notEvidenceFor clinical validity
Renderer: Chromium Playwright, http://127.0.0.1:5174/authoring, 1600×1200 native. Subject: CaseAuthoringWorkbench with the bank case loaded.
World compile graph canvas: Add node and Remove node buttons, three columns Body, Wardrobe, and Equipment baker nodes with body_to_clothing and wardrobe_to_equipment edges
Renderer: Chromium Playwright of CompileGraphCanvas via an isolated ui-admin shot page (127.0.0.1:5174/compile-graph-shot.html; live /exam-forms does not render in dev), 1400×420 native, captured 2026-09-10 at HEAD bbf3f84a. Subject: Body → Wardrobe → Equipment baker DAG with Add node / Remove node; edges seeded in buildCompileEdges shape.

claimScope: local admin UI capture of an authored Scenario at capture commit 05120405 plus the compile DAG recaptured 2026-09-10 at bbf3f84a; current revision identity and runtime-delta behavior are supported by later committed application tests, not by these stills. notEvidenceFor: Quest readiness, clinical validity, live Blender bake, LLM draft quality, or that the parent BothyBoard program card is closed (it is still Idle). Node labels truncate in this crop; that is the canvas, not missing data.

Evidence you can look at · 2026-09-01

Faculty configure the nine production stations as cards, not a hand list.

Each production factory station is a Standard Schema V1 interface (~standard.validate plus jsonSchema.input). Admin cards are derived from that schema: one card per id, one control per property, Apply refuses invalid payloads. instrument is a gate and is not a card. The TRELLIS control adds a worldview bake model (subject/pack ecg-cart-imagine-box), distinct from a fixtureSlot bind.

Factory station cards: nine production stations as Ant Design cards with schema-derived fields, Add TRELLIS bake model, body_param filled for patient_maya_johnson_v1, equipment_generate filled for ecg-cart-imagine-box viewCount 4
Renderer: Chromium Playwright of isolated FactoryStationCards (Vite 127.0.0.1:5174/factory-station-cards-shot.html), 688×3870 native, captured 2026-09-10 at HEAD bbf3f84a. Subject: all nine production station cards. Scroll the figure for the full stack.
equipment_generate station card: subjectId and packId ecg-cart-imagine-box, seed 7, remesh on, viewCount 4, decimationTarget 80000, Apply
Native close-up of the equipment_generate card from the same render. Knobs include schema-only decimationTarget.

claimScope: local admin React cards derived from factoryStationSchemas on origin/main. notEvidenceFor: live TRELLIS GPU bake, Quest readiness, clinical validity, or that Apply invokes a baker (it validates and emits the payload).

Evidence you can look at

The patient on the bed was a wad of geometry. Now she is a person.

Three of the fourteen stations lay their patient down. For weeks those three rendered a crumpled knot on the mattress. Five separate fixes landed and none of them changed a pixel — each was aimed at a cause nobody had located. On 2026-08-20 the sixth worked, because an ablation was run first instead of a sixth guess: render the pose's two mechanisms separately and see which one is the damage.

Three-cell ablation contact sheet. Left: the gowned MPFB body standing upright, no supine call. Centre: the same body lying on its back with only the root basis applied. Right: the full supine path. Centre and right are identical.
The ablation, isolated harness, product renderer. standing · root_only · full. The centre cell was always a person; the right cell used to be the knot.

The cause was seventeen joint rotations hand-tuned against Anny's 23-bone skeleton, being applied to an MPFB body with 138 joints. They are correct for the rail they were written for, so the fix was not to delete them — it was to scope them. Anny keeps its table; MPFB skips it. One switch, and the right-hand cell above became the centre cell.

The part worth keeping is the measurement that could not tell them apart. Before the fix, the knot and the person had identical envelope metrics — height 446 mm, minimum Y 0.570, to three decimal places. One was a patient and one was refuse, and every bounding-box assertion in the repository scored them the same. Any future contract that grades a lying figure by its bounding box is measuring nothing.

Assembled postoperative ward station: a patient in a blue gown lying on her back on the bed, with a nurse and a surgical resident standing near the doorway, EHR panel to the right.
postop_fever_consult_pressure_v1, assembled station, graded by eye. All three recumbent stations were re-captured and all three now render a person on the bed.

Not fixed, and visible in that capture: her arms rest raised in the air rather than at her sides. That is the bind pose showing through now that the wrong rotations are gone, and the answer to it is a retargeted recumbent motion clip — not a second table of hand-authored angles. That work is open. Nothing here is a claim about clinical validity of the pose; it is a claim that a figure on a bed reads as a human being.

Evidence you can look at

The case said 52 years old. The factory was reading a Python file.

A case definition describes a person in numbers: age, height, build, how tense they look. Until today the factory did not read them. Bodies were baked from hand-written Python presets — four of them, covering four actors out of a hundred and four. Every other figure a learner met was the same body with a different job title, and the numbers in the case were decoration.

The case now authors the phenotype and the generator resolves it. Measured in the shipped fixture data: 32 of 32 actor records carry numeric phenotype, where before the audit found thirty-eight of forty-two actors with none at all.

patient_robert_hayes_v1
  age            52
  height_cm     178
  bmi            26
  brow_tension  0.55
  anxious       0.65
  flush         0.15
actor-phenotype.v1.json, generated from the case bank. The chest-pain patient is fifty-two because the case says so, not because a preset did.

What this is not: it is not a claim that every shipped body has been re-baked from these numbers. It is the data and the seam that reads it — the resolver now has something to resolve. Re-baking the cast against it is the next piece of work and it is open. Nothing here is a claim about clinical validity of any figure; it is a claim that the case definition finally reaches the body.

Evidence you can look at

The case says brown eyes. Now the figure has brown eyes.

A case definition describes a person: age, build, hair colour, eye colour. The factory was reading almost none of it. Every shipped figure's iris came from a role default — patients brown, family green, nurses blue — assigned by matching words in a job title. The case's own eye_color field was carried all the way to the bake and then dropped, because the one call site passed an empty dictionary where the phenotype belonged.

Four close-up crops of rendered eyes. Top row: the paediatric parent, green irises before and brown irises after. Bottom row: the nurse, blue irises before and brown irises after.
Isolated renders at 4096², cropped to the eyes at native resolution · parent green → brown, nurse blue → brown, both as their case authored

The more useful half is what happens when a case asks for something the factory cannot build. One patient is authored with hazel eyes. There is no hazel material in the licensed set, and the old behaviour was to silently fall back to a role default — the case asked for one thing, the learner saw another, and nothing anywhere said so. The factory now refuses that value out loud, and publishes the nine colours it can actually produce, each with its licence, generated from the same list the renderer reads so the two cannot drift apart.

That refusal is deliberate and it is the point. A factory that quietly substitutes is a factory whose output nobody can trust to match the case; a factory that refuses forces the question upstream, where a human or an authoring tool can answer it against a real list of options. The hazel patient is still unbuilt, and that is the correct state until someone chooses.

Not claimed: that these faces look finished. The skin is procedurally baked and carries no painted facial detail, which is visible in these crops and tracked separately. This shows one field of the case definition reaching one property of the render, verified by comparing the shipped bytes and by looking at the pixels.

Evidence you can look at · 2026-08-28

Roles now wear distinct fitted garments, not one beige palette.

Shipped MPFB bodies carry MakeClothes library garments fitted onto the standard rig: teal scrubs on the nurse, a cyan exam gown on the adult patient, sage street clothes and boots on the walk-in, muted rose on the family partner. Contrast against skin is visible at a glance. These are isolated grade captures of the tracked GLBs under apps/ui-xr/public/generated-humanoids/, not the 2026-08-21 sage t-shirt pair that documented a palette bug.

Isolated lit grade of the MPFB clinical nurse in teal V-neck scrubs, trousers, and shoes.
Nurse · mpfb-clinical-nurse-adult.glb · scrubs
Isolated lit grade of the MPFB adult patient in a cyan knee-length exam gown and shoes.
Patient · mpfb-gown-adult-patient.glb · exam gown
Isolated EEVEE grade of the MPFB street-casual adult male in a sage t-shirt and MakeClothes punkduck classic jeans, brown boots, world ambient lighting.
Street patient · pants02 punkduck_male_classic_jeans (CC-BY)
Isolated lit grade of the MPFB family partner in a rose t-shirt, trousers, and shoes.
Family · mpfb-family-partner-adult.glb

Street still: isolated EEVEE finished_figure_grade.py --frame full with world Background strength 0.55 (grade-lighting.json), 2026-09-10. Other cells: model-vetting-glb-grade-capture front_lit, 2026-08-25/28. Garments are fitted via MakeClothes ClothesService.fit_clothes_to_human onto the MPFB basemesh. A 2026-08-21 incident (cream closed_casual tint 21 RGB units from skin) is closed.

Still visible and not claimed as production cloth: nurse and family still show a waistband gap (cargo cover shell). Street trousers are MakeClothes pants02 punkduck_male_classic_jeans (pack-page CC-BY), not a cover shell. Isolated EEVEE bake includes world Background strength 0.55. Poke-through at the chest on some shirts. Isolated harness, not a station. claimScope: role-distinct fitted clothing on shipped MPFB GLBs. notEvidenceFor: production cloth, Quest readiness, pregnancy morphology, clinical validity, or exam equivalence.

Evidence you can look at

The audited legacy MakeClothes cache contains no hospital gown.

Every garment in the audited legacy MakeHuman/MakeClothes cache was fitted, rendered, and assigned a geometry-derived class. Within that specific cache, hospital_gown has zero members. This does not describe the separate case-linked real-gown path shown elsewhere in the repository and on this page.

Thirteen-cell contact sheet of every cached garment, each fitted and rendered in a distinct colour with its filename and assigned class: an evening dress, two open lab coats, scrub top and trousers, several t-shirts and a sweater, boots, flats and cloth shoes.
13 unique garments from 16 cached files. Classes derived from fitted geometry — hem height and shoulder coverage — not from the filename.

street 8 · footwear 3 · labcoat 2 · scrub 2 · evening_dress 1 · hospital_gown 0. Nothing fell into other and nothing was left unknown.

The first cell is the whole reason this was built. A file named crudegown.mhclo once passed three machine contracts — its licence was verified, its vertex indices were verified, its presence on the body was verified — and then somebody looked at the render and found a floor-length spaghetti-strap evening dress. Presence, placement and provenance are three questions, and none of them is class. The inventory now answers the fourth one from geometry, in machine-readable form, before anyone opens an image: crudegown → evening_dress, hem at 3% of body height.

The practical consequence is narrower: this legacy cache cannot satisfy a hospital-gown request, and the resolver must not substitute its evening dress because the filename sounds plausible. The current real-gown path is a separate, provenance-tracked source and still has to pass its own fit, motion, and runtime evidence gates.

Evidence you can look at

Clothing is visible and case-linked, but remains below production realism.

Garment layers now come from the case definition and are visible on browser-rendered actors, including the real-gown path and clothed multi-role stations shown on this page. That is evidence of case-to-runtime wiring, not a claim of production-quality cloth, fit, or motion.

Correction, 2026-08-10 evening. This section previously said one actor “renders translucent”. That was our diagnosis and measurement disproved it. Nothing on that figure is transparent; the garment covers every face of the region it claims — across four body/slot pairs, hidden-plus-behind-cloth accounts for all of them and zero faces have no garment nearby. The skin a viewer notices sits outside the claimed region, which makes it a question about how far the garment should reach, not a rendering fault.

The open quality bar is visible: some garments still read as rigid shells, hems and joins remain rough, and motion evidence does not yet establish production cloth behavior. New clothing claims therefore require measured Model Vetting and UI-XR evidence rather than inference from asset presence alone.

Inspection packet: ed-real-garment-webxr-inspection.json. Deeper factory reports live under docs/openclinxr and Model Vetting cagematch outputs in the repo.

Evidence you can look at

The grader caught a figure with no trousers.

On 2026-08-11 a rig upgrade landed on the library bodies — MPFB's 64-bone mixamo_unity skeleton and its shipped CC0 weight map, replacing a hand-rolled bounding-box armature whose hands carried 0.00% of the skin weight with 20,216 vertices collapsed onto a single upper-arm bone. Three machine contracts passed. Then the capture was graded by eye, and the figure had no trousers.

Isolated grade capture of the adult lean female library body: an upright clothed figure in a blue shirt and teal trousers, shoes on the ground plane
Before · makeclothes_library_cargo_pants… at 2,530 triangles.
The same body after the rig upgrade: the blue shirt remains but the legs are bare skin from hem to shoes, the trousers mesh absent
After the upgrade, since reverted · same mesh list minus the trousers. The lower garment is gone, not mis-shaded.
Update, same day: the rig upgrade has since landed cleanly. Both bodies carry trousers again (2,530 and 3,767 triangles) on the 64-bone rig, and the re-graded capture is indistinguishable from the “before” image — which is the correct outcome for a skeleton change. The acquired CC0 trousers geometry is still not the thing that ships; a deterministic cover shell is. That half is open.

The revert took one commit. What it exposed took the rest of the day: the trousers were never rebuildable. Scrub_Shirt.mhclo and cortu_cargo_pants.mhclo were on no disk in the repository — the staging directory is empty and gitignored, and the provider cache held three upper garments and no lower-body source at all. The 2,530 triangles in the tracked asset came from a bake whose input no longer existed. Meanwhile the pipeline's own “find-or-stop” guard, asked for a lower garment and finding none, logged a warning and baked the body anyway.

Both halves are fixed and in the repository: the guard now throws instead of warning, and the sources are acquired, tracked, and recorded in the licence ledger at acquisition time — makehuman-pants01 (CC0, Cortu Johnstone) and Scrub_Shirt (CC-BY, WojackOWL). A re-bake now finds its inputs with no network, or refuses loudly.

What this picture is not. It is not a claim that the figure looks good. The same capture carries three defects we have measured and not fixed: a sawtooth band of bare skin where the shirt hem meets the trousers; hands painted the garment colour rather than clothed — the shirt mesh spans Y 0.913–1.505 m on a 1.760 m body while the hands sit near 0.79 m, below its lowest vertex; and a shirt with no volume of its own, breast and navel anatomy reading straight through it.

A correction we made to ourselves. Grading the lit pass alone, we recorded the hands as “mittens with no separated fingers” and were about to file missing hand geometry. The structure pass shows fingers and thumbs present and well formed. Lit resolves silhouette, structure resolves topology, and the lit pass flatters. The wrong finding was caught because both passes are captured, not because anyone was careful.

Structure pass of the same body: a normal-shaded wireframe showing complete continuous topology including separated fingers and thumbs
Structure pass · same asset, same run. Complete continuous topology, fingers included — the detail the lit pass hid.

Captures produced by model-vetting-glb-grade-capture, whose NodeIO-versus-scene-graph self-check agreed to 1.2 × 10⁻⁵ relative error. That agreement proves the renderer drew the file and nothing about whether the file is right — which is why a human graded the pixels, and why the missing trousers were noticed at all.

Evidence you can look at

These hands could not bend this morning.

The two library bodies were bound to a hand-rolled bounding-box armature with Blender's automatic weights. Measured, that put 0.00% of the skin weight on hand.L/R and collapsed 20,216 vertices onto a single upper-arm bone — an arm that moved as one rigid piece from shoulder to fingertip. The fingers were in the mesh. Nothing could move them.

They now ride MPFB's 64-bone mixamo_unity rig with its shipped CC0 weight map — mixamorig:LeftHand alone carries 592 vertex entries, with full finger chains. Both files shipped with MPFB and neither was being used.

Supine isolated render of the heavy-male library body in green scrubs, hands resting together on the abdomen with individually posed fingers, knees slightly flexed
hm08 heavy-male body · recumbent posture lab · hands folded on the abdomen, fingers posed individually.
Supine isolated render of the lean-female library body in a blue top and teal trousers, hands together on the abdomen with posed fingers
hm08 lean-female body · same lab, same rig · the arms fold across the body rather than swinging as one piece.

Why supine, and why that is the better evidence. A standing rest pose looks identical before and after a skeleton change — it would prove nothing. These are recumbent renders from the isolated posture harness, and the folded arms and separated fingers are shapes the previous rig could not produce at all.

What these are not. Isolated lab renders, not in-station frames: no room, no other actors, no clinical context. Framing is computed from the subject's bounding box by the harness, not authored — which is the point, because the authored per-mode cameras in the full scene are a separate and still-open defect. Two attempts at hand-tuning those camera positions today were measured and reverted, one of them producing a 7.4 KB blank frame.

Defects still visible and unfixed: the sawtooth seam where the top meets the trousers, and low-polygon faceting across the limbs. Neither is claimed as solved.

Dark software factory · shaping up

Prompt in. Budget mesh out. Deterministic stations in between.

Lights-out generation: a hard-surface prompt becomes a budget mesh through named, measured stations — not a person pushing sliders on one GLB. Grok Imagine packs condition TRELLIS Metal in an isolated process; then factory:trellis:optimize runs high-error meshopt targets from the raw mesh, a weld pass, and optional factory:trellis:pack (gltfpack). One ECG cart, VR hard-surface pack, measured 2026-08-11: 973,639 raw → 60,000 preferred (clears ≤80k); the same ladder can stretch to 34,443 (−96.5%) when a station needs the share band. Photoreal packs on the same ladder stalled at ~186k. Meta Quest 3 class scene guidance is ~1.3–1.8M tris for a whole scene; ≤40k is a multi-prop share, not the device limit. Not worn-headset readiness. Not clinical realism.

  1. 1 Imagine prompt
    Low-poly game-ready medical ECG monitor cart prop for WebXR / Quest.
    Hard-surface stylized, NOT photoreal. Clean boxy forms only.
    Wheeled base · upright column · large matte black screen.
    6–8 square button pads · ≤6 circular jacks. NO free cables.
    NO logos, labels, text. Matte grey plastic. Studio grey bg.
    Maximize large flat planes for 3D reconstruction.

    Hard-surface pack prompt — not a photoreal product shot. Photoreal inputs defeat post-opt (measured ~186k floor); hard-surface packs unlock preferred / share bands without fighting high-frequency detail.

  2. 2 Image output
    Grok Imagine hard-surface ECG cart: grey boxy monitor on casters, black screen, colorful button pads and ports
    Grok Imagine · VR hard-surface reference (single view). Multi-view pack (front / side / ±¾) conditions TRELLIS.
    Four-view multi-view pack of the same ECG cart for TRELLIS conditioning
    Multi-view pack · factory input to factory:trellis:bake
  3. 3 Pre-optimized mesh
    Raw TRELLIS ECG cart, three-quarter, studio grey lighting matching the Imagine still
    Raw TRELLIS Metal bake · Blender EEVEE studio (same grey world + key/fill/rim as the Imagine still)
    Triangles
    973,639
    Stage
    raw export
    vs prop preferred
    ~12× over 80k

    MADR 0050: do not reject the generator on raw tris. Judge after optimization. Raw megameshes are never delivery assets.

  4. 4 Post-optimized mesh
    Post-opt ECG cart at 34443 triangles, three-quarter, studio grey lighting matching the Imagine still
    Stretch rung 34,443 tris · same EEVEE studio and camera as the raw still
    Preferred rung
    60,000
    Stretch rung
    34,443
    From raw
    −93.8% / −96.5%

    Direct high-error targets from raw. Chain ratios plateau ~59k on this pack. Stop at the first graded rung under ≤80k; 34k is share-band stretch, not “Quest wants 40k.” Delivery: optional factory:trellis:pack. Harness prop — not a worn-headset claim.

Measured optimize path (ECG cart, hard-surface pack): 973,639 raw → 60,000 preferred −93.8% · stretch 34,443 when share-band pressure · same subject, 2026-08-11
Deterministic stations between the prompt and the budget mesh. ECG cart unless noted.
Station What it does Measured
Hard-surface pack Grok Imagine prompt for boxy planes, not a photoreal product shot Photoreal pack post-opt floor ~186k. Hard-surface chain ~59k; high-error stretch 34k
Isolated Metal bake One OS process per subject (factory:trellis:bake) Same-process multi-subject TRELLIS OOMs the MPS heap and cascades
Multi-view condition front / side / ±¾ PNGs concatenated into TRELLIS embeddings ECG far-side fill 0.35 → 0.44, surface area +47%, 3.7% fewer triangles
High-error from raw factory:trellis:optimize direct targets (180k / 120k / 80k / 60k / 40k) 973,639 → 179,999 / 60,000 / 39,999. Chain ratios plateau; further 0.05 cuts barely move
Weld, then same targets Position merge so split verts stop inflating the count 40k rung stays 39,999 after weld; 25k stretch floors ~34.5k
Pack factory:trellis:pack gltfpack quantize + meshopt compress Delivery size. Do not use -sa -se 1 (can zero the mesh)
Champion policy First graded rung under preferred ≤80k; denser sibling if ≤40k looks worse 60k is a legitimate champion. 34,443 is a measured stretch, not a Quest-3 prop limit

Budget policy: prop preferred ≤80k (default stop) · share ≤40k only under multi-prop pressure · acceptable ≤120k · skeleton hard ≤180k (partial station, not a full multi-actor exam) · Quest 3 device-class ceiling ~1.3–1.8M scene tris (Meta native). Draw calls, materials, and fill-rate usually beat another −15k on one cart. Order: batching → materials → KTX2 → multiview → tris. CLIs: pnpm factory:trellis:bake → pnpm factory:trellis:optimize → pnpm factory:trellis:pack (hatch: pnpm factory:trellis:hatch). Skill: .agents/skills/trellis-vr-equipment-optimize/. Evidence: .openclinxr/evidence/trellis-bake-vr-hard/, trellis-vr-optimize-iterations/ecg-cart-vr-hard/. claimScope: factory automation path for equipment props, measured on the ECG cart hard-surface pack. notEvidenceFor: Quest readiness, clinical accuracy, exam equivalence, sampler-knob sweeps (those fifteen knobs are still vendor balanced-tier defaults).

Evidence you can look at

Clinical equipment, generated from a single reference image.

The input half, added 2026-08-10. Before anything is reconstructed, the factory renders reference views of the object from its own parametric builder. On 2026-08-10 that step went from 3 subjects to 38: 35 clinical objects, five views each, 175 renders in a single 61-second pass with one browser and one dev server. Every view is measured for isolation — no room geometry, no HUD, no ground plane — and every one passed.

Six procedurally generated clinical objects rendered on a plain background: a whiteboard, two chairs, an ECG machine, a wall oxygen port and a tissue box.
Six of the 35, three-quarter view. These are the parametric source — low-poly, untextured, deterministic. They are not the finished asset; they are the reference input the reconstruction step above consumes. Shown so the two halves of the pipeline can be compared honestly.

Each of these began as one reference image and was reconstructed to a textured mesh by TRELLIS running on Apple Silicon Metal — no hand-modelling, no per-asset artist pass. Rendered here in isolation from the shipped GLB, lit and wireframe, at the triangle budget each survives.

Wall clock generated by TRELLIS, lit render
Wall clock · 34.5k triangles · under the 60k soft station band.
Bedside monitor generated by TRELLIS, lit three-quarter render
Bedside monitor · 106k triangles · hard band. Front three-quarter; the unseen rear reconstructs poorly.
ECG cart generated by TRELLIS, lit three-quarter render
ECG cart · 151k triangles · hard band. Lead wires and casters are geometry, not texture.
A clinical station showing a detailed generated wall clock on the wall at left and a simple procedural bedside monitor at right.
The same station, one capture: the generated wall clock (left) beside the procedural bedside monitor (right). Cropped to the equipment band — the figures in the full frame are fixture-grade and are not part of this claim.
A clinical station with a generated wall clock mounted on the wall and a generated bedside monitor standing on the floor.
Two generated assets in a live station: the wall clock on the wall at 34,885 triangles, and the bedside monitor at 60,378. The monitor is standing on the floor — a bedside monitor belongs at bedside height, and the fix is open. Shown as it renders, not as we would like it to.
Side-by-side renders of a generated ECG cart, one from a single reference view and one from four.
The same ECG cart from one reference image (left) and four (right). Four views close the torn hole in the side panel and add a drawer stack and casters; they also flatten the monitor screen into a blank panel, because the views carry no camera poses. Both results are real.

The wall clock is now consumed by the runtime: a station that declares it renders the generated mesh, measured at 34,885 triangles in a live scene, up from a 26-triangle placeholder. The monitor (~106k) and denser ECG cart variants remain isolated harness renders pending grade + multi-prop station share — 106k is still under prop acceptable (≤120k) and far under Quest 3 scene class (~1.3–1.8M). None of this is a Quest performance claim, and none of it is evidence of clinical realism.

Evidence you can look at · 2026-08-14

Escape hatch: text → Imagine → remesh → 80k.

Last-resort factory when the object is not in the kit/parametric store and not acquirable CC0/CC-BY. One Grok Imagine upper-¾ on a black void, flood-keyed (Imagine writes JPEG RGB — no native alpha), then TRELLIS.2 Metal with Space-order remesh on compact extracts, then factory:trellis:optimize to the preferred ≤80k stop. Six subjects ran that path. The 49M medication cart is a recorded miss. Kit remains exam SSOT. These GLBs are harness champions — not promoted into ui-xr.

Grok Imagine fetal monitor: thick grey CRT box, four square pads, knob, two top pucks, handle, black void
Fetal monitor · Imagine input. Thick volumes so occupancy keeps a screen face.
Isolated three-quarter lit grade of the 80k fetal-monitor champion: CRT well, four pads, knob, two pucks survive
Same subject · 80,000 triangles. CRT well, four pads, knob, two pucks survive. Softer than the Imagine plate — expected after remesh + high-error simplify.
Grok Imagine wall oxygen port: beige plate, two metal barrels, green bar, latch cube, black void
Wall O₂ port · Imagine input. Plate + two barrels + green bar + latch.
Isolated three-quarter lit grade of the 80k oxygen-port champion: two barrels, green bar, latch with a dark albedo smudge
Same subject · 79,991 triangles. Barrels and latch are geometry. Dark blotch on the latch is albedo, not a hole.
Grok Imagine standalone IV pump: brick body, recessed screen, button row, knob, black void
IV pump · Imagine input. Standalone brick, not a tablet slab.
Isolated three-quarter lit grade of the 80k IV-pump champion: screen well, six buttons, knob, side C-clamp
Same subject · 79,998 triangles. Screen well, six pads, knob, C-clamp. No crack islands after remesh.
Grok Imagine handheld digital thermometer: grey brick, LCD well, square button, metal probe tip, black void
Digital thermometer · Imagine input. First live factory:trellis:hatch subject.
Isolated three-quarter lit grade of the 80k thermometer champion after planted-AABB framing: whole brick and probe visible
Same subject · 80,000 triangles. Whole object after the grade camera framed the planted AABB — the prior humanoid camera sat inside the long brick. Reads as a handheld with a probe, not a photoreal clinical device.

Orchestrator pixel grade, 2026-08-14: four Imagine plates are isolated black-void product shots; four 80k champions are the same silhouettes with remesh-soft surfaces. Pulse-ox and glucometer also ran the hatch to 80k; their grade crops are either too tight (hinge cavity) or flank-on, so they stay in the ledger and off this page. claimScope: escape-hatch factory stills for compact hard-surface props. notEvidenceFor: Quest readiness, clinical accuracy, device equivalence, kit replacement, UI-XR promote, Imagine-shader smoothness. CLI: pnpm factory:trellis:hatch (commit c758a276). Evidence: .openclinxr/evidence/trellis-escape-hatch/ (gitignored champions).

Next proof

The contracts are connected. The experience still has to earn trust.

The next work is to turn the newly connected lifecycle into visible, measured product evidence. Dates are not SLAs, and nothing here promises production deployment, headset certification, exam equivalence, or clinical validation.

One visible encounter

  • Capture a single reviewed scenario moving from authoring delta through promotion, learner runtime, interruption, note phase, and faculty replay.
  • Keep every visible state tied to the immutable scenario revision and promoted bundle that produced it.

Raise runtime quality

  • Replace fixture-grade actor and garment presentation only when Model Vetting and UI-XR evidence both show the improvement.
  • Grade voice, viseme, affect, gaze, and motion synchronization as an observed performance, not merely a passing contract test.
  • Continue publishing verified room and equipment assets through the same review-gated encounter bundle.

Prove the hard boundaries

  • Exercise the learner flow on physical Quest hardware before making any readiness statement.
  • Keep faculty review, evidence provenance, and stale-identity rejection fail closed as the experience broadens.
  • Treat educational and clinical outcome validation as separate future work, not an inference from software completeness.

Public roadmap copy follows committed product evidence and the repository’s protected claim boundaries. The operational queue remains in PROJECT_STATUS.md and the project board.

Deployment posture

Local-first by default.

Development and validation run locally with deterministic providers and preconfigured assets where possible. Connected adapters exist behind explicit gates. Azure is a long-term home for API, orchestration, and admin surfaces— not a claim that production is live today.

Build model

Disciplined multi-agent execution—not vibes.

The repo runs an OpenClaw-style operating model: role charters, path scopes, leases, drift guards, and slice records. That keeps long autonomous work from inventing features outside the blueprint-factory mission. It is how we build OpenClinXR—not a separate product you install.

Evidence Docs

Deeper artifacts stay in the repository.

Marketing pages should not replace the ledger. The following links are committed factory and evidence posts for operators and auditors.