> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mascot.bot/llms.txt
> Use this file to discover all available pages before exploring further.

# Migrating the Mascotbot Lipsync SDK

> How to move existing integrations onto the current Mascotbot lipsync SDK — the 0.2.x → 0.3.0 unified-naming cutover, plus the legacy → 0.2.x architectural migration for older integrations.

Two migrations live here:

1. **0.2.x → 0.3.0** — current pre-release cutover. Package scope rename
   * symbol consolidation (single Provider, single mount component).
     Most readers want this.
2. **Legacy → 0.2.x** — older architectural migration (server-side
   visemes → on-device WebAssembly). Kept for integrators on the
   pre-0.2 SDK.

***

# 0.2.x → 0.3.0

The 0.3.0 release unifies SDK naming across the package scope and the
React component surface. **Hard cutover** — no deprecated aliases.
The bytes computing visemes are unchanged; only names and the mount
shape moved.

## Package scope

```ini .npmrc theme={null}
# 0.2.x
@mascotbot-sdk:registry=https://npm.mascot.bot/

# 0.3.0
@mascotbot:registry=https://npm.mascot.bot/
```

```diff theme={null}
# package.json
- "@mascotbot-sdk/lipsync-core":         "^0.2.9",
- "@mascotbot-sdk/lipsync-react":        "^0.2.9",
+ "@mascotbot/core":         "^0.3.0",
+ "@mascotbot/react":        "^0.3.0",
```

The same renaming applies to `lipsync-native` → `@mascotbot/native` and
`lipsync-react-native` → `@mascotbot/react-native` for RN integrators.

## React mount — one Provider, one component

```diff theme={null}
- import { LipsyncProvider, useLipsync } from "@mascotbot-sdk/lipsync-react";
- import {
-   MascotProvider, MascotClient, MascotRive,
-   useMascotPlayback, useMascotInputs,
- } from "@mascotbot-sdk/lipsync-react/rive";
+ import { MascotProvider, useMascot } from "@mascotbot/react";
+ import {
+   Mascot, MascotRive,
+   useMascotPlayback, useMascotInputs,
+ } from "@mascotbot/react/rive";

- <LipsyncProvider apiKey="mascot_pub_…">
-   <MascotProvider>
-     <MascotClient src="/avatar.riv">
-       <MascotRive />
-     </MascotClient>
-   </MascotProvider>
- </LipsyncProvider>
+ <MascotProvider apiKey="mascot_pub_…">
+   <Mascot src="/avatar.riv" />
+ </MascotProvider>
```

If you previously wrapped `<MascotRive />` (or a loading slot) inside
`<MascotClient>`, pass those as children of `<Mascot>` instead —
they take over from the default canvas:

```tsx theme={null}
<MascotProvider apiKey="mascot_pub_…">
  <Mascot src="/avatar.riv" inputs={["wave"]}>
    <div className="my-layout-wrapper">
      <MascotRive />
      <MyCustomOverlay />
    </div>
  </Mascot>
</MascotProvider>
```

## Symbol rename — quick reference

| 0.2.x                                                                   | 0.3.0                                                 | Notes                                                                                  |
| ----------------------------------------------------------------------- | ----------------------------------------------------- | -------------------------------------------------------------------------------------- |
| `<LipsyncProvider>`                                                     | `<MascotProvider>`                                    | Top-level provider; takes `apiKey`                                                     |
| `<MascotProvider>` (empty Rive marker)                                  | **gone**                                              | Merged into the top-level                                                              |
| `<MascotClient>` + `<MascotRive />`                                     | `<Mascot>`                                            | Single component; renders canvas by default. `<MascotRive />` stays as an escape hatch |
| `useLipsync()`                                                          | `useMascot()`                                         | Same return shape                                                                      |
| `MascotLipsyncClient`                                                   | `LipsyncClient`                                       | Class — package scope provides brand                                                   |
| `MascotLipsyncConfig`                                                   | `LipsyncConfig`                                       | Type                                                                                   |
| `LipsyncContextValue`                                                   | `MascotContextValue`                                  | Type returned by `useMascot()`                                                         |
| `MascotClientContext` / `MascotClientContextType` / `MascotClientProps` | `MascotContext` / `MascotContextType` / `MascotProps` | Component renamed → matching types                                                     |

**Unchanged on purpose** (feature-descriptive — `lipsync` here names
what the thing is, not who ships it):

* `useProcessAudio(url)`
* `useLipsyncStream({ source })`
* `useMascotRive()` / `useMascotInputs()` / `useMascotPlayback()`
* `LipsyncError` / `LipsyncStatus` / `LipsyncLogger`
* `LipsyncStreamSource` / `LipsyncStreamFrame`
* `LicenseError` / `NetworkError` / `EngineError` / `RefusedError`
* `VisemeTimeline` / `VisemeCue` / `MascotPlayback` (the class) / `NaturalLipSync*`

## Migration steps (0.2.x → 0.3.0)

<Steps>
  <Step title="Update .npmrc + package.json">
    Change `@mascotbot-sdk:registry=…` to `@mascotbot:registry=…`, then
    rewrite the four dep names. `pnpm install --force` after — pnpm
    caches tarballs by hash, the new scope needs a fresh resolve.
  </Step>

  <Step title="Run the rename grep">
    ```bash theme={null}
    git grep -nE '\b(LipsyncProvider|MascotClient|useLipsync|MascotLipsyncClient|MascotLipsyncConfig|LipsyncProviderProps|LipsyncContextValue|MascotClientContext|MascotClientContextType|MascotClientProps)\b'
    ```

    Every hit needs the substitution in the table above. `useLipsyncStream`,
    `LipsyncError`, etc. are intentionally preserved — they survive
    because the word `lipsync` precisely describes what they are.
  </Step>

  <Step title="Collapse the Provider + Mascot mount">
    `<LipsyncProvider><MascotProvider>…` → single `<MascotProvider apiKey>`.
    `<MascotClient src><MascotRive /></MascotClient>` → `<Mascot src />`
    if you don't need a custom layout slot. If you do, change the tag
    name from `MascotClient` to `Mascot` and keep the children.
  </Step>

  <Step title="Typecheck + smoke">
    `npx tsc --noEmit` is the gate. Runtime behavior is identical — if
    types pass, runtime almost always passes too.
  </Step>
</Steps>

***

# Legacy → 0.2.x

The 0.2.x SDK was a new architecture, not a renamed release. If you
integrated a pre-0.2 build, the changes below still apply (cascade
0.2.x → 0.3.0 from the section above on top).

## What changed, conceptually

| Then (legacy)                                                                                                                           | Now (0.2.x+)                                                                                                                                                                    |
| --------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| One legacy `@mascotbot/react` package (pre-0.2)                                                                                         | `@mascotbot/core` + `@mascotbot/react`, each with a `/rive` subpath                                                                                                             |
| Visemes computed **server-side** and streamed to the client (REST `/v1/visemes`, SSE, a Mascot Bot WebSocket proxy, "viseme injection") | Visemes computed by the SDK on-device. No proxy, no REST viseme API, no SSE viseme protocol                                                                                     |
| Provider-specific hooks (`useMascotElevenlabs`, `useMascotLiveAPI`, `useMascotOpenAI`) and a TTS hook (`useMascotSpeech`)               | One realtime contract: tap the assistant's audio into `useLipsyncStream({ source: { kind: "mediaStream", stream } })`. TTS server returns audio only; the SDK does the lip sync |
| `getSignedUrl` proxy that injected visemes                                                                                              | Provider wired with its **own** official SDK; server mints only a short-lived provider credential                                                                               |
| `result.argmax` per-frame array                                                                                                         | `result.timeline` — a serializable, versioned `VisemeTimeline`                                                                                                                  |
| Private-registry `.tgz` install                                                                                                         | Private npm registry `npm.mascot.bot` with an `.npmrc` token                                                                                                                    |

The dedicated provider guides ([ElevenLabs](/libraries/elevenlabs-avatar),
[Gemini Live](/libraries/gemini-live-api-avatar),
[OpenAI Realtime](/libraries/openai-realtime-api-avatar)) and
[Realtime overview](/realtime/overview) show the new wiring end to end.

## Symbol map (legacy → 0.2.x — then add the 0.3.0 column above)

| Legacy                                                    | 0.2.x replacement                                                                                                                                                                                                                                                                                                            |
| --------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Legacy `@mascotbot/react` (pre-0.2)                       | `@mascotbot/react` + `@mascotbot/react/rive` (0.3.0 names)                                                                                                                                                                                                                                                                   |
| `useMascot()`                                             | `useMascotRive()` + `useMascotInputs()` (split the grab-bag — `rive`/render from the first, `custom`/`has`/`riveInputs` from the second)                                                                                                                                                                                     |
| `useMascotClient()`                                       | `useMascotRive()` + `useMascotInputs()`                                                                                                                                                                                                                                                                                      |
| `useRiveInputs()`                                         | `useMascotInputs()`                                                                                                                                                                                                                                                                                                          |
| `useMascotSpeech()`                                       | Server route returns audio → `createPCMStreamPlayer` + `useLipsyncStream`. See [Realtime overview](/realtime/overview)                                                                                                                                                                                                       |
| `useMascotElevenlabs()`                                   | [ElevenLabs guide](/libraries/elevenlabs-avatar) — own SDK + audio tap                                                                                                                                                                                                                                                       |
| `useMascotLiveAPI()`                                      | [Gemini Live guide](/libraries/gemini-live-api-avatar)                                                                                                                                                                                                                                                                       |
| `useMascotOpenAI()`                                       | [OpenAI Realtime guide](/libraries/openai-realtime-api-avatar)                                                                                                                                                                                                                                                               |
| `gesture: true` (auto-fire at utterance start)            | The auto-fire is gone — the SDK never drives the `gesture` input. The built-in `stress` emphasis is `useMascotPlayback().stress([{ offset, stress }])`. A separate custom `gesture` trigger input is consumer-fired via `useMascotInputs().custom.gesture.fire()` — declare it on `<Mascot inputs={["gesture", ...]}>` first |
| `useMicLipsync()`                                         | `useLipsyncStream({ source: { kind: "mic" } })`                                                                                                                                                                                                                                                                              |
| `useStreamingLipsync()`                                   | `useLipsyncStream({ source: { kind: "manual" } })`                                                                                                                                                                                                                                                                           |
| `useMediaStreamLipsync()`                                 | `useLipsyncStream({ source: { kind: "mediaStream", stream } })`                                                                                                                                                                                                                                                              |
| `playback.add(visemes)`                                   | `playback.pushVisemes(cues)` (streaming) or `playback.setTimeline(tl)` (offline)                                                                                                                                                                                                                                             |
| `loadPrefetchedData(...)`                                 | Persist a `VisemeTimeline` JSON; replay via `parseTimeline` + `setTimeline`. See [Offline lip sync](/libraries/offline-lipsync)                                                                                                                                                                                              |
| `result.argmax`                                           | `result.timeline` (a `VisemeTimeline`)                                                                                                                                                                                                                                                                                       |
| `rive.stateMachineInputs(...)` introspection for presence | `useMascotInputs().has(name)` / `hasRiveInput(rive, name)`                                                                                                                                                                                                                                                                   |
| Mascot Bot proxy / `get-signed-url` / `/v1/visemes`       | Removed — hybrid architecture; license init + refresh against `license.mascot.bot`.                                                                                                                                                                                                                                          |

## Hooks are not reference-stable

Legacy `useMascot()` returned a **memoised, stable** object;
`useMascotRive()` / `useMascotInputs()` return a **fresh wrapper every
render**.

* Putting their return (`custom`, `has`, the whole handle) in a
  `useEffect` / `useCallback` **dependency array** re-runs it every
  render — e.g. a Rive event listener that re-binds \~60×/s. Capture
  what you need in a `useRef` and depend only on the **stable `rive`
  instance** (stable once loaded). `riveInputs` identity is stable,
  but the wrapper object and `has` are fresh per render — so this rule
  applies to them.

<Warning>
  The one consumer responsibility that is real is **full per-call
  teardown on a shared client** — `stop()` + `close()` + null the stream
  state on every call-end path; see the
  [Troubleshooting guide](/libraries/react-troubleshooting). A lingering
  pipeline on the shared client corrupts other widgets that use the same
  `<MascotProvider>`.
</Warning>

## Next

<Columns cols={3}>
  <Card title="Quickstart" icon="rocket" href="/quickstart">
    The current happy path.
  </Card>

  <Card title="Realtime overview" icon="bolt" href="/realtime/overview">
    Provider wiring.
  </Card>

  <Card title="Troubleshooting" icon="wrench" href="/libraries/react-troubleshooting">
    Post-migration issues.
  </Card>
</Columns>
