Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified docs/media/jumping-jacks.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 64 additions & 0 deletions docs/superpowers/specs/2026-07-12-mixamo-hero-hook-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Mixamo Hero Hook — Design

**Date:** 2026-07-12
**Status:** Approved

## Goal

Use one Mixamo mocap clip (**jumping jacks**) as the "eye-candy hook" on the
three primary marketing surfaces, while the procedural DSL stays the source of
truth everywhere a visitor actually engages.

Role of Mixamo = **pure attention-grabber**, not a claim about tool output. It
is delivered through the real `clip "<name>"` DSL directive (clip-back), so the
document honestly declares what it plays; deleting that one line drops it to
procedural.

## Scope — three surfaces, one directive

All three read the same `spec/examples/jumping-jacks.posecode` document and the
same `SHOWCASE_CLIPS` map, so a single directive lights up all of them:

1. **Landing hero** (`playground/index.html` + `landing.ts`) — already wired to
`clips: SHOWCASE_CLIPS`; needs no code change once the directive is present.
2. **Playground default movement** — first thing shown on `/play`.
3. **Main README GIF** (`docs/media/jumping-jacks.gif`) — re-rendered from the
clip via the existing capture script (it drives the real playground).

**Out of scope / unchanged:** the sub-three README GIFs (`deadlift`, `squat`,
`lateral-raise`) stay **procedural**. No other preset changes.

## Changes

1. **`spec/examples/jumping-jacks.posecode`** — add `clip "jumping-jacks"` so the
doc plays the retargeted Mixamo motion when the skinned figure is active.
2. **`playground/public/clips/`** — rename the re-uploaded Mixamo file
`Jumping Jacks.fbx` → `jumping-jacks.fbx` to match the `SHOWCASE_CLIPS` key
(`jumping-jacks → /clips/jumping-jacks.fbx`). Files remain gitignored /
CDN-served; this only reconciles local + CDN naming.
3. **`playground/src/main.ts`** — introduce `DEFAULT_PRESET_ID = "jumping-jacks"`
and boot from it (approach A), leaving the library list order unchanged.
4. **Re-render** `docs/media/jumping-jacks.gif` via
`node scripts/capture-gifs.mjs jumping-jacks`.

## Honesty / consistency guardrail

Only the jumping-jacks doc declares a clip. Every other preset, and the sub-3
GIFs, render procedurally — so the moment a visitor browses the library or edits
the default doc, they see real DSL output. The default doc visibly contains the
`clip "jumping-jacks"` line, so the mechanism is transparent, not hidden.

## Verification

- Browser preview: confirm the landing hero and the playground default both play
the Mixamo jumping-jacks loop (not the procedural fallback), and that the loop
reads cleanly through the neutral standing pose.
- Confirm removing the `clip` line falls back to procedural.
- Inspect the re-rendered GIF before committing.

## Risk / history note

Last session the landing jumping-jack "looked too bad" — that was the
*procedural* fallback after clips were dropped (`26a5e68`). Re-adding the Mixamo
clip is expected to be the fix. If the mocap loop itself reads poorly, adjust
loop/framing before re-rendering the GIF.
12 changes: 9 additions & 3 deletions playground/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@ import {
import type { PosecodeEditor } from "./editor.js";
import { PRESETS } from "./presets.js";
import { SHOWCASE_CLIPS } from "./clips.js";

// The movement shown on first open (no shared link). Jumping jacks plays the
// Mixamo showcase clip, so the playground greets visitors with polished mocap
// while every other preset — and the editor once you engage — stays procedural.
// Falls back to the first library entry if the id is ever removed.
const DEFAULT_PRESET = PRESETS.find((p) => p.id === "jumping-jacks") ?? PRESETS[0]!;
import { renderWarnings } from "./warnings.js";
import llmPrompt from "../../spec/llm-authoring.md?raw";

Expand Down Expand Up @@ -506,9 +512,9 @@ if (sharedSource) {
initialDoc = sharedSource;
intro.hidden = true;
} else {
initialDoc = PRESETS[0]!.source;
currentPresetId = PRESETS[0]!.id;
libCurrent.textContent = PRESETS[0]!.label;
initialDoc = DEFAULT_PRESET.source;
currentPresetId = DEFAULT_PRESET.id;
libCurrent.textContent = DEFAULT_PRESET.label;
}

// Boot the two heavyweights (CodeMirror editor + Three.js renderer) after the
Expand Down
1 change: 1 addition & 0 deletions spec/examples/jumping-jacks.posecode
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
posecode exercise "Jumping jacks"
rig humanoid
clip "jumping-jacks"
pose start = standing

step "Out" 0.5s settle:
Expand Down
Loading