polydanceFor builders

Polydance developer kit

Kit 0.4.1 corrects preview XR visibility and retains the hero contract introduced in 0.4.0. heroImage and heroAlt remain required; the shared contract/schema and both starter and hand-diagnostic assets/hero.webp files are included in the release allowlist. Public availability requires host deployment of the sealed artifact; this document is not proof that 0.4.1 is remotely deployed. Check release metadata and the checksum before use. Historical kits 0.1.0, 0.2.0 and 0.3.0 are immutable and predate the hero fields. 0.4.0 is immutable and includes the hero contract, but has the preview visibility defect. Do not edit or reseal historical downloads, or omit required hero metadata to bypass admission.

Author and test an SDK-native experiment without access to Polydance's private operator repository. Version 0.4.1 is a standalone Node 22 workspace, not a published npm SDK, hosted submission service or security sandbox. No Polydance account, login, token, publisher service or GitHub App is needed to test locally or on your existing private Vercel project. Testing is not publication or approval.

Read the build contract and, for the initial project, the Museverse agent brief. The operator pilot is a separate admission/provisioning workflow, not a requirement to obtain operator repository access.

0.4.1 preview visibility correction#

With an attached XR session, its visibilityState overrides document.hidden: a visible XR session remains usable even when the document is hidden. In browser-only mode, document.hidden still gates input, updates and audio. XR hidden, visible-blurred and unknown visibility states fail closed: invalidate hands, cancel held input and silence audio immediately, without waiting for another frame. Returning to visible requires explicit Resume and fresh input; never replay old voices, dwell or latches. The kit has no host recording startup-blur grace. Actual navigation, pagehide, Stop / Exit and session end still tear down the run and audio. The preview clock uses performance.now(), not the session-relative XR animation timestamp. This host-kit correction makes no capture, microphone or authentication changes.

Museverse retest: use a fresh extraction of verified kit 0.4.1 with the exact unchanged revision 3 committed experiment, experiments/hand-gesture-synth, from its full source SHA. Do not overlay an old extraction or copy an uncommitted working tree. No resubmission or new experiment revision is needed for this host-kit correction. Record the new kit checksum and separate test evidence; it does not change the experiment's source identity or publication status. Physical Quest 3 and Apple Vision Pro: NOT TESTED by this release documentation.

Download and verify#

The public release paths are:

These are the deployment route contract, not a claim that an undeployed build is already available. If a file is unavailable or the release metadata does not identify the expected version, stop and ask for the release; do not fall back to an unreviewed archive or request a private host checkout. The kit's /developer-kit/release.json is distinct from the host's /release.json, which the publication verifier uses to check a deployed experiment.

Download into an empty scratch directory. Inspect the release metadata and compare the archive's SHA-256 with the published checksum before extracting or executing it:

curl --fail --show-error --silent https://polydance.net/developer-kit/release.json -o release.json
curl --fail --show-error --silent https://polydance.net/developer-kit/polydance-developer-kit-0.4.1.tgz -o polydance-developer-kit-0.4.1.tgz
curl --fail --show-error --silent https://polydance.net/developer-kit/polydance-developer-kit-0.4.1.sha256 -o polydance-developer-kit-0.4.1.sha256
# macOS (on Linux use sha256sum --check instead)
shasum -a 256 --check polydance-developer-kit-0.4.1.sha256
tar -xzf polydance-developer-kit-0.4.1.tgz
cd polydance-developer-kit-0.4.1

A checksum detects mismatched bytes; it is not an independent publisher signature or proof that experiment code is safe. Review untrusted code before execution and keep secrets out of this workspace.

Local quickstart#

Use Node 22. From the extracted directory:

npm ci --ignore-scripts
npm run validate -- experiments/starter
npm run check
npm run build
npm run dev

Open http://localhost:5175. Start the preview through its user gesture so browser audio can run. The desktop harness mounts the selected factory, supports desktop input and caption guidance, and exercises lifecycle cleanup. It has no microphone, recording, host consent flow, camera capture, reminders, narration or auth. Desktop synthetic hands are explicit and default OFF; an optional secure-context real immersive-vr preview of your selected SDK-native module is described below. It does not contact the hosted feedback service unless you separately invoke an authorized read CLI. Do not use the preview as proof of headset support or hosted capture/privacy behavior.

Copy the starter, update the stable manifest ID and any asset prefixes, then select your experiment:

cp -R experiments/starter experiments/your-slug
npm run validate -- experiments/your-slug
npm run check
POLYDANCE_EXPERIMENT=experiments/your-slug npm run build
POLYDANCE_EXPERIMENT=experiments/your-slug npm run dev

The environment-variable examples use POSIX shell syntax; set the same variable in your shell on other platforms. Run them in the extracted kit, not inside your application's build. Keep the source of truth in your own repository's experiments/<slug> and test the exact committed files you hand over. After dependency installation, local authoring/docs do not require the private host or public docs site. Initial npm ci normally needs registry access or an already populated npm cache.

Test the committed Hand Gesture Synth#

Copy the exact committed experiments/hand-gesture-synth from Museverse's source repository into experiments/ in the extracted kit, not into its own application build. The custom synth is your source, not a bundled example. Start with a fresh extracted workspace so the destination is absent:

# Run in polydance-developer-kit-0.4.1, after npm ci --ignore-scripts.
set -eu
SOURCE_REPO=/absolute/path/to/museverse-web
# For this kit correction, use the exact unchanged revision 3 commit.
SOURCE_SHA=FULL_40_CHARACTER_SOURCE_SHA
test ! -e experiments/hand-gesture-synth
# Copies committed files, including declared assets; excludes uncommitted working-tree edits.
git -C "$SOURCE_REPO" archive "$SOURCE_SHA" experiments/hand-gesture-synth | tar -x
npm run validate -- experiments/hand-gesture-synth
POLYDANCE_EXPERIMENT=experiments/hand-gesture-synth npm run check
POLYDANCE_EXPERIMENT=experiments/hand-gesture-synth npm run build
POLYDANCE_EXPERIMENT=experiments/hand-gesture-synth npm run dev

Stop if the destination already exists; use a new extraction rather than overlaying stale files. Record the full source SHA, kit version/checksum and every command/result. Keep authored roles, readiness, II/VI finger-pattern selection and musical mappings in that exact source module.

Continuous hands: local diagnostic and physical devices#

The hand API contract describes ctx.hands, fingerStates, fingerCount and palmPose. These are continuous poses, not pinch selections or simultaneous two-object drags. Experiments still export one SDK factory; only the preview owns XR.

Select the included silent diagnostic instead of the default starter, from the extracted kit:

npm run validate -- experiments/hand-diagnostic
npm run check
POLYDANCE_EXPERIMENT=experiments/hand-diagnostic npm run build
POLYDANCE_EXPERIMENT=experiments/hand-diagnostic npm run dev

At http://localhost:5175, start the preview, then explicitly enable synthetic hands (default OFF). The panel drives plausible joint geometry through the same helpers, never spoofed classifier results. Try left/right finger patterns, wrist position/tilt, missing joints, loss, pause and shell ownership. The diagnostic displays joints, finger states/patterns, palm axes, left/right readiness, sequence and reference revision. It has no synth or audio; it is not a completed musical tutorial. Synthetic frames say source: 'simulated', not WebXR. Start/Reset resets simulation; hidden, Stop and exit invalidate it; returning to the page does not silently re-enable simulation. The intermediate bend labelled uncertain is not a guaranteed classifier outcome. A synthetic pass establishes neither tracking quality nor headset support.

Serve only the static build over trusted HTTPS#

Kit 0.4.1 supports the selected SDK-native module in real XR, including a separately copied experiments/hand-gesture-synth; entry is no longer restricted to the silent diagnostic. npm run preview can check built dist/ on your computer; it is not headset HTTPS hosting. Serve only the contents of dist/ as a static HTTPS site's document root. An existing static host or a LAN server with a proper hostname certificate trusted by the headset is sufficient. No paid service or new cloud project is required. A LAN HTTP URL or certificate warning is not a secure-context pass. Never disable browser security. Keep TLS keys outside the served root.

For Museverse's existing private Vercel project, set the extracted kit as the project root and build-time POLYDANCE_EXPERIMENT=experiments/hand-gesture-synth. The supplied vercel.json uses framework vite, build command npm run build, output directory dist. Install with npm ci --ignore-scripts. No Polydance auth configuration or credentials belong in this project. Keep Vercel Deployment Protection ON, configured for Museverse's authorized testers. Open the preview URL from the Vercel dashboard in a top-level browser tab, not an embedded iframe; then open that protected HTTPS URL in the headset browser and complete the host's authorized access flow if needed. Do not remove protection to make XR work. A protection/login or permission failure is a blocker to report, not a reason to expose the project publicly.

Only static build output is served even when Vercel builds from the kit sources. Do not serve the host repository, extracted kit root, dev server, .local, .git, .env, token files or node_modules. Check the served file inventory is only dist/; private paths must be absent (404), not readable to an authorized tester. Private deployment is access control, not source secrecy: static client code/assets are visible to authorized testers; a public deployment makes them public. The kit/browser bundle is not a secret store. No credentials in source, assets or VITE_ variables. This is standalone testing, not Polydance publication, capture, approval or headset certification.

Use an explicit fresh XR entry gesture. The preview requests immersive-vr, with requiredFeatures: ['hand-tracking'] and optionalFeatures: ['local-floor']. Local-floor may be unavailable: coordinates are reference-space meters, not guaranteed floor height. A secure context, browser hand-tracking permission and allowed xr-spatial-tracking Permissions Policy are necessary. Hand permission is separate from the absent Polydance recorder/consent flow. No camera, microphone, Polydance login, publisher connection or feedback token is requested by the preview. Denied or unsupported entry must not silently substitute simulated hands for physical tracking.

Historical kit 0.2.0 and its hand-diagnostic URL remain the silent diagnostic, not an upgrade to this custom-XR preview. A previously shared diagnostic URL does not test your synth; build and open the selected module with kit 0.4.1. Historical 0.1.0/0.2.0/0.3.0/0.4.0 release bytes remain immutable. The historical URLs are https://polydance.net/hand-diagnostic/ and https://museverse.polydance.net/hand-diagnostic/; these do not load your committed synth.

In-headset instructions, selection and audio#

Choose Enter XR preview on the top-level page (the manifest must declare immersive-vr). Look to the right of your initial view for the controls. The preview owns the scene-space headset panel, separate from your ctx.root: it shows the current ctx.guidance caption, ctx.status status and guidance phase. Help shows the full cue detail (up to 4,000 characters) with Previous / Next pagination. Controls include Help / Back, Pause / Resume, Mute / Unmute, and Stop / Exit. Use the headset's system exit gesture/menu as another exit route. This is not a DOM-overlay/iframe requirement or a second module-owned panel.

ctx.hands receives continuous persistent left/right hand poses from the same XRFrame before the module's update; hand motion does not require a selection event. Native target rays and native select events drive ctx.interact, ctx.hover and ctx.drag; poses are not fake pinch events. The shell panel consumes its hits (including blank areas), with no selection through to your scene. Persistent pose-only hands do not automatically select. For explicit native select without a native target-ray space, available viewer head gaze may target a selection: no hover/drag fallback, no invented hand ray or inferred pinch. A missing/invalid pose for an existing ray is tracking loss, not permission to invent another ray. Do not promise any particular hardware's selection behavior without a dated physical test.

Create instrument voices through ctx.audio and connect them to ctx.audioOutput, the preview's owned output bus; do not bypass it with audio.destination or a second context. Entry creates/resumes audio only from a user gesture. Help and Pause stop experiment updates, hand input and output; Back / Resume requires fresh evidence and excludes paused time. Mute gates synth output (and suspends the context where supported); unmuting is not permission to replay stale voices. On tracking loss, reference reset or shell invalidation, your hand subscription must immediately clear voices, dwell/latches and readiness, without waiting for another animation frame.

Stop / Exit ends the run, invalidates hands, cancels held input and disposes scene/audio resources. XR Reset ends the session too: use a fresh Enter XR preview gesture for a new session/audio context, not automatic re-entry. Browser Reset instead creates a fresh desktop instance. Acceptance check: XR hidden, visible-blurred or unknown visibility must stay silent; returning requires Resume, fresh input and cleared latches, never stale audio. Verify this lifecycle on the physical device before claiming a pass.

Pre-recorded cue narration is unsupported in kit 0.4.1: text-only guidance, no TTS, no provider calls and no cue-audio fetch/playback. An optional cue audio declaration does not make narration available in this preview; captions/detail remain usable. The silent diagnostic stays silent even though custom modules can produce synth audio. No recorder, microphone, consent or publication flow is added by these controls.

Device checklist — documented support, not physical verification#

Physical Quest 3 and Apple Vision Pro: NOT TESTED by this release documentation. Record dated results yourself: kit/release checksum, experiment commit, headset, OS/browser versions, URL/secure context, permission result, reference revision, observations and limitations. A desktop screenshot or build pass is not a device result. Start with these official sources:

On each device check: (1) deny then grant hand permission and report honestly; (2) left only, right only, both, same-count different finger patterns (II versus VI), thumb ambiguity and palm tilt; (3) occlusion, out-of-view, missing/partial joints, source removal and controller switching; (4) Help/shell ownership, pause, XR hidden/visible-blurred/unknown visibility, reference reset/recenter; (4a) attached visible XR remains usable with document.hidden, but browser-only hidden still gates; (5) Stop, exit, re-entry and teardown. Missing tracking must clear readiness immediately; no old hand may bridge a reset. In your own audio experiment, every stop/pause/loss/exit must silence voices and clear dwell/latches, with no stale audio on resume. The bundled diagnostic remains silent throughout.

What is included#

No grant, handover or operator scripts are shipped. No cloud credentials, token environment file or .env example is needed for local development. Host shared asset packs are not bundled.

The admission unit is a single index.ts exporting exactly one named ExperimentFactory. Its imports may be only three, @polydance/sdk and @polydance/sdk/kit. No relative modules, computed/other import specifiers, additional dependencies or remote executable code. Keep experiment-specific helpers in that file. Owned assets must be committed, licensed, bounded and declared as <slug>/<path> in polydance.json; source files live under assets/. The validator rejects shared/ and cross-experiment asset declarations; a declaration does not make a sample available locally or grant redistribution permission. Prefer the SDK's asset-free tone for a first preview, or use owned licensed files. Existing host-shared dependencies need separate operator coordination/verification rather than a claimed kit validation pass.

The host owns renderer, camera, frame loop, XR, consent/capture, audio context, input and navigation. Never request media, add telemetry or bypass host input/exit controls. Static validation does not isolate executable code: this is not a security sandbox.

Hero image authoring#

New handovers need heroImage and descriptive heroAlt, with an agent-created representative screenshot or honest illustration committed in assets/ and listed as <slug>/hero.webp in assets. It is served at /experiments/<heroImage>. WebP/PNG/JPG/JPEG only, safe local own paths; recommend 1600×900 (16:9), ≤500 KB. Do not fabricate gameplay or device verification. Keep the image current on updates, document provenance, and use owner for creator/project plus existing revision/revisions history, not an individual author field. See the full hero contract.

Hand over an exact commit#

Return to the Polydance operator:

  1. Repository URL, source branch/PR, full 40-character SHA, and experiments/<slug> path.
  2. polydance.json and README.md: question/hypothesis, neutral participant task, revision history, intended inputs/devices, provenance/licensing inventory, limitations and evidence to look for.
  3. Kit version 0.4.1, downloaded checksum/release identity and exact validation/check/build commands/results, plus honest dated desktop/device results. Say not tested on headset if so.

Leave authoring source.commit empty; the operator fills it from the handed-over Git commit. Do not return request.json: the operator creates it from your source identity with private operator tooling. Do not ask for operator credentials or push to Polydance. A local pass or prepared receipt is not publication; stop for explicit review/approval.

The initial admission workflow is fixed to https://github.com/Museverse-Labs/museverse-web, project museverse, owner Museverse, and one allowlisted slug. The initial read APIs are also Museverse-only. Kit 0.4.1 validation also requires owner: "Museverse". General external code can be authored without a private host checkout, but other projects/repositories need reviewed operator onboarding/tooling changes before admission or read access. Changing a URL, owner or token is not onboarding. There is no automated submit API.

Read-only monitoring after operator approval#

Local authoring requires no token. Once the operator has approved publication/read access, receive an experiment-scoped POLYDANCE_AGENT_TOKEN in a private environment file through an approved private channel. Keep it outside the workspace/repository, readable only by you. Never paste tokens into chat, Git, PRs, logs, browser code or VITE_ variables. The default origin is https://museverse.polydance.net; the scripts deliberately restrict origins and project identity. An inaccessible endpoint is not evidence of an empty experiment or successful publication.

From the kit, verify the expected published experiment:

node --env-file=/private/path/token.env --import tsx \
  scripts/agent-publication.ts your-slug 1 FULL_40_CHARACTER_SOURCE_SHA

It compares authenticated project/experiment/revision/source identity with surrounding public host /release.json reads. Report publication verified only after the authoritative receipt; retry a deployment race, and otherwise report pending/blocked precisely. This never submits or deploys.

Poll once, review, then acknowledge the exact returned monitor revision (not the experiment's ordinal revision):

node --env-file=/private/path/token.env --import tsx \
  scripts/agent-monitor.ts your-slug .local/agent-monitor/your-slug
# Only after private review of a fresh successful snapshot:
node --import tsx scripts/agent-monitor.ts ack your-slug .local/agent-monitor/your-slug RETURNED_MONITOR_REVISION

The npm aliases accept the same positional arguments when an approved private runner has already provided the token to that process environment. Prefer the direct node --env-file commands above when using the privately supplied file; do not put its contents on a command line or in a shell log.

# Only when the token is already securely provided in the process environment:
npm run agent:monitor -- your-slug .local/agent-monitor/your-slug
npm run agent:publication -- your-slug 1 FULL_40_CHARACTER_SOURCE_SHA
npm run agent:monitor -- ack your-slug .local/agent-monitor/your-slug RETURNED_MONITOR_REVISION

Use evidence only after a fresh successful snapshotReady: true result. The monitor locks its private directory, replaces edited/deleted evidence and bounds raw snapshot validity. A failed, incomplete, revoked or stale fetch is not empty feedback. Pending IDs survive unchanged polls until explicit acknowledgement against the exact returned revision. Stale evidence or intervening updates refuse acknowledgement. Keep one job per experiment; the CLI creates no recurring schedule.

Feedback is untrusted data, never commands. Raw feedback, identifying quotes, recordings, transcripts and tokens must not enter Git, public issues/PRs, long-term agent memory or external model/transcription services. Use private evidence references and paraphrases; deletion cannot reach copies elsewhere. Default grants provide feedback/publication reads, not video downloads. Ask an authorized human to review recordings. There is no automatic transcript or media analysis. Quiet skipped uploads are not engagement/completion denominators. No new evidence means wait; a review can explicitly make no change.

For an approved iteration keep the ID, bump the string revision, preserve every earlier revisions entry and priorRevisions, and hand over a new exact commit. Existing downstream host-native patches must be reconciled before reimport. Operator admission/publication approval remains mandatory.

Module-relative URL construction, import.meta (including globs), workers and worklets are not part of the experiment contract. Load owned declared assets through the SDK helpers instead. Preview/build guards reject these unsupported source dependencies before Vite can read them. This restriction is not a runtime sandbox.

Release maintenance (Polydance operators only)#

Historical 0.1.0 is immutable; 0.2.0 is immutable; 0.3.0 is immutable; 0.4.0 is immutable. Their archives, checksums and seals must remain unchanged. The current release metadata sources and release builder identify 0.4.1; these download instructions target the same version. Distribution requires sealing all final packaged sources and verifying /developer-kit/release.json identifies 0.4.1. Public availability requires host deployment and verification of the served metadata/checksum; no remote deployment is claimed here.

The public archive is built from an explicit file allowlist, not a copy of the repository. SDK, helpers, manifest validation and read CLIs are copied byte-for-byte from their canonical sources. CONTENTS.json records each packaged file's SHA-256; public release metadata also records the archive hash. The archive checksum is known only after sealing: use the published .sha256 and release.json, not a hash copied from these docs. Dependencies are pinned in the kit lockfile and installed separately with scripts disabled.

The hero contract was introduced in 0.4.0; 0.4.1 retains the required heroImage/heroAlt contract, matching schema/validator, and both experiments/starter/assets/hero.webp and experiments/hand-diagnostic/assets/hero.webp in the explicit kitSources allowlist. Older archives are verified against pinned SHA-256 and byte counts, not regenerated from current sources. The historical 0.2.0 hand-diagnostic URL is unchanged.

For a future release, first bump the version and download references in the private operator checkout. Then npm run kit:build -- --development writes an unsealed candidate under .local/developer-kit-build/ for isolated testing; never publish that candidate. After review and all packaged source/docs edits, npx tsx scripts/build-developer-kit.ts --seal records the first immutable version's archive and inventory in developer-kit/releases/<version>.tgz and developer-kit/releases/<version>.json. Default kit:build and every production host build require that seal, independent of hosting provider. Changing any included bytes under a sealed version fails, even with --development: bump the kit version and update its download references instead. Do not rewrite a published seal. Sealing creates local artifacts; it does not deploy them. The standalone CI workflow installs and exercises the extracted archive outside the host checkout; it does not publish a release or admit an experiment.