Web Visualizer

June 4, 2026 · View on GitHub

Generate a self-contained static site that lets anyone browse a GRAIL motion library in a web browser — hover to play the MP4 preview, per-motion metadata panel, client-side name filter, dark/light mode.

No server-side code, no WebGL, no shader setup. It's just HTML + JS + a manifest JSON alongside kinematic-replay MP4s in <motion_lib>/vis/ and meta pickles from retarget / data-export.

Generate

Render kinematic-replay MP4s into <motion_lib>/vis/ first (if not already present):

conda activate sonic    # or GRAIL_SONIC_ENV override
bash grail/visualization/scripts/visualize.sh data/pickup_table

Then build the static site:

python -m grail.web_visualizer.generate_manifest \
    --motion_lib data/pickup_table \
    --output     out/viz/pickup_table

Outputs:

out/viz/pickup_table/
├── index.html
├── main.js
├── style.css
├── manifest.json            # 41 motions, 41 with video (example)
└── vis/                     # relative symlinks into motion_lib/vis/
    ├── pickup_table__apple_0__000.mp4
    └── ...

The vis/ entries are relative symlinks by default — fast + space-efficient. Pass --copy_videos if you need a self-contained directory (e.g. publishing to S3).

Serve

Any static-file host works. For a quick local check:

cd out/viz/pickup_table
python -m http.server 8000
# open http://localhost:8000/

To publish on GitHub Pages, host from a dedicated branch or drop the output into a path the Pages workflow picks up.

What the manifest contains

A stable-allowlist projection from the motion library:

FieldSourceNotes
namefilename stem of robot/*.pkl
framesrobot pkl (joint_pos / body_pos)number of frames
videovis/*.mp4omitted if video missing
meta.*meta/*.pkl (allowlisted keys)object_name, table_{pos,quat,size}

Only the allowlisted meta keys are copied into the manifest so the schema stays stable even as meta/*.pkl grows new fields.

What the viewer does

  • Renders a grid of cards, responsive down to mobile width.
  • Hover-to-play / mouseleave-to-pause. No autoplay, no audio.
  • Type-to-filter: search box performs a case-insensitive substring match against motion names (client-side, instant).
  • Dark mode honours prefers-color-scheme; no theme switch widget.