<div id="overlay" style="visibility:hidden">
		<div>
			<button id="startButton">Click to Play</button>
			<p>Video playback with audio requires user interaction.</p>
		</div>
	</div>
	<div id="container"></div>

	<video id="video" loop crossOrigin="anonymous" autoplay="true" muted="muted" playsinline style="display:none">
		<source src="http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4"
			type='video/mp4; codecs="avc1.42E01E, mp4a.40.2"'>
	</video>
body {
	margin: 0;
	background-color: #000;
	color: #fff;
	font-family: Monospace;
	font-size: 13px;
	line-height: 24px;
	overscroll-behavior: none;
}

a {
	color: #ff0;
	text-decoration: none;
}

a:hover {
	text-decoration: underline;
}

button {
	cursor: pointer;
	text-transform: uppercase;
}

canvas {
	display: block;
}

#info {
	position: absolute;
	top: 0px;
	width: 100%;
	padding: 10px;
	box-sizing: border-box;
	text-align: center;
	-moz-user-select: none;
	-webkit-user-select: none;
	-ms-user-select: none;
	user-select: none;
	pointer-events: none;
	z-index: 1; /* TODO Solve this in HTML */
}

a, button, input, select {
	pointer-events: auto;
}

.dg.ac {
	-moz-user-select: none;
	-webkit-user-select: none;
	-ms-user-select: none;
	user-select: none;
	z-index: 2 !important; /* TODO Solve this in HTML */
}

#overlay {
	position: absolute;
	z-index: 2;
	top: 0;
	left: 0;
	width: 100%;
	height:100%;
	display: flex;
	align-items: center;
	justify-content: center;
	opacity: 1;
	background-color: #000000;
	color: #ffffff;
}

#overlay > div {
	text-align: center;
}

#overlay > div > button {
	height: 20px;
	background: transparent;
	color: #ffffff;
	outline: 1px solid #ffffff;
	border: 0px;
	cursor: pointer;
}

#overlay > div > p {
	color: #777777;
	font-size: 12px;
}
import * as THREE from 'https://threejs.org/build/three.module.js';

var container;

var camera, scene, renderer;

var video, texture, material, mesh, groupMesh;

const mouseVec = new THREE.Vector3(0, 0, 0);
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;

var cube_count,

  meshes = [],
  materials = [],

  xgrid = 20 * 2,
  ygrid = 10 * 2;

window.mobileCheck = function () {
  let check = false;
  (function (a) { if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) check = true; })(navigator.userAgent || navigator.vendor || window.opera);
  return check;
};

if (mobileCheck()) {
  var overlay = document.getElementById('overlay');
  overlay.style.visibility = 'visible'

  var startButton = document.getElementById('startButton');
  startButton.addEventListener('click', function () {

    init();
    animate();

  }, false);
} else {
  init();
  animate();
}

function init() {

  var overlay = document.getElementById('overlay');
  overlay.remove();

  container = document.createElement('div');
  document.body.appendChild(container);

  camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000);
  camera.position.z = 500;

  scene = new THREE.Scene();

  var light = new THREE.DirectionalLight(0xffffff);
  light.position.set(0.5, 1, 1).normalize();
  scene.add(light);

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

  video = document.getElementById('video');
  video.play();

  texture = new THREE.VideoTexture(video);

  var i, j, ux, uy, ox, oy,
    geometry,
    xsize, ysize;

  ux = 1 / xgrid;
  uy = 1 / ygrid;

  xsize = 480 / xgrid;
  ysize = 204 / ygrid;
  var parameters = { color: 0xffffff, map: texture, wireframe: true };

  cube_count = 0;
  groupMesh = new THREE.Object3D();
  for (i = 0; i < xgrid; i++)
    for (j = 0; j < ygrid; j++) {

      ox = i;
      oy = j;

      // geometry = new THREE.SphereBufferGeometry(xsize, ysize, xsize);
      geometry = new THREE.BoxBufferGeometry(xsize, ysize, xsize, 5, 5, 5);

      change_uvs(geometry, ux, uy, ox, oy);

      materials[cube_count] = new THREE.MeshLambertMaterial(parameters);

      material = materials[cube_count];

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

      mesh.position.x = (i - xgrid / 2) * xsize;
      mesh.position.y = (j - ygrid / 2) * ysize;
      mesh.position.z = 0;

      mesh.initialPosition = {
        x: (i - xgrid / 2) * xsize,
        y: (j - ygrid / 2) * ysize,
        z: 0
      }

      mesh.initialRotation = {
        x: mesh.rotation.x,
        y: mesh.rotation.y,
        z: mesh.rotation.z
      }

      mesh.scale.x = mesh.scale.y = mesh.scale.z = 1;

      mesh.dx = 0.01 * (0.5 - Math.random());
      mesh.dy = 0.01 * (0.5 - Math.random());

      meshes[cube_count] = mesh;
      groupMesh.add(mesh);
      cube_count += 1;

    }

  scene.add(groupMesh);

  renderer.autoClear = false;

  //
  if (mobileCheck()) {
    document.addEventListener('touchmove', onDocumentMouseMove, false);
  } else {
    document.addEventListener('mousemove', onDocumentMouseMove, false);
  }


  window.addEventListener('resize', onWindowResize, false);

}

function onDocumentMouseMove(event) {
  const x = event.touches ? event.touches[0].clientX : event.clientX
  const y = event.touches ? event.touches[0].clientY : event.clientY
  mouse.x = (x / window.innerWidth) * 2 - 1;
  mouse.y = - (y / window.innerHeight) * 2 + 1;
}

function onWindowResize() {

  windowHalfX = window.innerWidth / 2;
  windowHalfY = window.innerHeight / 2;

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

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

}

function change_uvs(geometry, unitx, unity, offsetx, offsety) {

  var uvs = geometry.attributes.uv.array;
  for (var i = 0; i < uvs.length; i += 2) {
    uvs[i] = (uvs[i] + offsetx) * unitx;
    uvs[i + 1] = (uvs[i + 1] + offsety) * unity;
  }

}

function animate() {

  requestAnimationFrame(animate);

  render();

}

var h, counter = 1;

function render() {

  var time = Date.now() * 0.00005;

  // update the picking ray with the camera and mouse position
  raycaster.setFromCamera(mouse, camera);

  var intersects = raycaster.intersectObjects(meshes);

  if (intersects.length) {
    const { x, y } = intersects[0].point;
    const intersectObj = intersects[0].object;

    for (var i = 0; i < cube_count; i++) {
      mesh = meshes[i];

      // NOTE: Paint intersect object
      if (intersectObj === mesh) {
        mesh.material.color.set(0xff0000);
      } else {
        mesh.material.color.set(0xffffff);
      }

      // NOTE: The range of circle to rotate object
      const dist = mesh.position.distanceTo(intersects[0].point);

      if (dist < 30) {
        mesh.rotation.x += 10 * mesh.dx;
        mesh.rotation.y += 10 * mesh.dy;
      } else {
        mesh.rotation.x = mesh.initialRotation.x;
        mesh.rotation.y = mesh.initialRotation.y;
        mesh.rotation.z = mesh.initialRotation.z;
      }
    }
  }

  renderer.clear();
  renderer.render(scene, camera);
}
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.