@dialtribe/dialtribe-bobl
v2.9.2
Published
Bobl studio — puppet editor, rendering engines, and physics
Readme
@dialtribe/dialtribe-bobl
The complete Bobl puppet studio editor — drop-in React component with all types, rendering engines, physics, hooks, and UI.
External Installation
yarn add @dialtribe/dialtribe-boblPublishing to NPM (once installed - see below)
- Build first: npm run build
- Bump version: npm version patch (or minor/major)
- Login (if not) npm login
- to troubleshoot: npm whoami or run in succession: npm logout npm login npm whoami -> (to confirm it worked)
- Publish: npm publish --access restricted
- run:
unset NPM_TOKEN # clears the stale value so the fallback kicks in
bash scripts/sync-clients.shLocal Development
For developing the package locally without publishing:
0. One time setup
npm install --save-dev typescript
npm install --save-dev @types/react @types/react-dom @types/node
npm run buildNOTE: you may still need to troubleshoot installing certain types
1. Build the package (with watch mode)
cd dialtribe-bobl
npm install
npm run devThis runs tsc --watch and Tailwind CSS watch in parallel — any change to src/ recompiles to dist/ automatically.
2. Run the demo app
In a separate terminal:
cd dialtribe-bobl/demo
cp .env.example .env # fill in your dialtribe API URL, app ID, etc.
npm install
npm run dev # opens on http://localhost:3012The demo uses "file:.." to link directly to the parent package. Vite picks up changes from dist/ with hot reload — no publishing needed.
3. Test in bobl (or another consumer app)
Instead of publishing, use yarn link:
# In the package root
cd dialtribe-bobl
npm link
# In bobl
cd bobl
npm link @dialtribe/dialtribe-boblChanges to src/ (with npm run dev running) are immediately available in bobl. Run npm unlink @dialtribe/dialtribe-bobl in bobl when done to switch back to the published version.
Required peer dependencies
yarn add react react-dom swr livekit-client \
@dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities \
opentype.jsOptional peer dependencies (install for specific features)
# Face tracking (MediaPipe head/face detection)
yarn add @mediapipe/tasks-vision
# Phone calling (Telnyx VoIP)
yarn add @telnyx/webrtc
# Audio modifiers (pitch shift, robot voice, etc.)
yarn add tone soundtouchjs
# GIF picker in chat panel
yarn add gif-picker-reactAuthentication
BoblStudio authenticates API calls using session tokens (sess_...). The full auth flow works as follows:
- Your server calls
POST /api/public/v1/sessionson the dialtribe API using your secret key (dt_sk_...) to create a scoped session token for the current user. - Your client fetches that token (e.g. via your own
/api/session-tokenroute) and passes it to<BoblStudio authToken={token} />. - BoblStudio sends the token as
Authorization: Bearer sess_...on every API request. - Sliding expiry: When a session token is used within 30 minutes of its expiry, the server automatically extends it by 1 hour. Active users are never logged out mid-workflow.
- Token refresh: Pass
onTokenExpiringto automatically refresh the token ~5 minutes before it expires. The callback should return a fresh session token string.
Creating a session token (server-side)
// POST to dialtribe API with your secret key
const res = await fetch(`${DIALTRIBE_API_URL}/api/public/v1/sessions`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${DIALTRIBE_SECRET_KEY}`, // dt_sk_... secret key
},
body: JSON.stringify({
permissions: ["app:read", "app:write", "broadcasts:watch", "broadcasts:write", "content:read", "content:write", "chat:read", "chat:write"],
user_id: "your-user-123", // optional external user ID
ttl: 3600, // 1 hour (default), max 86400 (24 hours)
}),
});
const { session_token, expires_at, permissions } = await res.json();Available permissions
| Permission | Description |
|---|---|
| app:read | Read app metadata and configuration |
| app:write | Update app settings |
| broadcasts:watch | View broadcast metadata and status |
| broadcasts:write | Create and manage broadcasts |
| content:read | Read content metadata and listings |
| content:write | Create, update, delete content |
| content:watch | View and play audio/video content |
| content:clip | Create clips from existing content |
| content:upload | Upload new audio/video content |
| content:transcribe | Request transcription of content |
| content:summarize | Request AI-generated summaries |
| chat:read | Read chat messages and room state |
| chat:write | Send chat messages |
Quick Start — Drop-in Editor
The primary export is <BoblStudio /> — a single component that renders the entire puppet studio editor.
Usage Modes
BoblStudio supports three usage modes depending on how it is deployed:
1. Anonymous / Demo mode
No authToken provided. The studio renders the full workspace with local-only state. All API fetches (app data, shows, project persistence, streaming) gracefully return defaults so the editor is fully usable for offline creation. Save-to-server and go-live features are disabled.
"use client";
import { BoblStudio } from "@dialtribe/dialtribe-bobl";
export default function DemoPage() {
return (
<BoblStudio
appId="demo"
baseUrl="https://api.dialtribe.tv"
/>
);
}2. Authenticated partner app (e.g. bobl)
Pass baseUrl, authToken, and onTokenExpiring so API calls reach the dialtribe server with authentication. The authToken is a session token (sess_...) created via the sessions API. All features are enabled including project persistence, streaming, and chat.
"use client";
import { useState, useEffect } from "react";
import { BoblStudio } from "@dialtribe/dialtribe-bobl";
export default function StudioPage() {
const [authToken, setAuthToken] = useState<string>();
useEffect(() => {
fetch("/api/session-token")
.then((r) => r.ok ? r.json() : null)
.then((d) => { if (d?.sessionToken) setAuthToken(d.sessionToken); })
.catch(() => {});
}, []);
return (
<BoblStudio
appId="Z6fICpr"
orgSlug="bobl"
baseUrl={process.env.NEXT_PUBLIC_DIALTRIBE_API_URL}
authToken={authToken}
onTokenExpiring={async () => {
const res = await fetch("/api/session-token");
const data = await res.json();
return data.sessionToken;
}}
header={<MyAppHeader />}
/>
);
}3. Same-origin dialtribe app
When running inside the dialtribe app itself, omit both baseUrl and authToken. All API calls use relative URLs and authenticate via the user's session cookie.
<BoblStudio
appId={appId}
orgSlug={orgSlug}
header={<AppHeader app={app} title="Studio" />}
/>BoblStudioProps
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| appId | string | Yes | The app ID (NanoId) to load the studio for |
| orgSlug | string | No | Organization slug, used for URL construction |
| isPopup | boolean | No | Whether this is rendered in a popup window (defaults to false) |
| header | ReactNode | No | Optional header component rendered above the studio (e.g., navigation bar) |
| builderUrl | string | No | URL to the puppet workshop/builder page |
| baseUrl | string | No | Base URL for all dialtribe API calls (e.g. "http://localhost:3010"). Defaults to "" (relative URLs). Required when running outside the dialtribe app. Omit for same-origin deployment. |
| authToken | string | No | Auth token sent as Authorization: Bearer header on all API requests. Accepts a session token (sess_...) or secret API key (dt_sk_...). When omitted in a partner app, the studio operates in anonymous/demo mode with local-only state. |
| onTokenExpiring | () => Promise<string> | No | Callback invoked ~5 minutes before the session token expires (assumes 1-hour TTL). Should return a fresh session token string. The studio automatically updates the auth context with the new token. If not provided, the token expires naturally. |
API Base URL
All internal fetch calls (asset uploads, project persistence, stream control, etc.) are prefixed with baseUrl. This is distributed via React context (DialtribeApiContext) so every nested hook and component automatically uses the correct server.
If you need the base URL in custom code, use the context hook:
import { useApiBaseUrl } from "@dialtribe/dialtribe-bobl";
function MyCustomComponent() {
const apiBase = useApiBaseUrl();
// apiBase is "" when inside dialtribe, or "http://localhost:3010" in partner apps
const res = await fetch(`${apiBase}/api/private/...`);
}For non-React utility functions (e.g. uploadFileToCdn, exportProject), pass baseUrl as an option:
import { uploadFileToCdn } from "@dialtribe/dialtribe-bobl";
await uploadFileToCdn(file, appId, { baseUrl: "http://localhost:3010" });Next.js Setup
Add to your next.config.ts:
const nextConfig: NextConfig = {
transpilePackages: ['@dialtribe/dialtribe-bobl'],
};Example: Full Next.js page
// app/studio/[id]/page.tsx
"use client";
import { useParams } from "next/navigation";
import { BoblStudio } from "@dialtribe/dialtribe-bobl";
export default function StudioPage() {
const { id } = useParams();
return (
<div className="h-screen w-screen">
<BoblStudio
appId={id as string}
orgSlug="my-org"
baseUrl={process.env.NEXT_PUBLIC_DIALTRIBE_API_URL}
header={
<header className="h-14 bg-gray-900 flex items-center px-4">
<h1 className="text-white font-bold">My Studio</h1>
</header>
}
/>
</div>
);
}Using Types & Engines Directly
Everything is importable from the top-level barrel:
import type { Overlay, PuppetStyle, Trigger, Viewport } from "@dialtribe/dialtribe-bobl";
import { OverlayRenderer, PhysicsEngine, createPuppetOverlay } from "@dialtribe/dialtribe-bobl";Types Reference
stage-overlays — Core overlay & puppet state
| Key Exports | Description |
|---|---|
| Overlay | Union interface for all canvas elements (text, image, video, puppet, shape, group, etc.) |
| OverlayType | "text" \| "image" \| "video" \| "puppet" \| "shape" \| "group" \| "audioVisualizer" \| "spawnPoint" \| "viewport" \| "template" |
| TextStyle, ImageStyle, VideoStyle, PuppetStyle, ShapeStyle, GroupStyle | Per-type style interfaces |
| OverlayAnimation | Animation config (entrance, exit, looping expressions) |
| PhysicsStyle, PhysicsConfig | Physics simulation settings per overlay |
| AnchorConstraint | Spatial constraints between overlays (pin, hinge, spring, rail, look-at) |
| AnimationPath | Path-based movement (linear, orbital, waypoint, follow) |
| FaceLayout | Puppet face feature positioning |
| createPuppetOverlay(), createTextOverlay(), createImageOverlay(), etc. | Factory functions for each overlay type |
| sortOverlaysForRendering() | Z-index sort for draw order |
| PUPPET_Z_INDEX | Default z-index for puppet overlays |
puppet-parts — Body part tree & layers
| Key Exports | Description |
|---|---|
| PuppetPart | Tree node — has children, layers, jiggle physics, parallax |
| PuppetPartType | "body" \| "head" \| "face" \| "eyes" \| "mouth" \| "leftArm" \| "rightArm" etc. (15 types) |
| PuppetLayer | Visual layer within a part (asset, opacity, z-index) |
| PuppetAsset | Image, SVG, primitive shape, or spritesheet |
| FaceSwapRule | Maps face tracking channels to layer variants |
| PuppetPose | Named snapshot of part transforms for pose blending |
| PuppetOutfit | Collection of layer changes for outfit switching |
triggers — Event-driven actions
| Key Exports | Description |
|---|---|
| Trigger | A trigger with conditions and actions |
| TriggerCondition | Keyboard, timer, media position, API poll, file watch, viewport enter/leave |
| TriggerAction | What happens: show/hide/move overlays, play audio, scene transitions |
| SceneTransition | Transition effects between scenes (cut, fade, wipe, curtain, etc.) |
| createTrigger(), createDefaultAction() | Factory functions |
modifiers — Visual & audio effects
| Key Exports | Description |
|---|---|
| VisualModifier | Chroma key, blur, color adjust, pixelate, crop, tile, shape mask, etc. |
| AudioModifier | Pitch shift, robot, telephone, echo, cave, megaphone |
| createVisualModifier(), createAudioModifier() | Factory functions |
viewports — Multi-viewport management
| Key Exports | Description |
|---|---|
| Viewport | A named rectangular view with position, size, crop, easing |
| MAIN_VIEWPORT, MAIN_VIEWPORT_ID | The default viewport |
| EasingType | Transition easing between viewport positions |
show-settings — Broadcast configuration
| Key Exports | Description |
|---|---|
| ShowSettings | Title, guest access, stream formats, resolution, face tracking, waiting room |
| CANVAS_SIZE | Reference workspace size ({ width: 1280, height: 1280 }) |
| DEFAULT_SHOW_SETTINGS | Sensible defaults |
| VideoFrame | Decorative frame graphic for framed video display |
| WaitingRoomConfig | Pre-show countdown configuration |
face-tracking — Face tracking & LOD
| Key Exports | Description |
|---|---|
| FaceTrackingData | MediaPipe face landmark values (mouth, eyes, brows, head rotation, pupils) |
| PuppetVideoOverride | Replaces puppet with live video (fill or framed mode) |
| LodTier | Level of detail: "full" \| "reduced" \| "placeholder" |
| computeLodTier(zoom) | Returns appropriate LOD for a given zoom level |
Additional types
| Module | Key Exports |
|---|---|
| index (shared) | App, Broadcast, Clip, Upload, Content |
| integrations | ShowIntegrationClient, ChatPlatform |
| keyboard-shortcuts | Keyboard shortcut definitions |
| media-sources | MediaSource, VideoSourceConfig |
| power-board | PowerButton, PlaybackMode |
| project | ProjectExport, SourceManifestEntry |
| scene-group-export | Scene group import/export types |
| shared-sources | SharedSource, ParticipantDisplayMode |
| show-requests | Show join request types |
| streaming | StreamDestinationClient, StreamPlatform |
| transcription | TranscriptSegment, TranscriptExportFormat |
Engines
OverlayRenderer
Canvas rendering engine — draws all overlay types onto an HTML canvas.
import { OverlayRenderer } from "@dialtribe/dialtribe-bobl";
const renderer = new OverlayRenderer();
renderer.render(ctx, overlays, canvasWidth, canvasHeight, time);PhysicsEngine
Spring-based physics for jiggle, bounce, and sway effects.
import { PhysicsEngine } from "@dialtribe/dialtribe-bobl";
const physics = new PhysicsEngine();
const transform = physics.update(overlay, deltaTime, faceData);Other engines
| Engine | Description |
|---|---|
| ConstraintSolver | Spatial constraints between overlays (pin, hinge, spring, rail, look-at) |
| PathAnimator | Path-based movement (linear, orbital, waypoint, follow) |
| EntranceExitEngine | Entrance/exit animation state and transforms |
| VisualModifierProcessor | Visual modifier stack processing (chroma key, blur, etc.) |
| evaluateFaceSwapRules() | Face-driven layer variant selection |
| getPoseTransformForPart() | Pose blending per body part |
| applyOutfit() | Outfit layer changes on body-part tree |
| flattenPartTree() | Flatten hierarchical body-part tree |
Tracking Hooks
// Face tracking (MediaPipe FaceLandmarker — face, pose, hands)
import { useHeadTracking } from "@dialtribe/dialtribe-bobl";
import type { FaceData, HeadTrackingOptions } from "@dialtribe/dialtribe-bobl";
// Audio analysis (mic level → mouthOpen)
import { useAudioAnalyzer } from "@dialtribe/dialtribe-bobl";
import type { AudioSettings, AudioAnalyzerState } from "@dialtribe/dialtribe-bobl";
// Tracking state consumed by renderBobblehead()
import type { BobbleheadTrackingState } from "@dialtribe/dialtribe-bobl";Utilities
// Puppet asset library — pre-made SVG assets
import { PUPPET_ASSET_LIBRARY, getAssetsForPart, searchAssets } from "@dialtribe/dialtribe-bobl";
// Puppet defaults — factory functions for body-part trees
import { createDefaultBodyParts, createLimbParts } from "@dialtribe/dialtribe-bobl";
// Workspace coordinate math
import { overlayToWorkspace, percentToPixelX, overlayIntersectsViewport, REFERENCE_SIZE } from "@dialtribe/dialtribe-bobl";Package Structure
src/
index.ts — barrel export
BoblStudio.tsx — full editor component (<BoblStudio />)
DialtribeApiContext.ts — API config context (baseUrl, authToken)
types/ — all TypeScript type definitions
index.ts — App, Broadcast, Clip, Upload, Content
stage-overlays.ts — Overlay, styles, factory functions
puppet-parts.ts — PuppetPart tree, layers, face-swap, poses
puppet-appearance.ts — Legacy simple puppet appearance
modifiers.ts — Visual & audio modifier stack
triggers.ts — Event/action trigger system
viewports.ts — Multi-viewport cameras
show-settings.ts — Broadcast & show configuration
face-tracking.ts — FaceTrackingData, PuppetVideoOverride, LodTier
integrations.ts — Chat platform integrations
keyboard-shortcuts.ts — Shortcut definitions
media-sources.ts — Media source configs
obs-import.ts — OBS import result types
power-board.ts — Power board button types
project.ts — Project export/import types
scene-group-export.ts — Scene group sharing types
shared-sources.ts — Guest shared source types
show-requests.ts — Join request types
streaming.ts — Stream destination types
transcription.ts — Live transcription types
engines/ — rendering & physics engines
overlay-renderer.ts — Canvas rendering engine
physics-engine.ts — Spring physics simulation
constraint-solver.ts — Spatial constraint resolution
path-animator.ts — Path-based movement
entrance-exit-engine.ts — Entrance/exit animations
visual-modifier-processor.ts — Effect stack processing
face-swap-engine.ts — Face-driven layer swapping
pose-engine.ts — Pose blending
outfit-engine.ts — Outfit switching
puppet-asset-library.ts — SVG asset registry
puppet-defaults.ts — Body-part tree factories
workspace-transform.ts — Coordinate math
components/ — React UI components
puppet-studio/ — 84 editor panel components
modifiers/ — 5 modifier stack UI components
chat/ — 5 chat components (GIF picker, reactions, etc.)
hooks/ — 45 React hooks
useOverlays.ts — Central overlay state management
usePuppetWebSocket.ts — Real-time puppet sync
useHeadTracking.ts — MediaPipe face detection
useModifierStack.ts — Audio/visual modifier pipeline
useTriggers.ts — Trigger evaluation & execution
useViewports.ts — Viewport state management
useLiveKitRoom.ts — LiveKit video room management
useProjectPersistence.ts — Auto-save & project loading
useCrashRecovery.ts — IndexedDB crash recovery
... and 36 more
lib/ — utility functions
asset-upload.ts — CDN asset upload
canvas-thumbnail.ts — Canvas JPEG capture
fonts.ts — Font registry & Google Fonts loading
shape-boolean.ts — Boolean geometry (union, intersect, subtract)
template-engine.ts — Template variable binding
viewport-templates.ts — Viewport layout presets
obs-import/ — OBS scene collection importer
... and more