title: Threejs particle system with dynamic lines
body: <p>I am trying to create a very basic particle system which has connecting lines. The logic for the lines is based on nearby particles. Any particles close enough get a line between them. I have worked out the logic but it seems a bit buggy. I am not sure if I am missing something of if there is a better way to do it. Currently there are lines that flicker, which I cannot understand.</p>
  
  <p>Have a look at the demo:</p>
  
  <p><a href="https://jsfiddle.net/awinhayden/387c3xa7/3/" rel="nofollow noreferrer">https://jsfiddle.net/awinhayden/387c3xa7/3/</a></p>
  
  <pre><code>    var canvas, canvasDom, ctx, scene, renderer, camera, controls, geocoder, deviceOrientation = false;
      var width = 800,
          height = 600;
      var particleCount = 30;
  
  
      var pMaterial = new THREE.PointsMaterial({
          color: 0x000000,
          size: 0.5,
          blending: THREE.AdditiveBlending,
          //depthTest: false,
          //transparent: true
      });
      var particles = new THREE.Geometry;
      var particleSystem;
      var line;
      var lines = {};
      var lineGroup = new THREE.Group();
      var lineMaterial = new THREE.LineBasicMaterial({
          color: 0x000000,
          linewidth: 1
      });
  
      var clock = new THREE.Clock();
      var maxDistance = 15;
  
      function init() {
          canvasDom = document.getElementById('canvas');
          setupStage();
          setupRenderer();
          setupCamera();
          setupControls();
          setupLights();
          clock.start();
          window.addEventListener('resize', onWindowResized, false);
          onWindowResized(null);
          createParticles();
          scene.add(lineGroup);
          animate();
      }
  
      function setupStage() {
          scene = new THREE.Scene();
      }
  
      function setupRenderer() {
          renderer = new THREE.WebGLRenderer({
              canvas: canvasDom,
              logarithmicDepthBuffer: true
          });
          renderer.setSize(width, height);
          renderer.setClearColor(0xfff6e6);
      }
  
      function setupCamera() {
          camera = new THREE.PerspectiveCamera(70, width / height, 1, 10000);
          camera.position.set(0, 0, -60);
      }
  
      function setupControls() {
          if (deviceOrientation) {
              controls = new THREE.DeviceOrientationControls(camera);
              controls.connect();
          } else {
              controls = new THREE.OrbitControls(camera, renderer.domElement);
              controls.target = new THREE.Vector3(0, 0, 0);
          }
      }
  
      function setupLights() {
          var light1 = new THREE.AmbientLight(0xffffff, 0.5); // soft white light
          var light2 = new THREE.PointLight(0xffffff, 1, 0);
  
          light2.position.set(100, 200, 100);
  
          scene.add(light1);
          scene.add(light2);
      }
  
      function animate() {
          requestAnimationFrame(animate);
          controls.update();
          animateParticles();
          updateLines();
          render();
      }
  
      function render() {
          renderer.render(scene, camera);
      }
  
      function onWindowResized(event) {
          width = window.innerWidth;
          height = window.innerHeight;
          camera.aspect = width / height;
          camera.updateProjectionMatrix();
          renderer.setSize(width, height);
      }
  
      function createParticles() {
          for (var i = 0; i &lt; particleCount; i++) {
              var pX = Math.random() * 50 - 25,
                  pY = Math.random() * 50 - 25,
                  pZ = Math.random() * 50 - 25,
                  particle = new THREE.Vector3(pX, pY, pZ);
              particle.diff = Math.random() + 0.2;
              particle.default = new THREE.Vector3(pX, pY, pZ);
              particle.offset = new THREE.Vector3(0, 0, 0);
              particle.velocity = {};
              particle.velocity.y = particle.diff * 0.5;
              particle.nodes = [];
              particles.vertices.push(particle);
          }
          particleSystem = new THREE.Points(particles, pMaterial);
          particleSystem.position.y = 0;
          scene.add(particleSystem);
      }
  
      function animateParticles() {
          var pCount = particleCount;
          while (pCount--) {
              var particle = particles.vertices[pCount];
              var move = Math.sin(clock.getElapsedTime() * (1 * particle.diff)) / 4;
  
              particle.offset.y += move * particle.velocity.y;
              particle.y = particle.default.y + particle.offset.y;
  
              detectCloseByPoints(particle);
          }
          particles.verticesNeedUpdate = true;
          particleSystem.rotation.y += 0.01;
          lineGroup.rotation.y += 0.01;
      }
  
      function updateLines() {
          for (var _lineKey in lines) {
              if (!lines.hasOwnProperty(_lineKey)) {
                  continue;
              }
              lines[_lineKey].geometry.verticesNeedUpdate = true;
          }
      }
  
      function detectCloseByPoints(p) {
          var _pCount = particleCount;
          while (_pCount--) {
              var _particle = particles.vertices[_pCount];
              if (p !== _particle) {
  
                  var _distance = p.distanceTo(_particle);
                  var _connection = checkConnection(p, _particle);
  
                  if (_distance &lt; maxDistance) {
                      if (!_connection) {
                          createLine(p, _particle);
                      }
                  } else if (_connection) {
                      removeLine(_connection);
                  }
              }
          }
      }
  
      function checkConnection(p1, p2) {
          var _childNode, _parentNode;
          _childNode = p1.nodes[particles.vertices.indexOf(p2)] || p2.nodes[particles.vertices.indexOf(p1)];
          if (_childNode &amp;&amp; _childNode !== undefined) {
              _parentNode = (_childNode == p1) ? p2 : p1;
          }
          if (_parentNode &amp;&amp; _parentNode !== undefined) {
              return {
                  parent: _parentNode,
                  child: _childNode,
                  lineId: particles.vertices.indexOf(_parentNode) + '-' + particles.vertices.indexOf(_childNode)
              };
          } else {
              return false;
          }
      }
  
      function removeLine(_connection) {
          // Could animate line out
          var childIndex = particles.vertices.indexOf(_connection.child);
          _connection.parent.nodes.splice(childIndex, 1);
          deleteLine(_connection.lineId);
      }
  
      function deleteLine(_id) {
          lineGroup.remove(lines[_id]);
          delete lines[_id];
      }
  
      function addLine(points) {
          var points = points || [new THREE.Vector3(Math.random() * 10, Math.random() * 10, Math.random() * 10), new THREE.Vector3(0, 0, 0)];
          var _lineId = particles.vertices.indexOf(points[0]) + '-' + particles.vertices.indexOf(points[1]);
          var lineGeom = new THREE.Geometry();
          if (!lines[_lineId]) {
              lineGeom.dynamic = true;
              lineGeom.vertices.push(points[0]);
              lineGeom.vertices.push(points[1]);
              var curLine = new THREE.Line(lineGeom, lineMaterial);
              curLine.touched = false;
              lines[_lineId] = curLine;
              lineGroup.add(curLine);
              return curLine;
          } else {
              return false;
          }
      }
  
      function createLine(p1, p2) {
          p1.nodes[particles.vertices.indexOf(p2)] = p2;
          addLine([p1, p2]);
      }
  
      $(document).ready(function() {
          init();
      });
  </code></pre>
  
  <p>What is causing the flickering? I am also hoping for some advice on how to improve the logic and performance. I am hoping to increase the number of particles.</p>
  
  <p>Any help is really appreciated.</p>