360-image-player
v0.2.0
Published
Framework-agnostic Three.js player for equirectangular 360 images.
Maintainers
Readme
360 Image Player
A framework-agnostic equirectangular image player built with Three.js, TypeScript, and Vite.
Features
- Mouse, touch, pinch, inertia, zoom controls, compass, and WebXR.
- Runtime color filters and configurable nadir covers.
- Typed hotspots for links, quizzes, clues, locked content, and products.
- Branded and MLS-friendly unbranded rendering modes.
- Local canvas snapshots and high-resolution server snapshot requests.
- Serializable player state and ZIP export for offline self-hosting.
- React wrapper with typed callbacks.
- GPL-3.0 licensed.
Hosting your source media
This player renders whatever equirectangular image URL you give it — you still need somewhere to upload, store, and serve the actual panorama files. Mirame360 is a free 360° photo/video hosting platform built by the same team: upload once, get optimized web-ready URLs and secure embeds, and drop them straight into this player (or use the Mirame360 WordPress plugin if your site runs WordPress).
Installation
npm install 360-image-player threeReact is an optional peer dependency.
Basic Usage
import { Image360Player } from '360-image-player';
const player = new Image360Player({
container: document.getElementById('viewer')!,
imageUrl: [
'https://example.com/panorama.webp',
'https://example.com/panorama.jpeg',
],
showControls: true,
compass: true,
initialView: { yaw: 20, pitch: 0, hfov: 80 },
nadir: {
imageUrl: 'https://example.com/logo.png',
radius: 55,
},
});
player.addHTMLOverlay({
id: 'product-1',
type: 'product',
yaw: 45,
pitch: 5,
product: {
id: 'chair-1',
title: 'Chair',
price: '$99',
vendor: 'shopify',
},
});
player.on('addtocart', ({ product }) => {
// Delegate the actual cart mutation to Shopify, PrestaShop, or the host app.
console.log(product);
});imageUrl accepts either one URL or an ordered list. When a source cannot be
loaded or decoded, the player tries the next URL and emits error only after
all configured sources have failed. Existing integrations using a string do
not need to change.
Viewport And Snapshots
player.setView({ yaw: 30, pitch: -5, hfov: 70 });
const viewport = player.getView();
// Optional low-cost motion for visible previews.
player.startAutoRotate(2);
player.setRenderingActive(false); // suspend GPU work while offscreen
player.setRenderingActive(true);
player.stopAutoRotate();
// Browser-resolution PNG from the current canvas.
const localBlob = await player.takeSnapshot();
// High-resolution image generated by the Mirame360 backend.
const result = await player.requestSnapshot({
endpoint: `/api/media/${mediaId}/snapshot/`,
width: 4096,
height: 2304,
format: 'jpeg',
headers: { Authorization: `Bearer ${token}` },
});The backend endpoint queues an FFmpeg v360 task and the player polls its authenticated status URL until the generated storage URL is available.
Gamification
player.addHTMLOverlay({
id: 'question-1',
type: 'quiz',
yaw: 10,
pitch: 0,
title: 'Which door is correct?',
quizChoices: [
{ id: 'left', label: 'Left' },
{ id: 'right', label: 'Right', correct: true },
],
unlocks: ['exit-door'],
});
player.addHTMLOverlay({
id: 'exit-door',
type: 'locked',
yaw: 90,
pitch: 0,
requires: ['exit-door'],
text: 'Exit',
});
player.setGameState(savedState);
const stateToPersist = player.getGameState();Branding And Security
Use brandingMode: 'unbranded' to hide hotspots unless they explicitly set branded: false. External URL opening can also be disabled with allowExternalLinks: false.
Custom hotspot HTML is sanitized by default. Supply sanitizeHTML to integrate a stricter application sanitizer.
Offline Export
const zip = await player.exportOffline({
playerScriptUrl: '/assets/360-image-player.standalone.umd.min.js',
fetchAssets: true,
});The ZIP contains the standalone player, panorama, optional nadir image, config.json, and a ready-to-host index.html. Remote assets must allow CORS.
Main Options
| Option | Default | Description |
| --- | --- | --- |
| imageUrl | required | Panorama URL or ordered list of primary and fallback URLs. |
| autoLoad | true | Load the panorama immediately. |
| showControls | true | Render zoom and reset controls. |
| compass | false | Render a heading compass. |
| initialView | { yaw: 0, pitch: 0, hfov: 90 } | Initial viewport. |
| brandingMode | branded | Hide branded content in unbranded mode. |
| allowExternalLinks | true | Allow URL hotspots to open new pages. |
| nadir | none | Circular nadir image configuration. |
| snapshotEndpoint | none | Default high-resolution snapshot endpoint. |
| colorFilters | neutral | WebGL color adjustments. |
Development
npm run buildnpm run lintnpm run typechecknpm run test:unit
