three.js dev template - module

HTML

<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
    
<script type="importmap">
	{
		"imports": {
			"three": "https://unpkg.com/three/build/three.module.js",
      "three/addons/": "https://unpkg.com/three/examples/jsm/"
		}
	}
</script>

<canvas id=web-gl></canvas>

CSS

body {
	margin: 0px;
}

JavaScript

import * as THREE from "three"
import { PointerLockControls } from 'three/addons/controls/PointerLockControls.js';

let camera, scene, renderer, controls // ThreeJS globals
let sizes

init()

function init() {
  const canvas = document.querySelector("canvas#web-gl")

  scene = new THREE.Scene()

  sizes = { width: window.innerWidth, height: window.innerHeight }
  window.addEventListener("resize", () => {
    sizes.width = window.innerWidth
    sizes.height = window.innerHeight

    camera.aspect = sizes.width / sizes.height
    camera.updateProjectionMatrix()

    renderer.setSize(sizes.width, sizes.height)
    renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
  })

  camera = new THREE.PerspectiveCamera(60, sizes.width / sizes.height, 0.1, 100)
  camera.position.set(0, 0, 0)
  scene.add(camera)

  renderer = new THREE.WebGLRenderer({
    canvas: canvas,
  })
  renderer.setSize(sizes.width, sizes.height)
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
  renderer.setAnimationLoop(() => {
    renderer.render(scene, camera)
  })

  controls = new PointerLockControls(camera, document.body)
  window.addEventListener("click", () => controls.lock())

  generateCloud(initCloudData({ length: 20 })).forEach((obj) => scene.add(obj))
}

function initDotData() {
  return {
    position: new THREE.Vector3(
      (Math.random() - 0.5) * 2,
      (Math.random() - 0.5) * 2,
      (Math.random() - 0.5) * 2,
    ).multiplyScalar(10),
    color: new THREE.Color(Math.random() * 0xffffff),
  }
}
function initCloudData({ length }, acc = []) {
  if (length === 0) return acc
  return initCloudData({ length: length - 1 }, [...acc, initDotData()])
}

function generateDot({ position, color }) {
  const sphere = generateSphere(color)
  sphere.position.copy(position)

  const spriteArray = [generateSprite(color), generateSprite(color)]
  const group = new THREE.Group()
  group.add(...spriteArray)
  group.position.copy(position)
  spriteArray[0].position.set(1, 0, 0)
...