Skip to content

Commit 1b8d1de

Browse files
committed
docs: launch-ready README with rendered GIFs + viewer captureFrame API
- README: live playground link, 3 motion GIFs (deadlift/squat/lateral raise) captured from the real renderer, examples section, safety story (ROM clamping + eval invariants), full package table - movit-render: Viewer.captureFrame() — synchronous PNG frame export (powers GIF capture, future embeds/posters) - playground exposes window.__movitViewer for capture/e2e tooling
1 parent 050cfce commit 1b8d1de

6 files changed

Lines changed: 67 additions & 4 deletions

File tree

README.md

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,21 @@
44
Movit gives them a way to <i>show movement</i> — exercises, physiotherapy, posture —<br/>
55
as a tiny human-readable language that renders to an animated 3D figure in the browser.</p>
66

7+
<p align="center">
8+
<a href="https://www.posecode.org/play"><b>▶ Live playground</b></a> ·
9+
<a href="spec/SPEC.md">Language spec</a> ·
10+
<a href="spec/examples">Examples</a> ·
11+
<a href="packages/movit-mcp">MCP server</a>
12+
</p>
13+
14+
<table align="center">
15+
<tr>
16+
<td align="center"><img src="docs/media/deadlift.gif" width="230" alt="Deadlift rendered from .movit text"/><br/><sub><code>hips: hinge 70</code> — deadlift</sub></td>
17+
<td align="center"><img src="docs/media/squat.gif" width="230" alt="Body-weight squat rendered from .movit text"/><br/><sub><code>knees: flex 95</code> — squat</sub></td>
18+
<td align="center"><img src="docs/media/lateral-raise.gif" width="230" alt="Lateral raise rendered from .movit text"/><br/><sub><code>shoulders: abduct 90</code> — lateral raise</sub></td>
19+
</tr>
20+
</table>
21+
722
---
823

924
## Why
@@ -46,15 +61,47 @@ movit exercise "Body-weight squat"
4661

4762
## Try it
4863

64+
**No install:** open the [live playground](https://www.posecode.org/play),
65+
pick an example, edit the text, watch the figure move. Hit **Copy LLM prompt**
66+
to get a system prompt that teaches ChatGPT/Claude to write Movit for you —
67+
or wire up the [MCP server](packages/movit-mcp) so your agent authors,
68+
validates, and renders movements natively.
69+
70+
**Locally:**
71+
4972
```bash
5073
npm install
5174
npm run dev # opens the playground (Vite) at http://localhost:5173
52-
npm test # parser + renderer test suites
75+
npm test # parser + renderer + eval test suites
76+
npm run eval # fidelity scorecard: geometric invariants over every example
77+
```
78+
79+
## Examples
80+
81+
Fourteen ready-to-paste movements live in [`spec/examples`](spec/examples)
82+
squat, deadlift, push-up, biceps curl, lateral raise, forward fold, roll-down,
83+
chair pose, side bend, spinal twist, neck rotation, shoulder stretch, and two
84+
posture resets. A hip hinge is one line:
85+
86+
```movit
87+
step "Hinge down" 2s ease-in-out:
88+
hips: hinge 70 # closed-chain: torso tips over planted feet
89+
knees: flex 20
90+
ground-lock: feet
91+
cue "Push the hips back, chest up, flat back"
5392
```
5493

55-
In the playground: pick an example, watch it animate, edit the text live, and
56-
hit **Copy LLM prompt** to get a system prompt that teaches ChatGPT/Claude to
57-
write Movit for you.
94+
## How Movit stays honest
95+
96+
Two safety layers ship with the language:
97+
98+
- **ROM clamping** — every angle is hard-clamped to healthy range-of-motion
99+
tables before rendering; a hallucinated `knee: flex 200` renders at 144°
100+
with a warning, never an impossible joint.
101+
- **Fidelity evals**[`movit-eval`](packages/movit-eval) re-runs the real
102+
parser → FK → ground-lock pipeline headlessly and scores geometric
103+
invariants ("a deadlift pitches the torso ≥ 55° with a flat back and
104+
vertical shins"). Every example must pass every invariant in CI.
58105

59106
## Packages
60107

@@ -64,6 +111,9 @@ write Movit for you.
64111
| [`movit-render`](packages/movit-render) | IR → animated low-poly mannequin (Three.js), forward kinematics + ground-lock CCD IK. |
65112
| [`movit-share`](packages/movit-share) | Encode a `.movit` doc to a URL-safe token so a movement travels as a link. Pure, dependency-free. |
66113
| [`movit-mcp`](packages/movit-mcp) | MCP server: lets an LLM agent author, ROM-validate, and get a render link for a movement — natively. |
114+
| [`movit-eval`](packages/movit-eval) | Fidelity harness: headless kinematic probing + biomechanical invariant scoring. |
115+
| [`movit-language`](packages/movit-language) | Editor smarts (completion, hover docs, diagnostics) shared by CodeMirror and the LSP. |
116+
| [`movit-lsp`](packages/movit-lsp) | Language Server Protocol server + [VS Code extension](editors/vscode). |
67117
| [`playground`](playground) | Live editor + 3D viewport + warnings + the LLM prompt + shareable links. |
68118

69119
The protocol and both libraries are **MIT-licensed** — the open core. See

docs/media/deadlift.gif

994 KB
Loading

docs/media/lateral-raise.gif

259 KB
Loading

docs/media/squat.gif

660 KB
Loading

packages/movit-render/src/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ export interface Viewer {
4040
get duration(): number;
4141
get time(): number;
4242
getTimeline(): TimelineInfo | null;
43+
/**
44+
* Render the current time synchronously and return the frame as a PNG data
45+
* URL. Works without preserveDrawingBuffer because the read happens in the
46+
* same task as the render (no buffer swap in between). Powers GIF/poster
47+
* export and headless capture tooling.
48+
*/
49+
captureFrame(): string;
4350
onPhase(cb: (info: ViewerPhaseInfo) => void): void;
4451
onTick(cb: (time: number, duration: number) => void): void;
4552
onLoop(cb: () => void): void;
@@ -291,6 +298,10 @@ export function createViewer(
291298
segments: timeline.segments,
292299
};
293300
},
301+
captureFrame() {
302+
frame();
303+
return renderer.domElement.toDataURL("image/png");
304+
},
294305
onPhase(cb) {
295306
phaseCb = cb;
296307
},

playground/src/main.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ const tabEditor = $<HTMLButtonElement>("tab-editor");
3838
const tabViewer = $<HTMLButtonElement>("tab-viewer");
3939

4040
const viewer = createViewer(canvas);
41+
// Exposed for capture/e2e tooling (frame capture drives README GIFs).
42+
(window as unknown as Record<string, unknown>).__movitViewer = viewer;
4143
let scrubbing = false;
4244
let repeat = 1;
4345
let rep = 1;

0 commit comments

Comments
 (0)