JSFiddle - React, Tailwind, and code Playground

by Matt

HTML

<base href="https://rawcdn.githack.com/mrdoob/three.js/r156/examples/" />
<script async src="https://cdn.jsdelivr.net/npm/[email protected]/dist/es-module-shims.js"></script>
<script type="importmap">
	{
		"imports": {
			"three": "https://cdn.jsdelivr.net/npm/[email protected]/build/three.module.js",
			"three/addons/": "https://cdn.jsdelivr.net/npm/[email protected]/examples/jsm/",
			"lil-gui": "https://cdn.jsdelivr.net/npm/[email protected]/dist/lil-gui.esm.min.js"
		}
	}
</script>

CSS

canvas {
	position: fixed;
	inset: 0;
}

JavaScript

import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { RGBELoader } from 'three/addons/loaders/RGBELoader.js'
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'
import { GUI } from 'lil-gui'

let scene, camera, renderer
let orbitControls, clock
let params, mixer, actions

const states = [ 'Idle', 'Walking', 'Running', 'Dance', 'Death', 'Sitting', 'Standing' ];
const emotes = [ 'Jump', 'Yes', 'No', 'Wave', 'Punch', 'ThumbsUp' ];

init().then(animate)

async function init() {

	params = {
	}
	
	scene = new THREE.Scene()

	camera = new THREE.PerspectiveCamera(75, undefined, 0.1, 1000)
	camera.position.set(4,4,4)
	
	clock = new THREE.Clock()

	renderer = new THREE.WebGLRenderer({ antialias: true })
	renderer.toneMapping = THREE.ACESFilmicToneMapping
	document.body.appendChild(renderer.domElement)

	orbitControls = new OrbitControls(camera, renderer.domElement)
  orbitControls.target.set(0, 2, 0)
	
	const rgbeLoader = new RGBELoader()
	const gltfLoader = new GLTFLoader()
	
	const [ hdri, gltf ] = await Promise.all([
		rgbeLoader.loadAsync('textures/equirectangular/venice_sunset_1k.hdr'),
		gltfLoader.loadAsync('models/gltf/RobotExpressive/RobotExpressive.glb'),
	])
  
  mixer = new THREE.AnimationMixer( gltf.scene );

  actions = {};

  for ( let i = 0; i < gltf.animations.length; i ++ ) {

    const clip = gltf.animations[ i ];
    const action = mixer.clipAction( clip ).play();
    action.enabled = false
    actions[ clip.name ] = action;
    
    if ( emotes.indexOf( clip.name ) >= 0 || states.indexOf( clip.name ) >= 4 ) {

      action.clampWhenFinished = true;
      action.loop = THREE.LoopOnce;

    }

  }
  
  
	hdri.mapping = THREE.EquirectangularReflectionMapping
	
	scene.background = hdri
	scene.environment = hdri
	
	scene.add(gltf.scene)

	const gui = new GUI()
  
  for (const [name, action] of Object.entries(actions)) {
  	const folder = gui.addFolder(name)
    folder.add(action,...