{"_id":"360-image-player","name":"360-image-player","dist-tags":{"latest":"0.2.0"},"versions":{"0.2.0":{"name":"360-image-player","version":"0.2.0","description":"Framework-agnostic Three.js player for equirectangular 360 images.","type":"module","license":"GPL-3.0-only","sideEffects":false,"main":"./dist/360-image-player.min.cjs","module":"./dist/360-image-player.min.js","browser":"./dist/360-image-player.standalone.umd.min.js","unpkg":"./dist/360-image-player.standalone.umd.min.js","jsdelivr":"./dist/360-image-player.standalone.umd.min.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/360-image-player.min.js","require":"./dist/360-image-player.min.cjs","default":"./dist/360-image-player.min.js"},"./standalone":{"default":"./dist/360-image-player.standalone.umd.min.js"}},"publishConfig":{"access":"public"},"scripts":{"build":"vite build --config vite.config.ts && vite build --config vite.standalone.config.ts && tsc -p tsconfig.build.json","demo":"npm run build && vite demo --host 0.0.0.0","demo:build":"npm run build && vite build demo --outDir ../dist-demo --emptyOutDir --base ./","lint":"eslint . --max-warnings 0","typecheck":"tsc -p tsconfig.json --noEmit","test:unit":"vitest run --config vitest.config.ts","test:coverage":"vitest run --config vitest.config.ts --coverage","test:e2e":"playwright test","test:e2e:ui":"playwright test --ui"},"keywords":["360-image","webgl","panorama","equirectangular","three.js","virtual-tour"],"peerDependencies":{"react":">=16.8.0","three":">=0.164.0 <1"},"devDependencies":{"@eslint/js":"^10.0.1","@playwright/test":"^1.60.0","@testing-library/jest-dom":"^6.9.1","@testing-library/react":"^16.3.2","@types/node":"^25.6.0","@types/react":"^19.2.14","@types/react-dom":"^19.2.3","@types/three":"^0.164.0","@vitest/coverage-v8":"^4.1.8","eslint":"^10.2.1","globals":"^17.5.0","jsdom":"^29.1.0","react":"^19.2.5","react-dom":"^19.2.5","three":"^0.164.1","typescript":"^5.4.5","typescript-eslint":"^8.59.0","vite":"^8.0.16","vitest":"^4.1.5"},"dependencies":{"fflate":"^0.8.3"},"gitHead":"7f398688af0fab14aebe3e95626e7a151cf7deff","_id":"360-image-player@0.2.0","_nodeVersion":"25.9.0","_npmVersion":"11.12.1","dist":{"integrity":"sha512-pb/BKHuVIoyFLAaiyKuURBRuOzIM9nqtISKh7OCLghfNSoramzz6KaQH/YlYxE9Yo4kH/qOs90pdimoW/TB/ng==","shasum":"26227b2601170df8d363e8c3e9ed7d468f626d31","tarball":"https://registry.npmjs.org/360-image-player/-/360-image-player-0.2.0.tgz","fileCount":6,"unpackedSize":646828,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCpYGqKAtwTqAt0NWoYWhUy660zaiE+EQatIrbBSDzvWwIhANbkCr4EPszyVVeUCZBaby2Bew8TWPKR7K1Zq9jAzZEs"}]},"_npmUser":{"name":"mirame360","email":"info.mirame360@gmail.com"},"directories":{},"maintainers":[{"name":"mirame360","email":"info.mirame360@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/360-image-player_0.2.0_1785167473009_0.29621062768413453"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-27T15:51:12.821Z","0.2.0":"2026-07-27T15:51:13.148Z","modified":"2026-07-27T15:51:13.427Z"},"maintainers":[{"name":"mirame360","email":"info.mirame360@gmail.com"}],"description":"Framework-agnostic Three.js player for equirectangular 360 images.","keywords":["360-image","webgl","panorama","equirectangular","three.js","virtual-tour"],"license":"GPL-3.0-only","readme":"# 360 Image Player\n\nA framework-agnostic equirectangular image player built with Three.js, TypeScript, and Vite.\n\n## Features\n\n- Mouse, touch, pinch, inertia, zoom controls, compass, and WebXR.\n- Runtime color filters and configurable nadir covers.\n- Typed hotspots for links, quizzes, clues, locked content, and products.\n- Branded and MLS-friendly unbranded rendering modes.\n- Local canvas snapshots and high-resolution server snapshot requests.\n- Serializable player state and ZIP export for offline self-hosting.\n- React wrapper with typed callbacks.\n- GPL-3.0 licensed.\n\n## Hosting your source media\n\nThis player renders whatever equirectangular image URL you give it — you still need somewhere to upload, store, and serve the actual panorama files. [Mirame360](https://mirame360.com) 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](https://github.com/mirame360/mirame360-wordpress-embed) if your site runs WordPress).\n\n## Installation\n\n```bash\nnpm install 360-image-player three\n```\n\nReact is an optional peer dependency.\n\n## Basic Usage\n\n```typescript\nimport { Image360Player } from '360-image-player';\n\nconst player = new Image360Player({\n  container: document.getElementById('viewer')!,\n  imageUrl: [\n    'https://example.com/panorama.webp',\n    'https://example.com/panorama.jpeg',\n  ],\n  showControls: true,\n  compass: true,\n  initialView: { yaw: 20, pitch: 0, hfov: 80 },\n  nadir: {\n    imageUrl: 'https://example.com/logo.png',\n    radius: 55,\n  },\n});\n\nplayer.addHTMLOverlay({\n  id: 'product-1',\n  type: 'product',\n  yaw: 45,\n  pitch: 5,\n  product: {\n    id: 'chair-1',\n    title: 'Chair',\n    price: '$99',\n    vendor: 'shopify',\n  },\n});\n\nplayer.on('addtocart', ({ product }) => {\n  // Delegate the actual cart mutation to Shopify, PrestaShop, or the host app.\n  console.log(product);\n});\n```\n\n`imageUrl` accepts either one URL or an ordered list. When a source cannot be\nloaded or decoded, the player tries the next URL and emits `error` only after\nall configured sources have failed. Existing integrations using a string do\nnot need to change.\n\n## Viewport And Snapshots\n\n```typescript\nplayer.setView({ yaw: 30, pitch: -5, hfov: 70 });\nconst viewport = player.getView();\n\n// Optional low-cost motion for visible previews.\nplayer.startAutoRotate(2);\nplayer.setRenderingActive(false); // suspend GPU work while offscreen\nplayer.setRenderingActive(true);\nplayer.stopAutoRotate();\n\n// Browser-resolution PNG from the current canvas.\nconst localBlob = await player.takeSnapshot();\n\n// High-resolution image generated by the Mirame360 backend.\nconst result = await player.requestSnapshot({\n  endpoint: `/api/media/${mediaId}/snapshot/`,\n  width: 4096,\n  height: 2304,\n  format: 'jpeg',\n  headers: { Authorization: `Bearer ${token}` },\n});\n```\n\nThe backend endpoint queues an FFmpeg `v360` task and the player polls its authenticated status URL until the generated storage URL is available.\n\n## Gamification\n\n```typescript\nplayer.addHTMLOverlay({\n  id: 'question-1',\n  type: 'quiz',\n  yaw: 10,\n  pitch: 0,\n  title: 'Which door is correct?',\n  quizChoices: [\n    { id: 'left', label: 'Left' },\n    { id: 'right', label: 'Right', correct: true },\n  ],\n  unlocks: ['exit-door'],\n});\n\nplayer.addHTMLOverlay({\n  id: 'exit-door',\n  type: 'locked',\n  yaw: 90,\n  pitch: 0,\n  requires: ['exit-door'],\n  text: 'Exit',\n});\n\nplayer.setGameState(savedState);\nconst stateToPersist = player.getGameState();\n```\n\n## Branding And Security\n\nUse `brandingMode: 'unbranded'` to hide hotspots unless they explicitly set `branded: false`. External URL opening can also be disabled with `allowExternalLinks: false`.\n\nCustom hotspot HTML is sanitized by default. Supply `sanitizeHTML` to integrate a stricter application sanitizer.\n\n## Offline Export\n\n```typescript\nconst zip = await player.exportOffline({\n  playerScriptUrl: '/assets/360-image-player.standalone.umd.min.js',\n  fetchAssets: true,\n});\n```\n\nThe ZIP contains the standalone player, panorama, optional nadir image, `config.json`, and a ready-to-host `index.html`. Remote assets must allow CORS.\n\n## Main Options\n\n| Option | Default | Description |\n| --- | --- | --- |\n| `imageUrl` | required | Panorama URL or ordered list of primary and fallback URLs. |\n| `autoLoad` | `true` | Load the panorama immediately. |\n| `showControls` | `true` | Render zoom and reset controls. |\n| `compass` | `false` | Render a heading compass. |\n| `initialView` | `{ yaw: 0, pitch: 0, hfov: 90 }` | Initial viewport. |\n| `brandingMode` | `branded` | Hide branded content in `unbranded` mode. |\n| `allowExternalLinks` | `true` | Allow URL hotspots to open new pages. |\n| `nadir` | none | Circular nadir image configuration. |\n| `snapshotEndpoint` | none | Default high-resolution snapshot endpoint. |\n| `colorFilters` | neutral | WebGL color adjustments. |\n\n## Development\n\n- `npm run build`\n- `npm run lint`\n- `npm run typecheck`\n- `npm run test:unit`\n","readmeFilename":"README.md","_rev":"1-b42e87e245bef0cf0bfd23289ef2adf2"}