<div id="container"></div>
html {
  overflow: hidden;
}

body {
  margin: 0;
  padding: 0;
  overflow: hidden;
  color: #fff;
  background: #4831ab;
}
View Compiled

var container = document.getElementById( 'container' );

var renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );

var camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, .1, 1000 );
camera.position.set( 0, 0, 4 );

var scene = new THREE.Scene();
scene.background = new THREE.Color( 0x4831ab );

// lights
var aLight = new THREE.AmbientLight( 0x7f46ab );
scene.add( aLight );

// ---------------------------------------------------------------
// ---------------------------------------------------------------

// clouds

var cloudsSize = new THREE.Vector3( 15, 4, 6 );
var cloudsBox = new THREE.Box3();
cloudsBox.setFromCenterAndSize( new THREE.Vector3(), cloudsSize );

// -----------------------------------------------------------------

var sphereGeo = new THREE.SphereBufferGeometry( 0.2, 32, 16 );
var cloudMat = new THREE.MeshPhongMaterial({
  color: 0xffffff,
  shininess: 0
});

var Cloud = function() {

  THREE.Group.call( this );

  this.spheres = [];

  // fill in 3 boxes with a random number of
  // randomly positioned and randomly scaled spheres

  // large base
  var center1 = new THREE.Vector3();
  var size1 = new THREE.Vector3( 1, 0.25, 0.6 );
  var number1 = THREE.Math.randInt( 10, 25 );
  var scaleRange1 = new THREE.Vector2( 0.2, 1 );
  this.fillBox( center1, size1, number1, scaleRange1 );

  // top of cloud
  var center2 = new THREE.Vector3( 0, size1.y, 0 );
  var size2 = new THREE.Vector3( 0.5, 0.25, 0.5 );
  var number2 = THREE.Math.randInt( 5, 15 );
  var scaleRange2 = new THREE.Vector2( 0.2, 1 );
  this.fillBox( center2, size2, number2, scaleRange2 );

  // small tail
  var center3 = new THREE.Vector3( -0.7, 0, 0 );
  var size3 = new THREE.Vector3( 0.4, 0.2, 0.25 );
  var number3 = THREE.Math.randInt( 3, 7 );
  var scaleRange3 = new THREE.Vector2( 0.1, 0.5 );
  this.fillBox( center3, size3, number3, scaleRange3 );

  this.userData.velocity = THREE.Math.randFloat( 0.003, 0.007 );

};

Cloud.prototype = Object.create( THREE.Group.prototype );
Cloud.prototype.constructor = Cloud;

Cloud.prototype.fillBox = function( center, size, number, scale ){

  var box = new THREE.Box3();
  box.setFromCenterAndSize( center, size );

  var boxGroup = new THREE.Group();
  boxGroup.position.copy( center );
  this.add( boxGroup );

  for ( var i = 0; i < number; i++ ) {

    var sphere = new THREE.Mesh( sphereGeo, cloudMat );

    sphere.position.set(
      THREE.Math.randFloatSpread( size.x ),
      THREE.Math.randFloatSpread( size.y ),
      THREE.Math.randFloatSpread( size.z )
    );

    sphere.userData.scale = THREE.Math.randFloat( scale.x, scale.y );
    sphere.scale.setScalar( sphere.userData.scale );

    sphere.userData.velocity = THREE.Math.randFloat( 0.001, 0.002 );

    boxGroup.add( sphere );
    this.spheres.push( sphere );

  }

  return boxGroup;

};

Cloud.prototype.update = function( t ){

  // scale individual spheres
  for ( var i = 0; i < this.spheres.length; i++ ) {
    var sphere = this.spheres[i];
    var scaleOffset = Math.sin( t + ( i + 1 ) ) * 0.4 + 1;
    sphere.scale.setScalar( sphere.userData.scale * scaleOffset );
  }

  // move entire cloud forward
  this.position.x += this.userData.velocity;

  // reset cloud position
  if ( this.position.x > cloudsBox.max.x ) {
    this.position.x = cloudsBox.min.x;
  }

};

// -----------------------------------------------------------------

// fill clouds box with clouds

var clouds = [];

for ( var i = 0; i < 40; i++ ) {

  var cloud = new Cloud();

  cloud.position.set(
    THREE.Math.randFloatSpread( cloudsSize.x ),
    THREE.Math.randFloatSpread( cloudsSize.y ),
    THREE.Math.randFloatSpread( cloudsSize.z )
  );

  scene.add( cloud );
  clouds.push( cloud );

}

// -----------------------------------------------------------------
// -----------------------------------------------------------------

// moon

var MoonShader = {

  uniforms: {

    colorA: { type: 'v3', value: scene.background },
    colorB: { type: 'v3', value: new THREE.Color('#fff') },

  },

  vertexShader: [

    "varying vec2 vUv;",
    "void main() {",
    " vUv = uv;",
    " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
    "}"

  ].join('\n'),

  fragmentShader: [

    "varying vec2 vUv;",
    "uniform vec3 colorA;",
    "uniform vec3 colorB;",

    "void main() {",
    " float pct = smoothstep( 0.39, 0.44, vUv.y );",
    " vec3 col = mix( colorA, colorB, pct );",
    " gl_FragColor = vec4( col, 1.0 );",
    "}"

  ].join('\n')

};

var moonGeo = new THREE.SphereBufferGeometry( 2, 32, 32 );
var moonMat = new THREE.ShaderMaterial({
  uniforms: THREE.UniformsUtils.clone( MoonShader.uniforms ),
  vertexShader: MoonShader.vertexShader,
  fragmentShader: MoonShader.fragmentShader,
});
var moon = new THREE.Mesh( moonGeo, moonMat );
moon.position.set( -3, 4, -20 );
moon.rotation.set(
  - Math.PI / 2,
  - Math.PI / 6,
  Math.PI / 4,
);
scene.add( moon );

var moonLight = new THREE.DirectionalLight( 0xffffff, 0.8 );
moonLight.position.copy( moon.position );
scene.add( moonLight );

//

// key light

var dLight = new THREE.DirectionalLight( 0xffffff, 0.2 );
dLight.position.set( 1, 1, 1 );
scene.add( dLight );

// ---------------------------------------------------------------
// ---------------------------------------------------------------

// stars

StarShader = {

  uniforms: {

    color:   { type: 'v3', value: new THREE.Color( 0xffffff ) },
    texture: { type: 't', value: null },
    time:    { type: 'f', value: 0 },
    size:    { type: 'f', value: 500.0 }

  },

  vertexShader: [

    'uniform float time;',
    'uniform float size;',
    'attribute float alphaOffset;',
    'varying float vAlpha;',
    'uniform vec4 origin;',

    'void main() {',

      'vAlpha = 0.5 * ( 1.0 + sin( alphaOffset + time ) );',

      'vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );',
      'float cameraDist = distance( mvPosition, origin );',
      'gl_PointSize = size / cameraDist;',
      'gl_Position = projectionMatrix * mvPosition;',

    '}'

  ].join('\n'),

  fragmentShader: [

    'uniform float time;',
    'uniform vec3 color;',
    'uniform sampler2D texture;',

    'varying float vAlpha;',

    'void main() {',
    '  gl_FragColor = vec4( color, vAlpha );',
    '  gl_FragColor = gl_FragColor * texture2D( texture, gl_PointCoord );',
    '}'

  ].join('\n')
};

var Stars = function( options ) {

  var color = this.color = options.color || 0x333333;
  var size = this.size = options.size || 0.4;

  var pointCount = this.pointCount = options.pointCount || 40;
  var range = this.range = options.range || new THREE.Vector3( 2, 2, 2 );

  THREE.Group.call( this );

  // circle texture

  var canvas = document.createElement('canvas');
  canvas.width = canvas.height = 128;
  var ctx = canvas.getContext( '2d' );

  var centerX = canvas.width / 2;
  var centerY = canvas.height / 2;
  var radius = canvas.width / 3;

  ctx.beginPath();
  ctx.arc( centerX, centerY, radius, 0, 2 * Math.PI, false );
  ctx.fillStyle = '#fff';
  ctx.fill();

  var texture = new THREE.Texture( canvas );
  texture.premultiplyAlpha = true;
  texture.needsUpdate = true;

  //

  StarShader.uniforms.texture.value = texture;
  StarShader.uniforms.size.value = size;

  var pointsMat = new THREE.ShaderMaterial( {
    uniforms:       StarShader.uniforms,
    vertexShader:   StarShader.vertexShader,
    fragmentShader: StarShader.fragmentShader,
    blending:       THREE.AdditiveBlending,
    depthWrite: false,
    transparent:    true
  });

  var pointsGeo = new THREE.BufferGeometry();

  var positions = new Float32Array( pointCount * 3 );
  var alphas = new Float32Array( pointCount );

  for ( var i = 0; i < pointCount; i++ ) {

    positions[ i*3 + 0 ] = THREE.Math.randFloatSpread( range.x );
    positions[ i*3 + 1 ] = THREE.Math.randFloatSpread( range.y );
    positions[ i*3 + 2 ] = THREE.Math.randFloatSpread( range.z );

    alphas[ i ] = i;

  }

  pointsGeo.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
  pointsGeo.addAttribute( 'alphaOffset', new THREE.BufferAttribute( alphas, 1 ) );

  var points = this.points = new THREE.Points( pointsGeo, pointsMat );
  points.sortParticles = true;
  points.renderOrder = 1;

  this.add( points );

}

Stars.prototype = Object.create( THREE.Group.prototype );
Stars.prototype.constructor = Stars;

// ---------------------------------------------------------------

var stars = new Stars({
  color: 0xffffff,
  range: new THREE.Vector3( 110, 60, 30 ),
  pointCount: 400,
  size: 400,
  speed: 0.1
});

scene.add( stars );

stars.position.z = -50;

// ---------------------------------------------------------------
// ---------------------------------------------------------------

window.addEventListener( 'resize', resize, false );
function resize() {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize( window.innerWidth, window.innerHeight );
}

var clock = new THREE.Clock();

renderer.setAnimationLoop( loop );

function loop() {

  var time = clock.getElapsedTime();

  for ( var i = 0; i < clouds.length; i++ ) {
    clouds[i].update( time );
  }

  stars.points.material.uniforms.time.value = time;

  renderer.render( scene, camera );
}
Run Pen

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