npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

senangwebs-tour

v1.0.2

Published

VR 360° virtual tour system with visual editor and viewer library

Downloads

19

Readme

SenangWebs Tour (SWT)

A powerful, data-driven 360° virtual tour system built on A-Frame WebVR. Create immersive virtual tours with a visual editor, or integrate the lightweight viewer library into your own projects. SenangWebs Tour Preview

Table of Contents

Features

Core Capabilities

  • Visual Editor - No-code tour builder with click-to-place hotspot interface
  • Viewer Library - Lightweight (12KB minified) JavaScript library for embedding tours
  • Standalone Viewer - Self-contained HTML viewer with drag-and-drop JSON support
  • Mobile & VR Ready - Full touch, mouse, and VR headset support
  • Customizable Hotspots - Custom colors, scales, and tooltip text
  • Multiple Export Formats - JSON configuration or standalone HTML files

Developer-Friendly

  • Two Integration Modes - Declarative (HTML attributes) or Programmatic (JavaScript API)
  • Modular Architecture - Six-controller editor pattern with clear separation of concerns
  • Event System - React to scene loads, hotspot clicks, and navigation events
  • Data-Driven - Pure JSON configuration, no internal state management
  • Built with Rollup - Modern ES6+ modules with sourcemaps for debugging

Quick Start

Using the Visual Editor (No Coding Required)

  1. Start Local Server (required for A-Frame CORS):

    npm install
    npm run serve
  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

Integrating with Code (Viewer Library)

Minimal Example (see examples/example-simple.html):

<!DOCTYPE html>
<html>
<head>
  <script src="https://aframe.io/releases/1.7.0/aframe.min.js"></script>
  <script src="https://cdn.jsdelivr.net/gh/a-hakim/senangwebs-tour@latest/dist/swt.min.js"></script>
</head>
<body>
  <a-scene id="tour-scene">
    <a-camera><a-cursor></a-cursor></a-camera>
  </a-scene>

  <script>
    const config = {
      initialScene: "room1",
      scenes: {
        room1: {
          name: "Living Room",
          panorama: "path/to/panorama1.jpg",
          hotspots: [{
            position: { x: 5, y: 0, z: -5 },
            action: { type: "navigateTo", target: "room2" },
            appearance: { color: "#FF6B6B", scale: 1.5 },
            tooltip: { text: "Go to Kitchen" }
          }]
        },
        room2: {
          name: "Kitchen",
          panorama: "path/to/panorama2.jpg",
          hotspots: [{
            position: { x: -5, y: 0, z: 5 },
            action: { type: "navigateTo", target: "room1" },
            appearance: { color: "#4ECDC4", scale: 1.5 },
            tooltip: { text: "Back to Living Room" }
          }]
        }
      }
    };

    const scene = document.querySelector('#tour-scene');
    scene.addEventListener('loaded', () => {
      const tour = new SWT.Tour(scene, config);
      tour.addEventListener('scene-loaded', (e) => {
        console.log('Now viewing:', e.detail.sceneName);
      });
      tour.start();
    });
  </script>
</body>
</html>

Custom Editor Integration

Build your own tour editor using the swt-editor.js bundle. Two initialization modes are supported:

Option 1: Declarative Mode (HTML Attributes)

Zero JavaScript - perfect for quick prototypes:

<!DOCTYPE html>
<html>
<head>
  <script src="https://aframe.io/releases/1.7.0/aframe.min.js"></script>
  <script src="dist/swt.js"></script>
  <script src="dist/swt-editor.js"></script>
  <link rel="stylesheet" href="dist/swt-editor.css">
</head>
<body>
  <!-- Auto-initializes on page load -->
  <div data-swt-editor 
       data-swt-auto-init="true"
       data-swt-project-name="My Virtual Tour">
    <div data-swt-scene-list></div>
    <div data-swt-preview-area></div>
    <div data-swt-properties-panel></div>
  </div>
</body>
</html>

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

Option 2: Programmatic Mode (JavaScript API)

Full control - for custom workflows and advanced integrations:

<!DOCTYPE html>
<html>
<head>
  <script src="https://aframe.io/releases/1.7.0/aframe.min.js"></script>
  <script src="dist/swt.js"></script>
  <script src="dist/swt-editor.js"></script>
  <link rel="stylesheet" href="dist/swt-editor.css">
</head>
<body>
  <div id="editor-container">
    <div id="scenes"></div>
    <div id="preview"></div>
    <div id="properties"></div>
  </div>

  <script>
    // Create editor instance with custom config
    const editor = new TourEditor({
      projectName: 'My Custom Tour',
      autoSave: true,
      autoSaveInterval: 30000
    });

    // Initialize with DOM elements
    editor.init({
      sceneListElement: document.getElementById('scenes'),
      previewElement: document.getElementById('preview'),
      propertiesElement: document.getElementById('properties')
    });

    // Access editor programmatically
    editor.addEventListener('scene-added', (scene) => {
      console.log('New scene:', scene.name);
    });

    // Export tour configuration
    const config = editor.exportJSON();
  </script>
</body>
</html>

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

Installation & Build

# Install dependencies
npm install

# Build all bundles (viewer + editor)
npm run build

# Development mode (watch for changes)
npm run dev

# Serve locally (required for A-Frame CORS)
npm run serve
# Access at http://localhost:8080

Build Output

| File | Size | Format | Purpose | |------|------|--------|---------| | dist/swt.js | 26KB | UMD | Viewer library (development) | | dist/swt.min.js | 12KB | UMD | Viewer library (production) | | dist/swt-editor.js | 89KB | IIFE | Editor bundle (development) | | dist/swt-editor.min.js | 38KB | IIFE | Editor bundle (production) | | dist/swt-editor.css | 39KB | CSS | Editor styles (development) | | dist/swt-editor.min.css | 25KB | CSS | Editor styles (production) |

All builds include sourcemaps for debugging.

Project Structure

src/
├── index.js                    # Viewer library entry (UMD export)
├── AssetManager.js             # Panorama preloading
├── SceneManager.js             # Sky entity, transitions, fades
├── HotspotManager.js           # Hotspot creation & updates
├── components/
│   └── hotspot-listener.js     # A-Frame hotspot component
└── editor/
    ├── editor-entry.js         # Editor bundle entry (IIFE)
    ├── editor-entry.css        # CSS bundle entry (@import)
    ├── css/
    │   └── main.css           # Editor UI styles
    └── js/
        ├── editor.js           # Main coordinator (TourEditor)
        ├── scene-manager.js    # Scene CRUD (SceneManagerEditor)
        ├── hotspot-editor.js   # Hotspot placement (HotspotEditor)
        ├── preview-controller.js # A-Frame preview (PreviewController)
        ├── ui-controller.js    # DOM rendering (UIController)
        ├── storage-manager.js  # LocalStorage (ProjectStorageManager)
        ├── export-manager.js   # JSON/HTML export (ExportManager)
        ├── utils.js            # Helper functions
        └── ui-init.js          # DOMContentLoaded initialization

examples/
├── example.html               # Full viewer demo
├── example-simple.html        # Minimal viewer demo
├── editor.html               # Full editor demo
├── editor-declarative.html   # Declarative editor demo
└── viewer.html               # Standalone drag-and-drop viewer

API Documentation

Viewer Library API

Constructor

new SWT.Tour(aframeSceneElement, tourConfiguration)

Parameters:

  • aframeSceneElement (HTMLElement) - A-Frame <a-scene> DOM element
  • tourConfiguration (Object) - Tour config (see structure below)

Returns: Tour instance


Configuration Structure

{
  initialScene: "scene-id",       // Required: Starting scene ID
  scenes: {                        // Required: Object (not array!)
    "scene-id": {                  // Key = scene ID
      name: "Scene Name",          // Required: Display name
      panorama: "url-or-dataurl",  // Required: Image URL or base64
      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.5,            // Default: 1.0 (number or "x y z" string)
            icon: "arrow"          // Default: sphere (future: custom icons)
          },
          tooltip: {               // Optional: Hover/focus text
            text: "Click to navigate" // Tooltip content
          }
        }
      ]
    }
  }
}

Important Notes:

  • scenes is an object (keys are scene IDs), not an array
  • Hotspot position is in 3D space (typically on 10-unit sphere surface)
  • Editor stores imageUrl, library expects panorama (export handles conversion)
  • Images can be URLs or Data URLs (base64) for offline tours

Methods

tour.start()

Initialize and start the tour. Loads the initial scene and sets up event listeners.

Returns: void

Example:

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:

tour.navigateTo('bedroom'); // Loads scene with id "bedroom"

tour.getCurrentSceneId()

Get the ID of the currently active scene.

Returns: String - Current scene ID

Example:

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:

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:

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:

{
  config: Object // Full tour configuration
}

scene-loading

Fired before a scene begins loading.

Detail:

{
  sceneId: String // ID of scene being loaded
}

scene-loaded

Fired after a scene is fully loaded and rendered.

Detail:

{
  sceneId: String,   // ID of loaded scene
  sceneName: String  // Display name of scene
}

hotspot-activated

Fired when a hotspot is clicked/activated.

Detail:

{
  hotspotId: String,     // Hotspot ID
  sceneId: String,       // Current scene ID
  action: Object         // Hotspot action object { type, target }
}

Usage Example

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

Visual Tour Creation

  • 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

Hotspot Configuration

  • 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

Data Management

  • 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)

Export Options

  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

Developer Tools

  • 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)
  • Event System - React to scene-added, hotspot-clicked events
  • Global Access - All classes attached to window for console debugging

Browser Compatibility

| Browser | Version | Notes | |---------|---------|-------| | Chrome | 90+ | Recommended - best performance | | Firefox | 88+ | Full support | | Safari | 14+ | WebGL support required | | Edge | 90+ | Chromium-based | | Mobile Safari | iOS 14+ | Touch and gyroscope support | | Chrome Mobile | Android 90+ | Touch and gyroscope support |

Requirements:

  • WebGL 1.0 or higher
  • ES6 module support (for editor)
  • LocalStorage (for editor persistence)

VR Headsets:

  • Oculus Quest 1/2/3
  • Meta Quest Pro
  • HTC Vive
  • Valve Index
  • Any WebXR-compatible headset

Documentation

Contributing

Contributions are welcome! Here's how you can help:

  1. Report Bugs - Open an issue with reproduction steps
  2. Suggest Features - Share your ideas in GitHub Issues
  3. Submit PRs - Fork, create a feature branch, and submit a pull request
  4. Improve Docs - Help make documentation clearer and more comprehensive

Development Workflow

# Fork and clone the repository
git clone https://github.com/your-username/senangwebs-tour.git
cd senangwebs-tour

# Install dependencies
npm install

# Start development server with watch mode
npm run dev

# In another terminal, serve the examples
npm run serve

# Make changes, test in browser at http://localhost:8080
# Build for production
npm run build

# Submit a pull request

Code Guidelines

  • Use ES6+ syntax (modules, classes, arrow functions)
  • Follow existing naming conventions (see copilot-instructions.md)
  • No debug console.log() - only console.error() for critical errors
  • All editor classes must use export default
  • Debounce text inputs (300ms) using debounce() from utils.js
  • Test in Chrome, Firefox, and Safari before submitting

License

MIT License - see LICENSE.md for details.

Acknowledgments

  • A-Frame - WebVR framework powering the 3D rendering
  • Rollup - Module bundler for clean ES6+ builds