<script type="x-shader/x-vertex" id="vertexshader">
  #include <color_pars_vertex>
  varying float vOpacity;
  attribute float opacity;
  void main() {
    #include <color_vertex>
    vOpacity = opacity;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  }
</script>

<script type="x-shader/x-fragment" id="fragmentshader">
  #include <color_pars_vertex>
  varying float vOpacity;
  void main() {
    gl_FragColor = vec4(vColor, vOpacity);
  }
</script>
canvas { 
  width: 100%;
  height: 100%;
}
console.log(Date.now());

const nodesDepthWrite = true;
const linksDepthWrite = true;
const nodesOpacity = 0.9;
const linksOpacity = 0.5;

const fov = 75;
const near = 0.1;
const far  = 1000;
const aspect = window.innerWidth / window.innerHeight;

let scene, camera, renderer, controls;

function init() {
  scene = new THREE.Scene();
  scene.background = new THREE.Color('#181b1c');
  camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  camera.position.z = 11;
  
  renderer = new THREE.WebGLRenderer();
  renderer.setSize( window.innerWidth, window.innerHeight );
  document.body.appendChild( renderer.domElement );
  controls = new THREE.TrackballControls(camera, renderer.domElement);
  controls.rotateSpeed = 1.382;
  controls.minDistance = 5;
  controls.maxDistance = 1000;
  controls.update();
  createNodes();
  createLinks();
  
  function render(){
    requestAnimationFrame( render );
    controls.update();
    renderer.render( scene, camera );
  }
  render();
}
init();

function createNodeImage() {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  const r = 120;
  canvas.width = 256;
  canvas.height = 256;
  ctx.beginPath();
  ctx.arc(r, r, r, 0, 2 * Math.PI, false);
  ctx.fillStyle = '#F9FFD9';
  ctx.fill();
  ctx.lineWidth = 5;
  ctx.strokeStyle = 'white';
  ctx.stroke();
  return canvas.toDataURL();
}

function createNode(x, y, z) {
  const img = createNodeImage();
  const texture = new THREE.TextureLoader().load(img);
  const material = new THREE.SpriteMaterial({
    map: texture,
    transparent: nodesOpacity < 1,
    opacity: nodesOpacity,
    depthWrite: nodesDepthWrite,
  });
  const node = new THREE.Sprite(material);
  node.position.set(x, y, z);
  return node;
}

function createNodes() {
  let i = 0;
  while (i < nPos.length) {
    scene.add(createNode(nPos[i++], nPos[i++], nPos[i++]));
  }
}

function createLinks() {
  const linkPos = [], linkColor = [], linkOpacity = []
  for (var i = 0; i < nPos.length; i += 3) {
    for (var j = i + 3; j < nPos.length; j += 3) {
      linkPos.push(nPos[i], nPos[i + 1], nPos[i + 2]);
      linkPos.push(nPos[j], nPos[j + 1], nPos[j + 2]);
      linkColor.push(1, 1, 1, 1, 1, 1);
      linkOpacity.push(linksOpacity, linksOpacity);
    }
  }
  
  const linksGeometry = new THREE.BufferGeometry();
  linksGeometry.addAttribute('position', new THREE.Float32BufferAttribute(linkPos, 3));
  linksGeometry.addAttribute('color', new THREE.Float32BufferAttribute(linkColor, 3));
  linksGeometry.addAttribute(
    'opacity',
    new THREE.Float32BufferAttribute(linkOpacity, 1),
  );
  
  const linksMaterial = new THREE.ShaderMaterial({
    vertexColors: THREE.VertexColors,
    vertexShader: document.getElementById( 'vertexshader' ).textContent,
    fragmentShader: document.getElementById( 'fragmentshader' ).textContent,
    transparent: linksOpacity < 1,
    depthWrite: linksDepthWrite,
  });
  
  const linksMesh = new THREE.LineSegments(linksGeometry, linksMaterial);
  scene.add(linksMesh);
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/three.js/98/three.min.js
  2. https://rawgit.com/mrdoob/three.js/master/examples/js/controls/TrackballControls.js
  3. https://codepen.io/aleksanderd/pen/jQLLKw.js