Equirectangular Panorama

HTML

<!DOCTYPE html>
<html lang="en">
	<head>
		<title>three.js webgl - equirectangular panorama</title>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
	</head>
	<body>

		<div id="container"></div>
	
	</body>
</html>

CSS

body {
	margin: 0;
}

canvas {
	display: block;
}

JavaScript

import * as THREE from 'https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js';
import {
  OrbitControls
} from 'https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/controls/OrbitControls.js';

var camera, scene, renderer;


init();
animate();

function init() {

  var container, mesh;

  container = document.getElementById('container');

  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 1, 1100);
  camera.position.x = 0.01;

  scene = new THREE.Scene();

  var geometry = new THREE.SphereBufferGeometry(500, 60, 40);
  // invert the geometry on the x-axis so that all of the faces point inward
  geometry.scale(-1, 1, 1);

  var texture = new THREE.TextureLoader().load('https://i.imgur.com/JQj0qVk.jpg');
  var material = new THREE.MeshBasicMaterial({
    map: texture
  });

  mesh = new THREE.Mesh(geometry, material);

  scene.add(mesh);

  renderer = new THREE.WebGLRenderer();
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(window.innerWidth, window.innerHeight);
  container.appendChild(renderer.domElement);

  //

  window.addEventListener('resize', onWindowResize, false);
	
	const controls = new OrbitControls( camera, renderer.domElement );
	controls.minAzimuthAngle = 0;
	controls.maxAzimuthAngle =  Math.PI;

}

function onWindowResize() {

  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();

  renderer.setSize(window.innerWidth, window.innerHeight);

}

function animate() {

  requestAnimationFrame(animate);
  update();

}

function update() {

  renderer.render(scene, camera);

}