A browser-based 3D heart viewer. Two anatomical models, one driven by its baked skeletal animation, the other by a procedural cardiac rhythm sampled from the first. Pulsing galaxy backdrop for vibes.
No build step, no npm — open index.html through a local HTTP server and it runs.
This project does not redistribute the heart models. The .gitignore excludes *.glb. You need to download both yourself from Sketchfab (free account required, CC BY 4.0):
| Slot | Model | Author | License |
|---|---|---|---|
public/heart.glb |
Beating Heart | jalmer | CC BY 4.0 |
public/heart-alt.glb |
Animated Beating Heart | nitwit.friends | CC BY 4.0 |
Please respect the original authors' licenses if you fork or redistribute this project.
- Download both GLBs from the links above. Sketchfab's "glTF" download bundles the
.glbinside a.zip. For jalmer's model the file is nested atsource/Beating heart.glbinside the inner zip. - Save them in
public/:public/heart.glb(primary, cutaway with skeletal animation)public/heart-alt.glb(alt, low-poly with morph target)
- Start a local HTTP server (browsers block
fetchfromfile://):python3 -m http.server 8000
- Open
http://localhost:8000.
- Drag — rotate
- Scroll — zoom
- "Switch model" (top right) — toggle between the two hearts
- Primary heart plays its baked 1-second skeletal animation through Three.js
AnimationMixer. 21 joints, 63 channels. - Alt heart's baked clip had duplicate frames and a mid-beat reversal that read as arrhythmia, so it's bypassed. Instead, the morph target weight is driven on the CPU using a curve sampled from the primary heart's right-atrium displacement track — same rhythm, normalized to
[0, 1]. - Atrioventricular delay: each vertex of the alt heart gets a phase offset based on its Y position. Top vertices (atria region) fire first; bottom vertices (ventricles) lag by 0.25s. This produces a visible "lub-dub" wave through the mesh.
- Material restyle: alt heart gets
MeshPhysicalMaterialwith beige base color and clearcoat for wet sheen; normals are recomputed from the geometry each frame to keep shading smooth through the deformation. - Galaxy backdrop: ~4,000 stars on a sphere plus a ~9,000-point 4-arm spiral disk, written with a small
ShaderMaterialso every star has its own random base size, phase delay (0–0.18s), and pulse amplitude. They pulse in time with the heartbeat — a ripple of light propagating through the field on every beat.
All of the per-frame logic lives in main.js. The tunable constants are clustered near the top of each system.
- Three.js 0.160.0 (ESM, loaded from unpkg via import map)
- Vanilla JS / HTML / CSS
- No bundler, no transpiler, no dependencies to install
Ideas if you want to play:
- Add audio — synthesize a "lub-dub" tone driven by
sampleBeat()for true cardiac feel - ECG strip overlay along the bottom, tracing the morph weight curve
- Per-vertex hue shift on the alt heart synced to the contraction (paler at peak)
- A third model slot for any GLB you find
- Camera auto-orbit when the user hasn't interacted for a while
- Asymmetric atrial-vs-ventricular curves (right now both phases use the same shape, just time-shifted; real cardiac timing has different waveforms)
PRs welcome. Open an issue first if you want to discuss a larger change.
The code in this repo is MIT licensed — see LICENSE.
The 3D models are not covered by this license. They are distributed by their original authors under CC BY 4.0 (see Credits above).