From mudra
Generate a working Mudra Band interactive app preview as a single-file HTML. Use when the user describes a Mudra-controlled experience (gesture, pressure, navigation, IMU, SNC), wants to prototype a Mudra Companion app, or asks to build/preview a Mudra app.
How this skill is triggered — by the user, by Claude, or both
Slash command
/mudra:mudra-previewThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Generate a complete, working single-file HTML app controlled by Mudra Band signals.
assets/ar-menu.htmlassets/document-scroller.htmlassets/drum-machine.htmlassets/emg-visualizer.htmlassets/generative-art.htmlassets/gesture-assistant.htmlassets/gesture-speech.htmlassets/hands-free-desktop.htmlassets/model-rotator.htmlassets/mudra-duel.htmlassets/mudra-monitor.htmlassets/mudra-ultimate-template.htmlassets/music-sequencer.htmlassets/neural-pong.htmlassets/neural-snake.htmlassets/presentation-controller.htmlassets/pressure-painter.htmlassets/runner.htmlassets/smart-home.htmlassets/space-invaders.htmlGenerate a complete, working single-file HTML app controlled by Mudra Band signals.
Mandatory feature (v1.1.0, updated v1.4.0): Every generated app MUST
include a Manual/Mudra Mode toggle as defined in references/promt.md
§ "Mode Toggle (Manual / Mudra) — Required". Manual is the default; Mudra
opens a single WebSocket lazily and disables the simulator panel so signals
come only from the band.
Removed in v1.4.0: Do not render a "Band disconnected" overlay, toast, or any other separate disconnect alert. Connection state is communicated only through the existing connection-status pill (which shows "Connecting…" / "Connected" / "Disconnected"). No extra notice, no banner, no modal.
Branding (v1.4.0): The footer/badge text MUST be exactly "Created by Mudra" — never "Created with Mudra Studio" or any other variant.
Mandatory feature (v1.2.0): Connection state MUST reflect the band, not
the WebSocket. The Companion service accepts socket connections even when no
band is paired, so flipping to "Connected" on ws.onopen is a lie. Every
generated app MUST send {command:"get_status"} on open and poll it every
2 s while in Mudra mode, and only show "Connected" when the response has
data.device.state === "connected". See references/promt.md §
"Disconnect detection — band state via get_status polling (mandatory)".
The canonical protocol is in references/agent_protocol.json (v2.0).
Read the full instructions from references/promt.md inside the skill base directory. That file contains the complete protocol contract, signal compatibility rules, the Mode Toggle architecture (mandatory), build defaults, and sample catalog — follow all of it.
Infer intent from the user's description (or the args passed to this skill). Fill gaps with smart defaults. Ask only if there is genuine ambiguity (e.g., gesture vs pressure, navigation vs nav_direction, or directional motion vs IMU+Biometric bundle).
Select the best-matching template from assets/ inside the skill base directory. Use the selection rule from references/promt.md (motion mode → interaction pattern → signal overlap).
Generate the app as a single self-contained HTML file:
references/promt.md (protocol contract, signal compatibility, mock WebSocket, theme, fallbacks)Every generated app MUST include a compact, always-visible simulator panel with one button set per subscribed signal. The user must be able to trigger every signal with a click — so they can test without the band, or alongside it.
Layout rules
Tap, 2Tap, Twist, ↑, ↓, Roll L)..btn style from the chosen template.Buttons per signal (only render the groups for signals the app actually subscribes to)
| Signal | Buttons |
|---|---|
gesture | Tap, 2Tap, Twist, 2Twist |
nav_direction | ↑, ↓, ←, →, Roll L, Roll R |
navigation | ↑, ↓, ←, → (each click emits one delta event of ±8) |
pressure | Slider 0–100% (or − / + buttons if horizontal space is tight) |
button | Press, Release |
imu_acc | Tilt X, Tilt Y, Tilt Z (each fires a 5-frame burst at ±2 m/s²) |
imu_gyro | Rot X, Rot Y, Rot Z (each fires a 5-frame burst at ±10 deg/s) |
snc | Spike (injects a burst of elevated samples on all 3 channels) |
How each button must fire
gesture buttons → ws.send(JSON.stringify({ command: 'trigger_gesture', data: { type: '<name>' } })). This works both when connected to the real band (round-trips through the service) and when the mock is active (mock echoes it back).ws.onmessage would dispatch. Example:
function simDir(direction) {
// same path the real signal would take
handleNavDirection({ direction, timestamp: Date.now() });
hudDir.textContent = direction;
}
Always update the telemetry HUD from the sim button, same as the real signal would.Save the output to preview/<concept-name>.html in the project root (create the preview/ directory if it doesn't exist). Use a short kebab-case name derived from the concept (e.g., preview/drum-machine.html).
Report the file path so the user can open it in a browser immediately.
Unless the user explicitly asks for a different signal, every generated app MUST restrict itself to at most these signals (subject to the exclusivity rules below):
pressure or gesture — never both. gesture and
pressure are mutually exclusive.
gesture): use tap OR double_tap
— never both together unless the user explicitly names both (e.g.,
"use single tap for X and double tap for Y").
tap → frequent/lightweight: counter increment, navigation step,
toggle, next/back, select, trigger. Default choice.double_tap → only when the user explicitly requests it by name.
Never pair it with tap as a default two-action scheme.tap.tap with twist — not with double_tap.
double_tap is reserved for explicit user requests only.nav_direction or
navigation, never both.Rules:
gesture only and skips the directional
signal).gesture and pressure are mutually exclusive — pick one
interaction model per app. Tap/twist concepts → gesture. Analog
concepts (volume, brush, throttle) → pressure.nav_direction and navigation are mutually exclusive per app —
pick the one that fits the interaction (discrete swipes →
nav_direction; continuous cursor/scroll → navigation). Never wire
both into the same app.imu_acc + imu_gyro + snc) is
inseparable. If the concept needs any one of them, subscribe to all
three. The bundle is mutually exclusive with navigation and
nav_direction — pick directional motion OR the IMU+Biometric bundle,
never both.twist, double_twist, etc.) and other
signals (button, imu_acc, imu_gyro, snc, battery) are
off by default. Only include them when the user's prompt names
them, names a synonym from the Signal Inference table in
references/promt.md § "Signal Inference Reference", or describes
an interaction that genuinely cannot be expressed with the defaults
(e.g., "tilt to steer" → IMU+Biometric bundle; "hold to charge" →
button).ws://127.0.0.1:8766 (bare URL — NOT /events)references/promt.md § "Mode Toggle (Manual / Mudra) — Required"{ "command": "subscribe", "signal": "<name>" } — singular signal, never signals, never an arraynavigation+button) / Direction (nav_direction) / IMU+Biometric (imu_acc+imu_gyro+snc, always all three together)imu_acc, imu_gyro, snc always subscribed together — never partially. The bundle is mutually exclusive with navigation and nav_direction.gesture and pressure are mutually exclusive — never combine thembutton and battery combine freely (subject to the Pointer XOR — button belongs to Pointer mode and never combines with nav_direction)step = 3, sim button ±3, cursor multiplier 0.002. Raise only when the prompt explicitly asks for fast/snappy movement. See references/promt.md § "Navigation sensitivity defaults".references/agent_protocol.json (v2.0)Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub wearable-devices/mudra-skills --plugin mudra