Running woman

A woman runs through a mysterious forest.

by realhunts

HTML

<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/loaders/GLTFLoader.js"></script>

<!-- Audio from https://freesound.org/people/arnaud%20coutancier/sounds/271323/
	Lincenced under Attribution-NonCommercial 3.0 Unported (CC BY-NC 3.0)
-->

<audio loop id="ambient" preload="auto" style="display: none">
	<source src="https://yume.human-interactive.org/examples/forest/run.ogg" type="audio/ogg">
</audio>

<section id="loading-screen">
	<div id="loader"></div>
</section>

<div id="start-screen">
  <button id="start" type="button">Start Demo</button>
</div>

CSS

html, body {
	overflow: hidden;
	margin: 0;
	width: 100%;
	height: 100%;
}

#loading-screen {
	position: absolute;
	z-index: 2;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	background-color: #000000;
	opacity: 1;
 	transition: 1s opacity;
}

#loading-screen.fade-out {
    opacity: 0;
}

#start-screen {
	position: absolute;
	z-index: 1;
	top: 0;
	left: 0;
  width: 100%;
  height:100%;
  display: flex;
  align-items: center;
  justify-content: center;
	opacity: 1;
 	transition: 1s opacity;
	background-color: #000000;
}

#start-screen.fade-out {
    opacity: 0;
}

#start-screen > button {
  height: 20px;
  width: 100px;
	color: #9370DB;
	background: transparent;
	outline: 1px solid #9370DB;
	border: 0px;
	cursor: pointer;
}

/* CSS from http://freefrontend.com/css-loaders/ */

#loader {
    display: block;
    position: relative;
    left: 50%;
    top: 50%;
    width: 150px;
    height: 150px;
    margin: -75px 0 0 -75px;
    border-radius: 50%;
    border: 3px solid transparent;
    border-top-color: #9370DB;
    -webkit-animation: spin 2s linear infinite;
    animation: spin 2s linear infinite;
}
#loader:before {
    content: "";
    position: absolute;
    top: 5px;
    left: 5px;
    right: 5px;
    bottom: 5px;
    border-radius: 50%;
    border: 3px solid transparent;
    border-top-color: #BA55D3;
    -webkit-animation: spin 3s linear infinite;
    animation: spin 3s linear infinite;
}
#loader:after {
    content: "";
    position: absolute;
    top: 15px;
    left: 15px;
    right: 15px;
    bottom: 15px;
    border-radius: 50%;
    border: 3px solid transparent;
    border-top-color: #FF00FF;
    -webkit-animation: spin 1.5s linear infinite;
    animation: spin 1.5s linear infinite;
}
@-webkit-keyframes spin {
    0%   {
        -webkit-transform: rotate(0deg);
        -ms-transform: rotate(0deg);
        transform: rotate(0deg);
    }
    100% {
        -webkit-transform: rotate(360deg);
        -ms-transform: rotate(360deg);
        transform:...

JavaScript

// ported from http://oos.moxiecode.com/js_webgl/forest/index.html

let camera, cameraTarget, scene, renderer, clock, textureLoader, glTFLoader;

let ground1, ground2;

let particles1, particles2;

const trees = new Set();
const rocks = new Set();
const flowers = new Set();

let r = 0;

init();

function init() {

    camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 100000 );
		cameraTarget = new THREE.Vector3( 0, 0, - 400 );
		
		clock = new THREE.Clock();

    scene = new THREE.Scene();
		scene.background = new THREE.Color( 0x473768 );
		scene.fog = new THREE.FogExp2( 0x473768, 0.0008 );
		
		//
		
		const loadingManager = new THREE.LoadingManager( onLoad );
		
		textureLoader = new THREE.TextureLoader( loadingManager );
		glTFLoader = new THREE.GLTFLoader( loadingManager );
		
		// setup scene

		addGround();
		addTrees();
		addRocks();
		addFlowers();
		addMoon();
		addCloud();
		addParticles()
		
		//

    renderer = new THREE.WebGLRenderer( { antialias: true } );
		renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize( window.innerWidth, window.innerHeight );
    document.body.appendChild( renderer.domElement );
		
		//
		
		window.addEventListener( 'resize', onWindowResize, false );
		
		// 
		
		const startButton = document.getElementById( 'start' );
		startButton.addEventListener( 'click', onStart, false );

}

function onWindowResize() {

		camera.aspect = window.innerWidth / window.innerHeight;
		camera.updateProjectionMatrix();
		
		renderer.setSize( window.innerWidth, window.innerHeight );

}

function animate() {

    requestAnimationFrame( animate );
		
		const delta = clock.getDelta();
		
		run( delta );
		
    renderer.render( scene, camera );

}

function run( delta ) {

	const speed = delta * 700;
	r += delta / 2;
	
	// animate camera
	
	camera.position.x = 20 * Math.cos( r * 2 );
	camera.position.y = 10 * Math.sin( r * 30 ) - 150;

	cameraTarget.x = 5 * Math.sin( r );
	cameraTarget.y =...