SDK Guide Get SDK

VIVERSE Me SDK Guide

Embed VIVERSE Me avatar selection and creation into your own site or game. This guide organizes every SDK feature, setting, event, and the optional Avatar Controller into searchable categories. Use the search box to jump straight to a keyword, attribute, or method.

Overview

The VIVERSE Me SDK lets a third-party host site embed the Happy Path character flow so visitors can pick or create an avatar without calling account APIs directly from the host page.

A host page owns its own app experience and loads the SDK as an embed layer. In Happy Path mode, the SDK owns the VIVERSE Me iframe, character picker, Avatar Creator launch, developer-owned anonymous storage, and avatar asset API calls. The stable public entry is https://viverse-me.com/sdk.js.

What the host page should do

  • Load https://viverse-me.com/sdk.js.
  • Pass a registered SDK Integration ID with data-ac3-partner-id or partnerId.
  • Set data-ac3-mode="sdk-happy-path".
  • Listen for SDK lifecycle and avatar events.
  • Use returned signed URLs for immediate preview only.
  • Keep durable avatar state in a backend, not in browser-only storage.
Do not call account routes such as /api/ac3/me, /api/ac3/files, /api/ac3/download-url, /api/ac3/upload-vrm, or /api/ac3/claim-draft directly from the host page. The embedded SDK and VIVERSE Me pages own those flows.

Quick Start

Add a target element and the SDK script, then listen for avatar selections.

<div id="viverse-me-button-slot"></div>
<div id="host-sign-in-button-slot"></div>
<script
  src="https://viverse-me.com/sdk.js"
  data-ac3-mode="sdk-happy-path"
  data-ac3-target="#viverse-me-button-slot"
  data-ac3-host-sign-in-target="#host-sign-in-button-slot"
  data-ac3-partner-id="partner_..."
  data-ac3-label="Start"
  async
></script>

Then listen for avatar selections:

window.addEventListener('viverse-me:avatar-selected', (event) => {
  const avatar = event.detail.avatar;

  hostScene.loadAvatar(avatar.vrmUrl);
  partnerApi.saveSelectedAvatarReference({
    partnerId: avatar.partnerId,
    userId: avatar.userId,
    tenantId: avatar.tenantId,
    key: avatar.key
  });
});
vrmUrl and thumbnailUrl are short-lived signed URLs for immediate preview. Store stable reference fields such as tenantId, key, and partnerId for durable use.

Getting your snippet

This public guide shows a partner_... placeholder everywhere a real SDK Integration ID is required. You never paste your ID from documentation — you copy a ready-made snippet.

  1. Sign in at VIVERSE Me and open Account Settings → My SDK.
  2. Add the website origins where the SDK may run under Allowed Websites.
  3. Use Copy in the SDK Code Snippet box. The copied snippet already contains your SDK Integration ID.
  4. Paste it into your host page and adjust the Happy Path attributes described in this guide.
Keep the SDK Integration ID that comes with the copied snippet. It identifies your account's integration settings and is checked against your Allowed Websites. Wherever this guide shows partner_..., substitute the value from your own snippet.

Requirements

Before a production host can initialize the SDK:

  • The signed-in VIVERSE Me account must have an SDK Integration ID.
  • The host website must be saved in that account's Allowed Websites.
  • The production snippet must include data-ac3-partner-id or a manual partnerId.
  • The host CSP must allow VIVERSE Me frames, for example frame-src https://viverse-me.com.
  • To use the landing-matched Roboto button exactly, the host CSP must allow style-src https://fonts.googleapis.com and font-src https://fonts.gstatic.com; otherwise the browser uses its sans-serif fallback.
  • Google Sign-In requires the exact embed origin to be allowed in the Google OAuth client when using preview or custom origins.
  • Apple Sign In requires the configured Services ID redirect URI to match the deployed VIVERSE Me callback URL.

Local development origins such as http://localhost:<port> are supported when added to Allowed Websites.

Website Authorization

The SDK verifies the current window.location.origin before creating its iframe UI.

Production snippets must include a valid partnerId, and that partner ID must allow the host origin. Users manage Allowed Websites from Account settings. Each allowed website is an exact origin, such as https://www.example.com. It must not include paths, query strings, hashes, wildcards, usernames, or passwords.

If authorization fails

  • ViverseMeSDK.init() resolves to null.
  • No SDK iframe UI is created.
  • The SDK logs a warning.
  • viverse-me:authorization-denied is dispatched.

SDK Happy Path

No-login default Developer-owned storage Optional library sign-in

SDK Happy Path is the game-start embed mode. It defaults to a no-login anonymous flow, and can optionally let end users switch into their authenticated VIVERSE Me Library inside the same panel.

<div id="viverse-me-button-slot"></div>
<script
  src="https://viverse-me.com/sdk.js"
  data-ac3-mode="sdk-happy-path"
  data-ac3-target="#viverse-me-button-slot"
  data-ac3-partner-id="partner_..."
  data-ac3-label="Start"
  async
></script>

When the SDK launch button (or instance.open()) is triggered, a centered modal opens with a character picker. By default the panel lists the public default avatars plus a Create entry. If this browser previously created an avatar for the same SDK Integration ID, the panel also displays that anonymous saved avatar:

  • Pick a default → emits viverse-me:avatar-selected with the public default vrmUrl and closes the modal.
  • Create → opens Avatar Creator with a developer-owned anonymous session (no sign-in). On finish it saves the avatar in that developer's storage namespace, generates a thumbnail, emits viverse-me:avatar-selected with short-lived signed vrmUrl/thumbnailUrl, and closes the modal.
  • Saved avatar → requests fresh signed asset URLs and restores the active avatar created previously in the same browser.

Use VIVERSE Me Library

The panel also shows a Use VIVERSE Me Library button by default. That button opens the same email / Google / Apple sign-in flow used by the Quick Selector. After sign-in, Happy Path switches from anonymous storage to the authenticated account library:

  • The account avatars replace the anonymous saved avatars and public defaults.
  • The button label changes to Sign Out.
  • New avatars created from Happy Path are stored in the signed-in account.
  • If the account is already at its avatar cap, Happy Path blocks creation and shows a library-full dialog.
The SDK stores an opaque anonymous ID in the host browser's localStorage. Clearing site data, changing browser profiles, or changing devices loses the anonymous link unless the host adds an authenticated recovery flow. On mobile, the embedded creator releases the Unity WebGL runtime and restores a thumbnail-only viewer before generating the saved avatar thumbnail.

Host Sign In

If the host has its own account system, keep the default Start button as the anonymous flow and add a separate SDK-owned Host Sign In button.

Provide a button slot with data-ac3-host-sign-in-target; the SDK renders the button with the same definition used by Start. For a Host Sign In only entry, set data-ac3-show-anonymous-button="false". To also remove the in-panel VIVERSE Me Library entry, set data-ac3-hp-show-library-button="false". To skip the prompt when the host already knows the user, set data-ac3-remember-host-sign-in="true".

<div id="viverse-me-button-slot"></div>
<div id="host-sign-in-button-slot"></div>
<script
  src="https://viverse-me.com/sdk.js"
  data-ac3-mode="sdk-happy-path"
  data-ac3-target="#viverse-me-button-slot"
  data-ac3-host-sign-in-target="#host-sign-in-button-slot"
  data-ac3-host-sign-in-label="Host Sign In"
  data-ac3-show-anonymous-button="false"
  data-ac3-remember-host-sign-in="true"
  data-ac3-partner-id="partner_..."
  async
></script>

When the SDK-owned Host Sign In button is clicked, the SDK dispatches viverse-me:host-sign-in-click and calls onHostSignInClick for manual init users. Open your own sign-in dialog from that event, then call openWithHostSignIn(account) after the host identity is known.

window.addEventListener('viverse-me:sdk-ready', (event) => {
  const sdk = event.detail.instance;
  window.addEventListener('viverse-me:host-sign-in-click', () => {
    hostSignInDialog.hidden = false;
  });
  window.addEventListener('viverse-me:host-identity-request', (identityEvent) => {
    identityEvent.detail.respond(hostAuth.currentUserId || '');
  });
  hostSignInForm.addEventListener('submit', async (submitEvent) => {
    submitEvent.preventDefault();
    const account = hostAccountInput.value;
    await sdk.openWithHostSignIn(account);
  });
});
For production, do not trust a browser text field as the account identity. Replace the mock Host Sign In UI with the partner's own authentication flow, verify the user on the partner backend, and only then request a short-lived AC3 host session for that verified external user. The anonymous browser ID used by Start is not sent, so anonymous avatars are not automatically merged into the host account.

Button styling

The default button follows the VIVERSE Me landing primary CTA: Roboto at 1.12rem, 700 weight, 0.04em letter spacing, a 48px minimum height, and a pill radius. Hosts can override its CSS custom properties without replacing the SDK-owned button:

:root {
  --viverse-me-button-background: #111;
  --viverse-me-button-color: #fff;
  --viverse-me-button-font: 700 1.12rem/1.2 Roboto, sans-serif;
  --viverse-me-button-letter-spacing: 0.04em;
  --viverse-me-button-min-height: 48px;
  --viverse-me-button-padding: 0 24px;
  --viverse-me-button-border-radius: 999px;
}

Panel Customization

Happy Path panel customization is an allowlisted client option. It does not require an API key and does not accept arbitrary CSS, HTML, JavaScript, or external stylesheet URLs. The SDK sanitizes the options and applies them as copy values, CSS variables, card visibility flags, and saved-avatar UI limits.

The saved avatar card, default avatar cards, and Create card can each be hidden independently. The panel defaults to one fetched and one displayed saved avatar; hosts can raise those counts without changing backend retention rules.

<script
  src="https://viverse-me.com/sdk.js"
  data-ac3-mode="sdk-happy-path"
  data-ac3-target="#viverse-me-button-slot"
  data-ac3-partner-id="partner_..."
  data-ac3-label="Start"
  data-ac3-hp-show-saved-avatar="false"
  data-ac3-hp-show-default-avatars="false"
  data-ac3-hp-show-create-avatar="false"
  data-ac3-hp-show-library-button="true"
  data-ac3-hp-saved-avatar-fetch-limit="3"
  data-ac3-hp-saved-avatar-display-limit="2"
  data-ac3-hp-panel-background="rgba(255, 255, 255, 0.92)"
  data-ac3-hp-title-color="#0f172a"
  data-ac3-hp-card-background="rgba(16, 24, 40, 0.82)"
  data-ac3-hp-create-label="Create"
  async
></script>

The same settings can be passed during manual initialization under the nested happyPath option:

const instance = await window.ViverseMeSDK.init({
  mode: 'sdk-happy-path',
  target: '#viverse-me-button-slot',
  partnerId: 'partner_...',
  label: 'Start',
  happyPath: {
    cards: {
      showSavedAvatar: false,
      showDefaultAvatars: false,
      showCreateAvatar: false,
      savedAvatarFetchLimit: 3,
      savedAvatarDisplayLimit: 2
    },
    auth: { showLibraryButton: true },
    layout: { maxVisibleRowsBeforeScroll: 2, disableDynamicPanelWidth: false },
    copy: { title: 'Choose a character', createLabel: 'Create' },
    theme: {
      panelBackground: 'rgba(255, 255, 255, 0.92)',
      titleColor: '#0f172a',
      cardBackground: 'rgba(16, 24, 40, 0.82)'
    }
  }
});

Copy & card attributes

Script attributeInit optionDescription
data-ac3-hp-titlehappyPath.copy.titlePanel title.
data-ac3-hp-subtitlehappyPath.copy.subtitleSubtitle before a saved avatar exists.
data-ac3-hp-subtitle-savedhappyPath.copy.subtitleSavedSubtitle when a saved avatar exists.
data-ac3-hp-saved-labelhappyPath.copy.savedLabelSaved avatar card label.
data-ac3-hp-create-labelhappyPath.copy.createLabelCreate card label. Defaults to Create.
data-ac3-hp-default-avatar-1-labelhappyPath.copy.defaultAvatar1LabelFirst default avatar card label.
data-ac3-hp-default-avatar-2-labelhappyPath.copy.defaultAvatar2LabelSecond default avatar card label.
data-ac3-hp-show-saved-avatarhappyPath.cards.showSavedAvatarSet false to hide the saved avatar card.
data-ac3-hp-show-default-avatarshappyPath.cards.showDefaultAvatarsSet false to hide default avatar cards.
data-ac3-hp-show-create-avatarhappyPath.cards.showCreateAvatarSet false to hide the Create card.
data-ac3-hp-show-library-buttonhappyPath.auth.showLibraryButtonSet false to hide the library / Sign Out button. Defaults to true.
data-ac3-hp-saved-avatar-fetch-limithappyPath.cards.savedAvatarFetchLimitHow many saved avatars the panel fetches. Defaults to 1.
data-ac3-hp-saved-avatar-display-limithappyPath.cards.savedAvatarDisplayLimitHow many fetched saved avatars render. Defaults to 1; cannot exceed the fetch limit.
data-ac3-hp-max-visible-rows-before-scrollhappyPath.layout.maxVisibleRowsBeforeScrollGrid rows visible before the grid scrolls. Defaults to 2.
data-ac3-hp-disable-dynamic-panel-widthhappyPath.layout.disableDynamicPanelWidthSet true to rely on your own panelWidth/grid styling.

Theme attributes

A large set of data-ac3-hp-* theme attributes map to happyPath.theme.* — including overlay, panel, title, subtitle, close-button, grid, and card values. Common ones are listed here.

Script attributeInit optionDescription
data-ac3-hp-font-familytheme.fontFamilyPanel font family.
data-ac3-hp-overlay-backgroundtheme.overlayBackgroundModal overlay background.
data-ac3-hp-overlay-backdrop-filtertheme.overlayBackdropFilterModal overlay backdrop filter.
data-ac3-hp-panel-widththeme.panelWidthPanel width.
data-ac3-hp-panel-paddingtheme.panelPaddingPanel padding.
data-ac3-hp-panel-radiustheme.panelRadiusPanel border radius.
data-ac3-hp-panel-backgroundtheme.panelBackgroundPanel background.
data-ac3-hp-panel-bordertheme.panelBorderPanel border color/value.
data-ac3-hp-title-colortheme.titleColorPanel title color.
data-ac3-hp-subtitle-colortheme.subtitleColorPanel subtitle color.
data-ac3-hp-card-backgroundtheme.cardBackgroundAll card backgrounds.
data-ac3-hp-card-bordertheme.cardBorderAll card borders.
data-ac3-hp-card-text-colortheme.cardTextColorAll card text colors.
data-ac3-hp-card-hover-backgroundtheme.cardHoverBackgroundCard hover/focus backgrounds.
data-ac3-hp-card-hover-bordertheme.cardHoverBorderCard hover/focus borders.
data-ac3-hp-grid-template-columnstheme.gridTemplateColumnsCard grid columns.
data-ac3-hp-grid-gaptheme.gridGapCard grid gap.
data-ac3-hp-create-icon-backgroundtheme.createIconBackgroundCreate icon background.
Happy Path only supports vertical grid overflow. Horizontal scrolling is intentionally unsupported in the public SDK surface.

Script Attributes & Init Options

Every script attribute has an equivalent manual init option.

Script attributeInit optionDescription
data-ac3-modemodeUse sdk-happy-path for the no-login game-start picker + developer-owned anonymous storage.
data-ac3-targettargetCSS selector for the host button slot.
data-ac3-partner-idpartnerIdRequired SDK Integration ID.
data-ac3-user-iduserIdStable host user ID. Recommended for production.
data-ac3-session-idsessionIdOptional host session ID.
data-ac3-character-idcharacterIdOptional host character ID.
data-ac3-localelocaleOptional locale forwarded to embedded flows. Defaults to en-US.
data-ac3-labellabelOptional host launch button label.
data-ac3-button-classbuttonClassOptional class added to the host launch button.
data-ac3-show-anonymous-buttonshowAnonymousButtonSet false to hide the anonymous Start button.
data-ac3-host-sign-in-targethostSignInTargetCSS selector for an SDK-owned Host Sign In button slot.
data-ac3-host-sign-in-labelhostSignInLabelHost Sign In button label. Defaults to Host Sign In.
data-ac3-host-sign-in-button-classhostSignInButtonClassClass added to the SDK-owned Host Sign In button.
data-ac3-remember-host-sign-inrememberHostSignInSet true to request a verified host identity before showing sign-in UI.
data-ac3-auto-initn/aSet false for manual initialization.
At least one stable host subject field should be provided for production, usually userId. The backend uses the subject with partnerId to maintain partner active-avatar records.

Manual Initialization

Manual initialization is useful when the host needs to load the SDK dynamically or wire callbacks directly. Set data-ac3-auto-init="false" on the script tag, then call window.ViverseMeSDK.init().

async function loadViverseMeSdk() {
  const script = document.createElement('script');
  script.src = 'https://viverse-me.com/sdk.js';
  script.async = true;
  script.dataset.ac3AutoInit = 'false';
  document.head.append(script);

  await new Promise((resolve, reject) => {
    script.addEventListener('load', resolve, { once: true });
    script.addEventListener('error', reject, { once: true });
  });

  return window.ViverseMeSDK;
}

const sdk = await loadViverseMeSdk();
const instance = await sdk.init({
  mode: 'sdk-happy-path',
  target: '#viverse-me-button-slot',
  partnerId: 'partner_...',
  label: 'Start',
  locale: 'en-US',
  onOpen() { hostScene.pause(); },
  onClose() { hostScene.resume(); },
  onAvatarSelected(event) { hostScene.loadAvatar(event.avatar.vrmUrl); },
  onAuthCleared() { hostScene.clearSelectedAvatar(); },
  onAuthorizationDenied(event) { console.warn(event.message); }
});
If website authorization fails, init() resolves to null, the SDK does not create its iframe UI, and it dispatches viverse-me:authorization-denied.

Events

The SDK dispatches browser events on the host window and also supports equivalent callback options in manual initialization.

EventWhen it firesPayload
viverse-me:openThe SDK opens a modal or embedded flow.SDK state details.
viverse-me:closeThe SDK closes the modal or embedded flow.SDK state details.
viverse-me:avatar-selectedA Happy Path or default avatar is selected.{ avatar } (also flattened on detail) plus animations and controlsHint.
viverse-me:controls-changedSDK control settings change.{ animationEnabled, controlEnabled, controllerSize }
viverse-me:selector-stateThe panel mounts, collapses, expands, or changes placement.{ mode, collapsed, placement, expanded }
viverse-me:host-sign-in-clickThe SDK-owned Host Sign In button is clicked.{ instance, button }
viverse-me:host-identity-requestThe SDK checks whether the host has a verified signed-in user.{ instance, respond }; call respond(externalUserId) or respond('').
viverse-me:authorization-deniedThe partner ID and origin are not authorized.partnerId, origin, code, message

Hosts with active rendering, audio, physics, or expensive polling should pause work on open and resume on close.

window.addEventListener('viverse-me:open', () => { hostScene.pause(); });
window.addEventListener('viverse-me:close', () => { hostScene.resume(); });

Avatar Selection Payload

A selected Happy Path avatar payload can include:

{
  tenantId: '[email protected]',
  key: 'accounts/user-example-com/avatars/avatar.vrm',
  fileName: 'avatar.vrm',
  partnerId: 'partner_...',
  userId: 'host-user-123',
  sessionId: 'optional-host-session',
  characterId: 'optional-character',
  vrmUrl: 'https://api.viverse-me.com/api/ac3/download?...',
  thumbnailUrl: 'https://api.viverse-me.com/api/ac3/download?...',
  vrmExpires: 1770000000,
  thumbnailExpires: 1770000000,
  animations: {
    idle: 'https://viverse-me.com/sdk/animations/idle.vrma',
    walk: 'https://viverse-me.com/sdk/animations/walk.vrma',
    run: 'https://viverse-me.com/sdk/animations/run.vrma',
    jump_start: 'https://viverse-me.com/sdk/animations/jump_start.vrma',
    jump_up: 'https://viverse-me.com/sdk/animations/jump_up.vrma',
    jump_loop: 'https://viverse-me.com/sdk/animations/jump_loop.vrma',
    jump_down: 'https://viverse-me.com/sdk/animations/jump_down.vrma'
  },
  controlsHint: { animationEnabled: true, controlEnabled: true, controllerSize: 'default' }
}

Default avatar selections are returned through the Worker sdk/default-avatar proxy, so third-party hosts do not need direct R2 bucket CORS access for shared default assets. If a host manually builds a default-avatar proxy URL, include both partnerId and the current host origin — this is especially important for thumbnails rendered with <img>, because image requests may not include an Origin header.

const url = new URL('/api/ac3/sdk/default-avatar', apiBase);
url.searchParams.set('partnerId', partnerId);
url.searchParams.set('origin', window.location.origin);
url.searchParams.set('file', 'default-avatar-1.png');

Animations & Integration Tiers

The SDK ships a complete set of optional VRMA motion clips. Pick one of two integration tiers depending on whether you want the SDK to run animations and jump physics for you, or you only want the raw assets.

QuestionPick Tier APick Tier B
Do you already have a game loop / physics engine?No — SDK runs everythingYes — SDK only loads VRM + VRMA
Do you need ground/wall collision, slopes, platforms, multi-floor?No (flat ground is fine)Yes (Tier A is flat-plane only)
Want default Space-jump + WASD + orbit camera + mobile joystick?YesYou'll build your own
Embedding into an existing Three.js / Unity / Unreal world?NoYes

Tier A — bundled avatar controller (recommended)

Load avatar-controller.js through window.ViverseMeSDK.avatarControllerUrl and you get walk / run / jump out of the box, including a gravity-driven jump state machine, the Space key, and an on-screen Jump button on touch devices. The controller also creates and drives a perspective camera for you. Tier A is optimised for a single avatar on flat ground and does not include world collision, floor cutouts, slopes/stairs, ceilings, or NPC interaction.

Tier B — clips with your own engine

If you already have your own Three.js / Unity / Unreal pipeline, take the URLs and ignore the controller. The SDK never modifies position.y on its own when you don't use the controller.

window.ViverseMeSDK.animations
// { idle, walk, run, jump_start, jump_up, jump_loop, jump_down } → absolute VRMA URLs

window.ViverseMeSDK.getAnimationUrl('idle');
// → absolute URL to the bundled idle VRMA

Clip roles:

  • idle, walk, run — locomotion loops.
  • jump_start — one-shot crouch / take-off before leaving the ground.
  • jump_up — one-shot rising phase right after take-off.
  • jump_loop — looping in-air pose for variable airtime.
  • jump_down — one-shot landing phase.

The recommended jump sequence is jump_startjump_upjump_loopjump_down. The animations are hosted with permissive CORS so they can be fetched directly from any host origin.

Avatar Controller

The SDK ships an optional ES module that wires VRMA playback (idle / walk / run plus optional gravity jump), pointer / touch orbit, wheel and pinch zoom, WASD or arrow-key movement, a touch-device virtual joystick, the Space key, and a Jump button onto a host-provided Three.js scene.

ModeWhen to useWhat the controller does
physics: 'builtin' (default)Tier A simple integrations.Owns position.y, applies gravity, plays the jump state machine, binds Space, mounts a Jump button on touch, and creates + drives a perspective camera.
physics: 'none'Tier B integrations with their own physics.Never modifies position.y, never binds Space, never mounts a Jump button, never touches the camera, never installs orbit/zoom listeners. Only handles VRM + mixer + idle/walk/run.

Peer dependencies & importmap

The controller never bundles Three.js or the VRM libraries. The host must provide them through an importmap so a single shared Three.js instance is used.

<script type="importmap">
  {
    "imports": {
      "three": "https://unpkg.com/[email protected]/build/three.module.js",
      "three/addons/": "https://unpkg.com/[email protected]/examples/jsm/",
      "@pixiv/three-vrm": "https://esm.sh/@pixiv/[email protected]?bundle&external=three",
      "@pixiv/three-vrm-animation": "https://esm.sh/@pixiv/[email protected]?bundle&external=three"
    }
  }
</script>
const { createAvatarController } = await import(window.ViverseMeSDK.avatarControllerUrl);

const controller = createAvatarController({
  scene,                    // THREE.Scene (host owns)
  domElement: canvas,       // input target; touch-action set to 'none'
  animations: window.ViverseMeSDK.animations
  // physics: 'builtin', enableJump: true, bindSpaceKey: true — all default on
});

controller.setAvatar(vrm);  // call again on avatar change

function loop() {
  controller.update(clock.getDelta());
  renderer.render(scene, controller.camera); // SDK created the camera for you
  requestAnimationFrame(loop);
}

Constructor options

OptionDefaultNotes
scenerequiredTHREE.Scene provided by the host.
domElementrequiredCanvas / element used for pointer, wheel, touch, and gesture listeners.
cameraundefinedOptional PerspectiveCamera. Tier A: omit to let the SDK create one; pass one to drive a host-owned camera. Tier B: SDK never touches it.
physics'builtin''builtin' owns position.y + jump; 'none' leaves both untouched.
enableJumptrueOnly meaningful when physics: 'builtin'. Disables Space binding, Jump button, and jump machine.
bindSpaceKeytrueBind Space to jump. Set false if Space is used elsewhere.
gravity18 m/s²Game-feel gravity. 9.81 matches Earth.
jumpVelocity6.5 m/sInitial upward velocity. Height ≈ v² / (2g).
variableJumpCutFactor0.45Rising velocity multiplier when the jump input releases early.
groundY0Y of the single ground plane. Tier A is flat-plane only.
enableAnimationtrueMixer master switch. Disabling also forces input off.
enableControltrueMaster switch for orbit / zoom / movement / Space / Jump.
input.joystick'auto''auto' mounts on touch devices; true always; false never.
moveSpeed2.4 m/sWalking speed. Run uses moveSpeed * runSpeedMultiplier.
runSpeedMultiplier2.0Multiplier when double-tap to run is active.
cameraOffset1.35Vertical look-at offset above the anchor (head height).
initialCameraDistance4.5Starting orbit distance, clamped to min/max.
minCameraDistance1.75Closest zoom.
maxCameraDistance6.5Farthest zoom.

Public methods

  • update(delta) — advance mixer, VRM, camera, movement, and jump physics; call once per frame.
  • setAvatar(vrm, animations?) — attach or replace the current VRM. Pass null to detach.
  • setAnimations({ idle, walk, run, jump_* }) — replace VRMA URLs and reload clips.
  • setEnableAnimation(boolean) — toggle clip playback.
  • setEnableControl(boolean) — toggle orbit / zoom / movement / Space / Jump input.
  • setEnableJump(boolean) — toggle the jump machine, Space binding, and Jump button. No-op when physics: 'none'.
  • jump() / cutJump() — programmatic jump trigger and variable-height release.
  • setCameraDistance(distance) — clamp the current orbit distance.
  • setJoystickVisible(boolean) — show/hide the virtual joystick and the Jump button together.
  • setControllerSize('default' | 'small') — switch on-screen control footprint.
  • dispose() — detach listeners, restore touch-action, stop the mixer, remove joystick / Jump button and the anchor.

Read-only getters

  • camera — the camera the SDK drives in Tier A; the host-provided camera or null in Tier B.
  • anchor, visualRootTHREE.Group parents around the VRM. Move / rotate anchor from your own physics in Tier B.
  • cameraState{ yaw, pitch, distance, minDistance, maxDistance }.
  • enableAnimation, enableControl, enableJump, physicsMode, isGrounded, jumpPhase.

Jump Behavior

The jump state machine is gravity-driven: pressing Space (or the on-screen Jump button) sets vertical velocity to jumpVelocity, then each frame applies vy -= gravity * dt until the avatar returns to groundY. Releasing Space while still rising multiplies vy by variableJumpCutFactor for short hops.

  • Standing jump (no movement input on Space press) — plays the full jump_startjump_upjump_loopjump_down sequence. Horizontal movement stays disabled during jump_start and jump_down.
  • Running jump (movement input present) — skips jump_start and jump_down so the legs keep cycling. Mid-air control is at 60% of ground speed (steering only).

If the player provides movement input while a standing-jump jump_down crouch is still playing, the controller cuts the landing clip short and blends to walk / run immediately. On coarse-pointer devices the controller mounts a virtual joystick and — when physics: 'builtin' and enableJump: true — a Jump button. Both are hidden by default; toggle them with setJoystickVisible(true).

Avatar URL Lifecycle

Treat vrmUrl and thumbnailUrl as preview transport, not durable storage.

  • Use signed URLs immediately to render or preview the avatar.
  • Check expiry fields before reusing a cached URL.
  • Store stable references in the partner backend.
  • Resolve fresh signed URLs server-side when restoring a user's selected avatar later.

A browser-only demo may keep a short-lived local preview cache, but a production partner should not rely on browser storage as the source of truth.

Server-side Active Avatar Fetch

Partner backends can resolve the current selected avatar for a host subject through the protected partner API.

GET https://api.viverse-me.com/api/ac3/partner/active-avatar?partnerId=partner_...&userId=host-user-123
Authorization: Bearer <PARTNER_API_TOKEN>

The subject can be userId, sessionId, or characterId. The response returns the stable avatar reference plus fresh signed asset URLs when an active avatar exists.

async function getViverseMeActiveAvatar({ partnerId, userId }) {
  const url = new URL('https://api.viverse-me.com/api/ac3/partner/active-avatar');
  url.searchParams.set('partnerId', partnerId);
  url.searchParams.set('userId', userId);

  const response = await fetch(url, {
    headers: { Authorization: `Bearer ${process.env.PARTNER_API_TOKEN}` }
  });

  if (!response.ok) {
    throw new Error(`Failed to fetch active avatar (${response.status})`);
  }
  return response.json();
}
Never expose PARTNER_API_TOKEN in browser code.

Security Notes

  • Do not put PARTNER_API_TOKEN or admin tokens in browser code.
  • Do not call account, draft, upload, claim, or download-url APIs directly from the third-party page.
  • Use SDK events for browser preview and partner backend APIs for durable restoration.
  • Keep production embeds on https://viverse-me.com/sdk.js unless a preview origin has been explicitly configured.
  • Allow VIVERSE Me in the host site's frame policy or CSP.
  • Safari, private browsing, and enterprise privacy settings can still block third-party cookies or storage used by embedded flows.

Troubleshooting

init() returns null

Check that partnerId is present and the current origin is saved as an Allowed Website.

The Happy Path panel does not show a saved avatar

The browser may not have a stored Happy Path anonymous ID for this SDK Integration ID, or site data may have been cleared. The panel should still show public default avatars unless the host hides them with happyPath.cards.showDefaultAvatars.

A cached avatar URL stops loading

Signed URLs expire. Use the partner backend active-avatar API to resolve a fresh URL, or wait for the SDK to emit a fresh viverse-me:avatar-selected event.

A default thumbnail URL returns SDK_ORIGIN_REQUIRED

Add origin=window.location.origin to the sdk/default-avatar proxy URL. This is usually needed for thumbnails loaded through <img>.

Demo Host

The runnable third-party host example loads https://viverse-me.com/sdk.js in Happy Path mode, listens for viverse-me:avatar-selected, loads the returned VRM into its own Three.js scene, restores developer-owned anonymous avatars, and shows mobile joystick / Jump controls only after an avatar is loaded and the panel is closed.

Run it locally with:

cd demo
python3 -m http.server 5500

Then open http://localhost:5500/.