body {
	background-color: #000;
	margin: 0px;
	overflow: hidden;
}
const vshader = `
    precision highp float;

    uniform mat4 modelViewMatrix;
    uniform mat4 projectionMatrix;

    attribute vec3 position;
    attribute vec3 translate;
    attribute vec4 color;
    attribute float scale;
    attribute vec2 uv;

    varying vec4 vColor;
    varying vec2 vUv;

    void main() {
        vec4 mvPosition = modelViewMatrix * vec4( translate, 1.0 );
        mvPosition.xyz += position * scale;
        vColor = color;
        vUv = uv;
        gl_Position = projectionMatrix * mvPosition;
    }
`;

const fshader = `
    precision highp float;

    uniform float inner;
    uniform float outer;

    varying vec4 vColor;
    varying vec2 vUv;
    
    void main() {
        vec2 uv = vUv;
        uv -= 0.5;
        
        float dist = length(uv);
        float mixing = smoothstep(inner - 0.01, inner, dist) - smoothstep(outer - 0.01, outer,  dist);
        
        vec3 baseColor = vec3(0);
        vec3 col = mix(baseColor, vColor.rgb, mixing);
        gl_FragColor = vec4(col, vColor.a);
    }
`;

let renderer;
let camera;
let material;
let scene;
let circle;
let circleGeometry;
let circleTranslateArray;
let circleColorArray;
let circleScaleArray;
let circleMesh;
let controls;

renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor(0x404040);
renderer.domElement.setAttribute("id", "scatterplot");
document.body.insertAdjacentElement('afterbegin', renderer.domElement);

camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight );

scene = new THREE.Scene();

circleGeometry = new THREE.CircleBufferGeometry( 5.0, 50 );

circle = new THREE.InstancedBufferGeometry();
circle.index = circleGeometry.index;
circle.attributes = circleGeometry.attributes;
console.log(circle.attributes);

circleTranslateArray = [0.0, 0.0, 0.0];
circleColorArray = [1.0, 0.0, 1.0, 1.0];
circleScaleArray = [1.0];

circle.addAttribute( 'scale', new THREE.InstancedBufferAttribute( new Float32Array(circleScaleArray), 1 ));
circle.addAttribute( 'translate', new THREE.InstancedBufferAttribute( new Float32Array(circleTranslateArray), 3 ));
circle.addAttribute( 'color', new THREE.InstancedBufferAttribute( new Float32Array(circleColorArray), 4 ));

material = new THREE.RawShaderMaterial({ 
    uniforms: {
      inner: {value: 0.4},
      outer: {value: 0.45}
    },
    vertexShader: vshader,
    fragmentShader: fshader
});

circleMesh = new THREE.Mesh( circle, material );
circleMesh.scale.set( 350, 350, 350 );

scene.add( circleMesh );

controls = new THREE.OrbitControls(camera, document.getElementById("scatterplot"));
controls.enableDamping = true;
controls.dampingFactor = 0.25;
controls.mouseButtons = {
    LEFT: THREE.MOUSE.LEFT,
    MIDDLE: THREE.MOUSE.RIGHT
}
controls.rotateSpeed = 0.15;
controls.target = new THREE.Vector3(0,0,0);
controls.screenSpacePanning = false;
controls.enablePan = false;
controls.minDistance = 50;
controls.maxDistance = 1500;
controls.maxPolarAngle = Math.PI;
controls.minPolarAngle = 0;

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

var gui = new dat.GUI();
gui.add(material.uniforms.inner, "value", 0.0, 0.4).name("inner");

animate();

function animate() {
    requestAnimationFrame( animate );
    controls.update();
    renderer.render( scene, camera );
}

function onWindowResize( event ) {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize( window.innerWidth, window.innerHeight );
}
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://threejs.org/build/three.js
  2. https://threejs.org/examples/js/controls/OrbitControls.js
  3. https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.5/dat.gui.min.js