VR 360° virtual tour system with visual editor and viewer library
npm install senangwebs-tourbash
npm install
npm run dev
`
2. Open Editor: Navigate to http://localhost:8080/examples/editor.html
3. Create Your Tour:
- Click "Add Scene" and upload 360° panorama images (JPG/PNG)
- Click "Add Hotspot" then click on the preview to place navigation points
- Configure hotspot properties: color, scale, tooltip text, target scene
- See changes instantly in the live A-Frame preview
4. Export Your Tour:
- JSON Export → Portable config file for library integration
- Viewer Export → Self-contained HTML file (no dependencies needed)
5. Test Standalone Viewer: Open http://localhost:8080/examples/viewer.html and drag-and-drop your exported JSON
$3
Minimal Example (see examples/example-simple.html):
`html
`
Custom Editor Integration
Build your own tour editor using the swt-editor.js bundle. Two initialization modes are supported:
$3
Zero JavaScript - perfect for quick prototypes:
`html
rel="stylesheet"
href="https://unpkg.com/senangwebs-tour@latest/dist/swt-editor.css"
/>
data-swt-editor
data-swt-auto-init="true"
data-swt-project-name="My Virtual Tour"
>
Supported HTML Attributes:
| Attribute | Description | Default |
|-----------|-------------|---------|
| data-swt-editor | Marks the editor container (required) | - |
| data-swt-auto-init | Auto-initialize on DOMContentLoaded | false |
| data-swt-project-name | Initial project name | "Untitled Tour" |
| data-swt-auto-save | Enable LocalStorage auto-save | false |
| data-swt-auto-save-interval | Auto-save interval (milliseconds) | 30000 |
| data-swt-scene-list | Scene list panel container | - |
| data-swt-preview-area | A-Frame preview container | - |
| data-swt-properties-panel | Hotspot properties panel | - |
Example: examples/editor-declarative.html
$3
Full control - for custom workflows and advanced integrations:
`html
rel="stylesheet"
href="https://unpkg.com/senangwebs-tour@latest/dist/swt-editor.css"
/>
`
Available Classes (all attached to window after loading swt-editor.js):
- TourEditor - Main coordinator, orchestrates all managers
- SceneManagerEditor - Scene CRUD operations
- HotspotEditor - Hotspot placement and editing
- PreviewController - A-Frame preview rendering
- UIController - DOM rendering and updates
- ProjectStorageManager - LocalStorage persistence
- ExportManager - JSON and HTML export
Examples:
- examples/editor-declarative.html - Declarative HTML-only mode
- examples/editor.html - Full-featured programmatic editor
Building from Source
$3
`bash
Install dependencies
npm install
Build all bundles (viewer + editor)
npm run build
Development mode (watch for changes)
npm run dev
`
API Documentation
$3
#### Constructor
`javascript
new SWT.Tour(aframeSceneElement, tourConfiguration);
`
Parameters:
- aframeSceneElement (HTMLElement) - A-Frame DOM element
- tourConfiguration (Object) - Tour config (see structure below)
Returns: Tour instance
---
#### Configuration Structure
Both the editor and viewer use the same data format. Scenes are stored as an array:
`javascript
{
initialScene: "scene-id", // Required: Starting scene ID
cursor: ".custom-cursor", // Optional: Cursor selector or false to disable
scenes: [ // Required: Array of scenes
{
id: "scene-id", // Required: Scene identifier
name: "Scene Name", // Required: Display name
panorama: "url-or-dataurl", // Required: Image URL or base64
thumbnail: "url", // Optional: Thumbnail for editor
startingPosition: { // Optional: Initial camera orientation
pitch: 0.1, // Vertical angle (radians)
yaw: 1.5 // Horizontal angle (radians)
},
hotspots: [ // Optional: Array of hotspots
{
id: "hotspot-1", // Optional: Auto-generated if omitted
position: { // Required: 3D coordinates
x: 10,
y: 1.5,
z: -3
},
action: { // Required: Hotspot action
type: "navigateTo", // Required: Action type
target: "scene-id-2" // Required: Target scene ID
},
appearance: { // Optional: Visual customization
color: "#00ff00", // Default: "#00ff00"
scale: "1 1 1", // Default: "1 1 1"
icon: "arrow" // Optional: Icon name or URL
},
tooltip: { // Optional: Hover/focus text
text: "Click here", // Tooltip title
description: "Details" // Optional: Extended description
},
cameraOrientation: { // Optional: Camera direction when created
pitch: -0.02, // Vertical angle (radians)
yaw: 2.06 // Horizontal angle (radians)
}
}
]
}
]
}
`
Important Notes:
- scenes is an array (not an object)
- The library also accepts scenes as an object keyed by ID for backward compatibility
- Hotspot position is in 3D space (typically on 10-unit sphere surface)
---
#### Methods
##### tour.start()
Initialize and start the tour. Loads the initial scene and sets up event listeners.
Returns: void
Example:
`javascript
const tour = new SWT.Tour(sceneElement, config);
tour.start();
`
---
##### tour.navigateTo(sceneId)
Navigate to a specific scene by ID.
Parameters:
- sceneId (String) - Target scene ID (must exist in scenes object)
Returns: void
Example:
`javascript
tour.navigateTo("bedroom"); // Loads scene with id "bedroom"
`
---
##### tour.getCurrentSceneId()
Get the ID of the currently active scene.
Returns: String - Current scene ID
Example:
`javascript
const currentScene = tour.getCurrentSceneId();
console.log("Viewing:", currentScene); // "living-room"
`
---
##### tour.destroy()
Clean up and remove the tour. Removes all hotspots, event listeners, and resets scene.
Returns: void
Example:
`javascript
tour.destroy(); // Cleanup before removing from DOM
`
---
##### tour.addEventListener(eventName, callback)
Listen to tour events. Custom event system (not DOM events).
Parameters:
- eventName (String) - Event name (see Events section)
- callback (Function) - Handler function receiving event object
Returns: void
Example:
`javascript
tour.addEventListener("scene-loaded", (event) => {
console.log("Scene:", event.detail.sceneName);
});
`
---
#### Events
All events include a detail object with event-specific data.
##### tour-started
Fired when tour.start() is called.
Detail:
`javascript
{
config: Object; // Full tour configuration
}
`
---
##### scene-loading
Fired before a scene begins loading.
Detail:
`javascript
{
sceneId: String; // ID of scene being loaded
}
`
---
##### scene-loaded
Fired after a scene is fully loaded and rendered.
Detail:
`javascript
{
sceneId: String, // ID of loaded scene
sceneName: String // Display name of scene
}
`
---
##### hotspot-activated
Fired when a hotspot is clicked/activated.
Detail:
`javascript
{
hotspotId: String, // Hotspot ID
sceneId: String, // Current scene ID
action: Object // Hotspot action object { type, target }
}
`
---
#### Usage Example
`javascript
const scene = document.querySelector("#vr-scene");
const tour = new SWT.Tour(scene, {
initialScene: "room1",
scenes: {
room1: {
name: "Living Room",
panorama: "360-living-room.jpg",
hotspots: [
{
position: { x: 5, y: 0, z: -5 },
action: { type: "navigateTo", target: "room2" },
appearance: { color: "#FF6B6B" },
tooltip: { text: "Kitchen" },
},
],
},
room2: {
name: "Kitchen",
panorama: "360-kitchen.jpg",
hotspots: [],
},
},
});
// Listen to events
tour.addEventListener("tour-started", (e) => {
console.log("Tour configuration:", e.detail.config);
});
tour.addEventListener("scene-loading", (e) => {
console.log("Loading scene:", e.detail.sceneId);
// Show loading indicator
});
tour.addEventListener("scene-loaded", (e) => {
console.log("Loaded:", e.detail.sceneName);
// Hide loading indicator
});
tour.addEventListener("hotspot-activated", (e) => {
console.log("Hotspot clicked:", e.detail.hotspotId);
console.log("Navigating to:", e.detail.action.target);
});
// Start the tour
tour.start();
// Programmatic navigation
setTimeout(() => {
tour.navigateTo("room2");
}, 5000);
// Cleanup
// tour.destroy();
`
Editor Features
$3
- Click-to-Place Hotspots - Raycast-based placement on panorama sphere
- Real-Time Preview - Instant A-Frame rendering as you edit
- Scene Management - Add, remove, reorder scenes with thumbnails
- Camera Control - Auto-point camera to selected hotspot with animation
- Position Validation - Hotspots clamped to 10-unit sphere radius
$3
- 3D Position - Click-to-place or manual X/Y/Z coordinate input
- Navigation Target - Link to any scene in the tour
- Visual Customization - Color picker and scale slider
- Tooltips - Custom hover text for each hotspot
$3
- LocalStorage Persistence - Auto-save projects (configurable interval)
- Import/Export - Load and save tour JSON configurations
- Data URLs - Panoramas embedded as base64 (no external files needed)
- Thumbnail Generation - Auto-generated scene previews (100x50px)
$3
1. JSON Export - Portable configuration file for library integration
- Converts editor's imageUrl to library's panorama format
- Compatible with SWT.Tour viewer library
- Use in custom integrations or standalone viewer
2. Viewer Export - Self-contained HTML file
- Embeds minified swt.min.js library
- Includes full tour JSON configuration
- No external dependencies - works offline
- Drag-and-drop ready for distribution
$3
- ES6 Module Architecture - Six-controller pattern with clear separation
- Sourcemaps - Debug original ES6 source in browser DevTools
- Two Init Modes - Declarative (HTML) or Programmatic (JS API)
- Comprehensive Event System - 30+ events including unified change event for any modification
- Load/Export API - generateJSON() and loadJSON() for round-trip data management
- Global Access - All classes attached to window` for console debugging