<div class='canvas-container'></div>

<script src='https://unpkg.com/three@0.99.0/build/three.min.js'></script>
<script src='https://unpkg.com/three@0.99.0/examples/js/controls/OrbitControls.js'></script>
<script src='https://unpkg.com/three@0.99.0/examples/js/postprocessing/EffectComposer.js'></script>
<script src='https://unpkg.com/three@0.99.0/examples/js/postprocessing/RenderPass.js'></script>
<script src='https://unpkg.com/three@0.99.0/examples/js/postprocessing/ShaderPass.js'></script>
<script src='https://unpkg.com/three@0.99.0/examples/js/shaders/CopyShader.js'></script>
<script src='https://unpkg.com/three@0.99.0/examples/js/shaders/LuminosityHighPassShader.js'></script>
<script src='https://unpkg.com/three@0.99.0/examples/js/postprocessing/UnrealBloomPass.js'></script>


<script id='sphere-vertex-shader' type='x-shader/x-vertex'>
    uniform float uTime;
    
    varying vec3 vNormal;
    varying vec3 vPosition;
    

    void main() {
        vNormal = normal;
        
        vec3 delta = 40.0 * normal * sin(
            abs(normal.x) * 2.0 +
            abs(normal.y) * 3.0 +
            abs(normal.z) * 4.0 + uTime / 2.0);
        
        delta.x += 20.0 * sin(uTime * normal.z);
        delta.y += 20.0 * sin(uTime * normal.x);
        delta.z += 20.0 * sin(uTime * normal.y);
        
        vec3 newPosition = position + delta;
        
        vPosition = newPosition;

        gl_Position = projectionMatrix * modelViewMatrix * vec4(newPosition, 1.0);
        gl_PointSize = 1.0;
    }
    

</script>


<script id='sphere-fragment-shader' type='x-shader/x-fragment'>
    uniform float uTime;
    
    varying vec3 vNormal;
    varying vec3 vPosition;
    
    
    void main() {
        gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
    }
    
</script>
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    overflow: hidden;
}

.canvas-container {
    height: 100vh;
    width: 100vw;
    background: #6a558e;
}
View Compiled
let SCENE;
let CAMERA;
let RENDERER;
let CONTROLS;
let COMPOSER;

let TIME = 10; // Let it be non zero at start


main();


function main() {
    init();
    animate();
}


function init() {
    initScene();
    initCamera();
    initRenderer();
    initComposer();
    initControls();
    initEventListeners();

    createObjects();

    document.querySelector('.canvas-container').appendChild(RENDERER.domElement);
}


function initScene() {
    SCENE = new THREE.Scene();

    initLights();
}


function initLights() {
    const point = new THREE.PointLight(0xffffff, 1, 0);
    point.position.set(0, 100, 50);
    SCENE.add(point);
}


function initCamera() {
    CAMERA = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 2000);
    CAMERA.position.z = 100;
}


function initRenderer() {
    RENDERER = new THREE.WebGLRenderer({ alpha: true });
    RENDERER.setPixelRatio(window.devicePixelRatio);
    RENDERER.setSize(window.innerWidth, window.innerHeight);
    RENDERER.shadowMap.enabled = true;
    RENDERER.shadowMapSort = true;
    RENDERER.setClearColor(0x6a558e, 0.3);
}


function initComposer() {
    COMPOSER = new THREE.EffectComposer(RENDERER);
    COMPOSER.setSize(window.innerWidth, window.innerHeight);

    const renderPass = new THREE.RenderPass(SCENE, CAMERA);
    COMPOSER.addPass(renderPass);

    const bloomPass = new THREE.UnrealBloomPass(
        new THREE.Vector2(window.innerWidth, window.innerHeight), 1.5, 1, 0.1);
    bloomPass.renderToScreen = true;
    COMPOSER.addPass(bloomPass);
}


function initControls() {
    CONTROLS = new THREE.OrbitControls(CAMERA);
    CONTROLS.enableZoom = false;
    CONTROLS.minPolarAngle = Math.PI * 1 / 4;
    CONTROLS.maxPolarAngle = Math.PI * 3 / 4;
    CONTROLS.update();
}


function initEventListeners() {
    window.addEventListener('resize', onWindowResize);

    onWindowResize();
}


function onWindowResize() {
    CAMERA.aspect = window.innerWidth / window.innerHeight;
    CAMERA.updateProjectionMatrix();

    RENDERER.setSize(window.innerWidth, window.innerHeight);
    COMPOSER.setSize(window.innerWidth, window.innerHeight);
}


function animate() {
    requestAnimationFrame(animate);
    CONTROLS.update();
    TIME += 0.005;
    updateUniforms();
    render();
}


function updateUniforms() {
    SCENE.traverse(function(child) {
        if (child instanceof THREE.Points
            && child.material.type === 'ShaderMaterial') {
            child.material.uniforms.uTime.value = TIME;
            child.material.needsUpdate = true;
        }
    });
}


function render() {
    CAMERA.lookAt(SCENE.position);
    COMPOSER.render(SCENE, CAMERA);
}


function createObjects() {
    const geometry = new THREE.TorusBufferGeometry(25, 1, 3000, 13);

    const shaderMaterial = new THREE.ShaderMaterial({
        uniforms: {
            uTime: { value: TIME }
        },
        transparent: true,
        side: THREE.DoubleSide,
        vertexShader:   document.getElementById('sphere-vertex-shader').textContent,
        fragmentShader: document.getElementById('sphere-fragment-shader').textContent
    });

    const torus = new THREE.Points(geometry, shaderMaterial);

    SCENE.add(torus);
}
View Compiled

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.