storysplat-viewer 2.9.47 → 2.9.48
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +153 -17
- package/dist/index.esm.js +1 -1
- package/dist/index.js +1 -1
- package/dist/storysplat-viewer.umd.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,6 +29,9 @@ A powerful npm package for embedding and interacting with 3D Gaussian Splatting
|
|
|
29
29
|
- [Post-Processing](#post-processing)
|
|
30
30
|
- [Custom Menu Links](#custom-menu-links)
|
|
31
31
|
- [Entity Animations](#entity-animations)
|
|
32
|
+
- [Guided Narration](#guided-narration)
|
|
33
|
+
- [Custom Scripts](#custom-scripts)
|
|
34
|
+
- [8th Wall AR](#8th-wall-ar)
|
|
32
35
|
- [React Integration](#react-integration)
|
|
33
36
|
- [Native App Integration](#native-app-integration)
|
|
34
37
|
- [Analytics & Tracking](#analytics--tracking)
|
|
@@ -599,7 +602,7 @@ viewer.getFrameProgress(); // Get current progress
|
|
|
599
602
|
### 4DGS Events
|
|
600
603
|
|
|
601
604
|
```javascript
|
|
602
|
-
viewer.on('frameChange', (
|
|
605
|
+
viewer.on('frameChange', (frame, total) => {
|
|
603
606
|
console.log(`Frame ${frame} of ${total}`);
|
|
604
607
|
});
|
|
605
608
|
|
|
@@ -800,8 +803,8 @@ viewer.on('loaded', () => {
|
|
|
800
803
|
});
|
|
801
804
|
|
|
802
805
|
// Loading progress
|
|
803
|
-
viewer.on('progress', ({
|
|
804
|
-
console.log(`Loading
|
|
806
|
+
viewer.on('progress', ({ progress, text }) => {
|
|
807
|
+
console.log(`Loading ${Math.round(progress * 100)}%: ${text}`);
|
|
805
808
|
});
|
|
806
809
|
|
|
807
810
|
// Waypoint changed
|
|
@@ -839,7 +842,7 @@ viewer.on('splatChange', ({ url, isOriginal }) => {
|
|
|
839
842
|
});
|
|
840
843
|
|
|
841
844
|
// 4DGS frame sequence
|
|
842
|
-
viewer.on('frameChange', (
|
|
845
|
+
viewer.on('frameChange', (frame, total) => {
|
|
843
846
|
console.log(`Frame ${frame} of ${total}`);
|
|
844
847
|
});
|
|
845
848
|
viewer.on('frameComplete', () => {
|
|
@@ -847,8 +850,8 @@ viewer.on('frameComplete', () => {
|
|
|
847
850
|
});
|
|
848
851
|
|
|
849
852
|
// Portal activated
|
|
850
|
-
viewer.on('portalActivated', ({
|
|
851
|
-
console.log(`Navigating to scene: ${
|
|
853
|
+
viewer.on('portalActivated', ({ targetSceneId }) => {
|
|
854
|
+
console.log(`Navigating to scene: ${targetSceneId}`);
|
|
852
855
|
});
|
|
853
856
|
|
|
854
857
|
// Errors and warnings
|
|
@@ -865,9 +868,9 @@ viewer.on('warning', ({ type, message }) => {
|
|
|
865
868
|
| Event | Data | Description |
|
|
866
869
|
|-------|------|-------------|
|
|
867
870
|
| `ready` | — | Viewer initialized |
|
|
868
|
-
| `loaded` |
|
|
869
|
-
| `progress` | `{
|
|
870
|
-
| `waypointChange` | `{ index, waypoint }` | Waypoint changed |
|
|
871
|
+
| `loaded` | `{ bandwidthUsed, isStorySplatHosted }` | Scene fully loaded |
|
|
872
|
+
| `progress` | `{ progress, text }` | Loading progress |
|
|
873
|
+
| `waypointChange` | `{ index, waypoint, prevIndex, cameraMode }` | Waypoint changed |
|
|
871
874
|
| `playbackStart` | — | Auto-play started |
|
|
872
875
|
| `playbackStop` | — | Auto-play paused/stopped |
|
|
873
876
|
| `playbackComplete` | — | Tour reached the end |
|
|
@@ -877,12 +880,11 @@ viewer.on('warning', ({ type, message }) => {
|
|
|
877
880
|
| `xrStart` | `{ type }` | Entered VR/AR |
|
|
878
881
|
| `xrEnd` | — | Exited VR/AR |
|
|
879
882
|
| `splatChange` | `{ url, isOriginal }` | Splat file swapped |
|
|
880
|
-
| `frameChange` | `
|
|
883
|
+
| `frameChange` | `(frame, total)` | 4DGS frame changed |
|
|
881
884
|
| `frameComplete` | — | 4DGS sequence finished |
|
|
882
885
|
| `portalActivated` | `{ portalId, targetSceneId, targetSceneName }` | Portal triggered |
|
|
883
|
-
| `
|
|
884
|
-
| `
|
|
885
|
-
| `portalNavigationError` | `{ error, targetSceneId }` | Portal navigation failed |
|
|
886
|
+
| `portalClick` | `{ portal }` | Portal clicked in editor mode |
|
|
887
|
+
| `panoramaModeChange` | `{ enabled }` | 360 panorama portal mode changed |
|
|
886
888
|
| `error` | `Error` | Viewer error |
|
|
887
889
|
| `warning` | `{ type, message }` | Non-fatal warning |
|
|
888
890
|
|
|
@@ -963,9 +965,8 @@ The `targetSceneId` is just a string you define — it doesn't need to exist on
|
|
|
963
965
|
| Event | Description | Data |
|
|
964
966
|
|-------|-------------|------|
|
|
965
967
|
| `portalActivated` | Portal clicked or proximity triggered | `{ portalId, targetSceneId, targetSceneName }` |
|
|
966
|
-
| `
|
|
967
|
-
| `
|
|
968
|
-
| `portalNavigationError` | Navigation failed | `{ error, targetSceneId }` |
|
|
968
|
+
| `portalClick` | Portal clicked in editor mode | `{ portal }` |
|
|
969
|
+
| `panoramaModeChange` | 360 panorama portal mode changed | `{ enabled }` |
|
|
969
970
|
|
|
970
971
|
## Audio Emitters
|
|
971
972
|
|
|
@@ -1247,6 +1248,113 @@ Keyframe-based animation system for any scene entity (hotspots, portals, lights,
|
|
|
1247
1248
|
| Scale | `scale.x`, `scale.y`, `scale.z` |
|
|
1248
1249
|
| Particles | `rate`, `lifetime`, `speed`, `scale`, `gravity`, `opacity`, `rotationSpeed` |
|
|
1249
1250
|
|
|
1251
|
+
## Guided Narration
|
|
1252
|
+
|
|
1253
|
+
Add a continuous voiceover that synchronizes with the camera tour. The camera speed adjusts automatically per segment to match the narration timing.
|
|
1254
|
+
|
|
1255
|
+
### Scene Data
|
|
1256
|
+
|
|
1257
|
+
```json
|
|
1258
|
+
{
|
|
1259
|
+
"narrationTrack": {
|
|
1260
|
+
"enabled": true,
|
|
1261
|
+
"audioUrl": "https://example.com/narration.mp3",
|
|
1262
|
+
"duration": 120,
|
|
1263
|
+
"volume": 0.8,
|
|
1264
|
+
"segments": [
|
|
1265
|
+
{
|
|
1266
|
+
"id": "intro",
|
|
1267
|
+
"audioStart": 0,
|
|
1268
|
+
"audioEnd": 15,
|
|
1269
|
+
"progressStart": 0,
|
|
1270
|
+
"progressEnd": 25
|
|
1271
|
+
},
|
|
1272
|
+
{
|
|
1273
|
+
"id": "gallery",
|
|
1274
|
+
"audioStart": 15,
|
|
1275
|
+
"audioEnd": 45,
|
|
1276
|
+
"progressStart": 25,
|
|
1277
|
+
"progressEnd": 60
|
|
1278
|
+
}
|
|
1279
|
+
]
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
```
|
|
1283
|
+
|
|
1284
|
+
| Property | Type | Description |
|
|
1285
|
+
|----------|------|-------------|
|
|
1286
|
+
| `enabled` | `boolean` | Master toggle for narration |
|
|
1287
|
+
| `audioUrl` | `string` | URL to the narration audio file (MP3, WAV, OGG) |
|
|
1288
|
+
| `duration` | `number` | Total audio duration in seconds |
|
|
1289
|
+
| `volume` | `number` | Playback volume (0–1) |
|
|
1290
|
+
| `segments` | `array` | Maps audio time ranges to tour progress ranges |
|
|
1291
|
+
| `segments[].id` | `string` | Stable segment identifier |
|
|
1292
|
+
| `segments[].audioStart` / `audioEnd` | `number` | Time range in the audio file (seconds) |
|
|
1293
|
+
| `segments[].progressStart` / `progressEnd` | `number` | Corresponding tour progress range (0–100) |
|
|
1294
|
+
|
|
1295
|
+
The viewer derives camera progress from the active audio segment and only seeks the audio when drift exceeds 0.5 seconds. Narration pauses when the viewer switches to Explore or Walk mode and resumes when returning to Tour mode.
|
|
1296
|
+
|
|
1297
|
+
## Custom Scripts
|
|
1298
|
+
|
|
1299
|
+
Execute custom JavaScript in the viewer sandbox for advanced behaviors. Scripts have access to the viewer API but run in a restricted environment with no network, DOM, or storage access.
|
|
1300
|
+
|
|
1301
|
+
### Scene Data
|
|
1302
|
+
|
|
1303
|
+
```json
|
|
1304
|
+
{
|
|
1305
|
+
"customScript": "viewer.on('waypointChange', ({ index }) => {\n console.log('Arrived at waypoint', index);\n});"
|
|
1306
|
+
}
|
|
1307
|
+
```
|
|
1308
|
+
|
|
1309
|
+
### Available Globals
|
|
1310
|
+
|
|
1311
|
+
| Global | Description |
|
|
1312
|
+
|--------|-------------|
|
|
1313
|
+
| `viewer` | The main viewer API (navigation, camera, audio, hotspots, splats, 4DGS) |
|
|
1314
|
+
| `canvas` | Read-only access to the rendering canvas (dimensions and pointer events) |
|
|
1315
|
+
| `getScrollPercentage()` | Returns current scroll progress (0–1) |
|
|
1316
|
+
| `getCurrentWaypointIndex()` | Returns the current waypoint index |
|
|
1317
|
+
| `registerUpdate(fn)` | Register a function that runs every frame |
|
|
1318
|
+
| `registerCleanup(fn)` | Register a function that runs on disposal |
|
|
1319
|
+
| `console` | Standard log, warn, error, info, debug |
|
|
1320
|
+
| `setTimeout`, `setInterval`, `clearTimeout`, `clearInterval` | Timer functions |
|
|
1321
|
+
| `requestAnimationFrame`, `cancelAnimationFrame` | Animation frame functions |
|
|
1322
|
+
| `queueMicrotask` | Microtask scheduling |
|
|
1323
|
+
|
|
1324
|
+
Scripts are limited to 200,000 characters. The `registerUpdate` callback count is capped at 200.
|
|
1325
|
+
|
|
1326
|
+
### Blocked (Security)
|
|
1327
|
+
|
|
1328
|
+
No `fetch`, `XMLHttpRequest`, `WebSocket`, `localStorage`, `document`, `window`, `eval`, `Function`, `Worker`, or dynamic `import()`.
|
|
1329
|
+
|
|
1330
|
+
## 8th Wall AR
|
|
1331
|
+
|
|
1332
|
+
For AR experiences on iOS devices (which lack native WebXR), the viewer supports 8th Wall (XR8) as an alternative AR runtime.
|
|
1333
|
+
|
|
1334
|
+
```typescript
|
|
1335
|
+
import { loadXR8, isXR8Available, isNativeWebXRAvailable, ARPlacement } from 'storysplat-viewer';
|
|
1336
|
+
|
|
1337
|
+
// Check available AR runtimes
|
|
1338
|
+
if (await isNativeWebXRAvailable()) {
|
|
1339
|
+
// Use native WebXR (Android Chrome, Quest, etc.)
|
|
1340
|
+
} else {
|
|
1341
|
+
// Load 8th Wall (iOS Safari, etc.)
|
|
1342
|
+
await loadXR8();
|
|
1343
|
+
if (isXR8Available()) {
|
|
1344
|
+
// XR8 is ready
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
```
|
|
1348
|
+
|
|
1349
|
+
| Export | Description |
|
|
1350
|
+
|--------|-------------|
|
|
1351
|
+
| `loadXR8()` | Loads the 8th Wall XR8 runtime. Returns a Promise. |
|
|
1352
|
+
| `isXR8Available()` | Checks synchronously whether 8th Wall is already loaded. |
|
|
1353
|
+
| `isNativeWebXRAvailable()` | Checks asynchronously whether the browser supports native WebXR AR. |
|
|
1354
|
+
| `ARPlacement` | AR placement controller for positioning scenes in real-world space. |
|
|
1355
|
+
|
|
1356
|
+
The bundled 8th Wall engine is self-hosted and does not require an API key. Use `eighthWallBaseUrl` or a `data-8thwall-base` attribute to point at custom engine files, and keep `scale: 'absolute'` for ground detection.
|
|
1357
|
+
|
|
1250
1358
|
## React Integration
|
|
1251
1359
|
|
|
1252
1360
|
```jsx
|
|
@@ -1456,9 +1564,25 @@ This data appears in your StorySplat admin dashboard.
|
|
|
1456
1564
|
| `createViewer` (self-hosted scenes) | No | No |
|
|
1457
1565
|
| iframe embed | Yes | Yes |
|
|
1458
1566
|
|
|
1567
|
+
### Manual Analytics Setup
|
|
1568
|
+
|
|
1569
|
+
If you use `createViewer` (self-hosted) but still want StorySplat analytics, use `setupAnalyticsTracking`:
|
|
1570
|
+
|
|
1571
|
+
```typescript
|
|
1572
|
+
import { createViewer, setupAnalyticsTracking } from 'storysplat-viewer';
|
|
1573
|
+
|
|
1574
|
+
const viewer = createViewer(container, sceneData);
|
|
1575
|
+
const tracker = setupAnalyticsTracking(viewer, 'https://discover.storysplat.com', 'SCENE_ID', 'OWNER_UID');
|
|
1576
|
+
|
|
1577
|
+
// Clean up when done
|
|
1578
|
+
tracker.destroy();
|
|
1579
|
+
```
|
|
1580
|
+
|
|
1581
|
+
This enables view counting, bandwidth tracking, and session recording for self-hosted scenes.
|
|
1582
|
+
|
|
1459
1583
|
### Privacy Note
|
|
1460
1584
|
|
|
1461
|
-
Tracking only occurs for scenes loaded via `createViewerFromSceneId
|
|
1585
|
+
Tracking only occurs for scenes loaded via `createViewerFromSceneId` (or when `setupAnalyticsTracking` is explicitly called). If you use `createViewer` without manual tracking setup, no data is sent to StorySplat servers.
|
|
1462
1586
|
|
|
1463
1587
|
### Comparison: Scene ID vs JSON File
|
|
1464
1588
|
|
|
@@ -1555,6 +1679,18 @@ import type {
|
|
|
1555
1679
|
RevealPresetConfig,
|
|
1556
1680
|
} from 'storysplat-viewer';
|
|
1557
1681
|
|
|
1682
|
+
// Analytics
|
|
1683
|
+
import { setupAnalyticsTracking } from 'storysplat-viewer';
|
|
1684
|
+
|
|
1685
|
+
// Custom Scripts
|
|
1686
|
+
import { CustomScriptSystem, setupCustomScript } from 'storysplat-viewer';
|
|
1687
|
+
|
|
1688
|
+
// 8th Wall AR
|
|
1689
|
+
import { loadXR8, isXR8Available, isNativeWebXRAvailable, ARPlacement } from 'storysplat-viewer';
|
|
1690
|
+
|
|
1691
|
+
// 4DGS
|
|
1692
|
+
import { FrameSequencePlayer } from 'storysplat-viewer';
|
|
1693
|
+
|
|
1558
1694
|
// Utilities
|
|
1559
1695
|
import {
|
|
1560
1696
|
DEFAULT_BUTTON_LABELS,
|