Small study apps on one site, one route per quiz:
| Route | Quiz |
|---|---|
/ |
The shelf: links to every quiz |
/anatomy/ |
3D anatomy flash cards (below) |
/colors/ |
Color Chips: the 139 CSS named colors, name ↔ RGB |
Adding a quiz: create <name>/index.html (plain HTML, or a Solid/TS app like
anatomy/), add it to build.rollupOptions.input in vite.config.ts, and copy a
card on the shelf in /index.html. Static data for a quiz goes in public/<name>/,
so it is served beside that quiz's page.
npm install
npm run dev # http://localhost:5173 (/, /anatomy/, /colors/)
npm run build # rebuilds anatomy's db.json, typechecks, bundles all pages to dist/A 3D anatomy study aid for personal trainers and physical-therapy students. The camera flies to a single muscle or bone, ghosts the anatomy around it, and asks you to name it. Reveal shows the name, etymology, and for muscles the origin, insertion, actions and innervation. You grade yourself (Again / Hard / Good / Easy) and the FSRS-5 spaced-repetition scheduler decides when you see it again.
- 348 cards: 123 bones + 225 individual muscles (rectus femoris, not "quads"), graded into 5 difficulty levels. New cards come from the easiest level first; each level unlocks once 70% of the one below has graduated.
- Left/right copies are picked at random each time.
- Lookup mode (top-bar switch): browse every muscle and bone by region, or
search by name, alternate name, region or etymology word (
/focuses search). Picking one flies the camera there and shows its full card, with a Left/Right toggle and "Part of" / "Made of" links (e.g. Anterior deltoid ↔ Deltoid). You can also tap any structure on the body to identify it. - Free orbit (drag), zoom (pinch / wheel), pan (two fingers / right-drag), re-center (R).
- Keyboard:
Spacereveal,1–4grade. - Progress lives in
localStorage. The dashboard shows review history, a due-date forecast, per-level progress and settings. From there you can include/exclude or force-unlock levels, reset a single level, or (danger zone) reset everything.
Pushing to main/master deploys to GitHub Pages via .github/workflows/deploy.yml
(enable Settings → Pages → Source: GitHub Actions once).
All state lives in one arena (anatomy/src/arena.ts), allocated once at boot. It holds
typed-array stores indexed by part or card id (struct-of-arrays), the review log
ring buffer, the camera rig, the input accumulators, a command queue and every
three.js object. Systems (anatomy/src/systems/*) are plain functions that run every
tick in a fixed order inside one requestAnimationFrame loop (anatomy/src/loop.ts):
1 input pointer deltas -> camera goals; taps recorded
2 pick lookup mode: tap -> BVH ray cast -> selection request
3 commands UI command queue -> grading / settings / mode / resets
4 session study: next card (FSRS), side, camera view; lookup: apply selection
5 resize canvas + visible-area projection offset
6 camera damp toward goals
7 focus target / ghosted / opaque goal per part (vicinity + camera ray tests)
8 fade animate per-part state -> GPU state texture
9 render draw only if something changed
10 persist throttled localStorage save
11 ui sync push a snapshot to Solid when ui.version changed
DOM listeners only record raw input into the arena; the Solid UI only pushes commands. There are no callbacks driving app logic.
Rendering. The whole body is one merged BufferGeometry (uint16-quantized
positions, int8 normals, ~380k triangles, 2.7 MB gzipped) drawn by two meshes:
an opaque pass and a translucent "x-ray" pass. A per-vertex partId attribute
indexes a 1-row DataTexture holding each part's highlight and opacity, so
re-focusing the scene means writing bytes into a typed array.
data/anatomy/cards/*.json: the card database, hand-authored per region. Schema is indata/anatomy/CARD_SCHEMA.md.data/anatomy/model-parts.json: mesh names, sides and bounds produced by the model build.scripts/anatomy/build-db.mjsmerges and validates both intopublic/anatomy/db.json. It fails if a card names a mesh that doesn't exist, and appliesSOURCE_FIXES: corrections for meshes the Z-Anatomy source mislabels (its upper and lower trapezius are swapped).data/anatomy/views.json: each card's camera direction, picked bynpm run anatomy:views. It ray-casts the real triangles from many angles and keeps the one that shows the most of the structure unobstructed.
Open the app with ?audit and call __audit.show(cardIndex) in the console. It
isolates that card's meshes and snaps the camera to them. Every muscle card was
rendered this way and checked by eye: each highlight is exactly the muscle (or set
of muscles) it names. Multi-mesh cards combine only the anatomical heads or parts
of one muscle (pec major = clavicular + sternocostal + abdominal part).
- Download Z-Anatomy's Blender template (
Z-Anatomy.zipfrom https://github.com/Z-Anatomy/The-blend) and unzip it. pip install bpy(Python 3.11), thenpython scripts/anatomy/blender-export.py Z-Anatomy/Startup.blend <rawDir> 0. This exports every bone and muscle mesh (excluding fascia, bursae, sheaths and ligaments), with.l/.rsides.node scripts/anatomy/build-model.mjs <rawDir> 380000simplifies with meshoptimizer to the triangle budget, quantizes, and writespublic/anatomy/model/body.bin.gz+data/anatomy/model-parts.json.
- 3D models: Z-Anatomy (CC BY-SA 4.0), https://www.z-anatomy.com. Derived from
BodyParts3D, © The Database Center for Life Science (CC BY-SA 2.1 JP / CC BY 4.0).
The files in
public/anatomy/model/are a simplified adaptation and are distributed under CC BY-SA 4.0. - Scheduling: FSRS-5 algorithm and default weights from the open-spaced-repetition project.
- Card text was written for this project. It's a study aid, so check it against your course texts.