JSFiddle - React, Tailwind, and code Playground

by Paul West

HTML

<script type="importmap">
  {
    "imports": {
      "three": "https://unpkg.com/[email protected]/build/three.module.js",
      "three/addons/": "https://unpkg.com/[email protected]/examples/jsm/",
      "three-subdivide": "https://unpkg.com/[email protected]/build/index.module.js"
    }
  }
</script>

CSS

body{
  overflow: hidden;
  margin: 0;
}

JavaScript

import * as THREE from "three";
import {
  OrbitControls
} from "three/addons/controls/OrbitControls.js";
import { LoopSubdivision } from 'three-subdivide';
import * as TWEEN from "three/addons/libs/tween.module.js";

class WobbleHead extends THREE.Group {
  constructor() {
		super();
    
    let body = new THREE.Mesh(
    	new THREE.CylinderGeometry(0.5, 0.25, 3, 32, 1).translate(0, 1.5, 0).scale(2, 1, 1),
      new THREE.MeshLambertMaterial({color: "white"})
    );
    
    
    let head = new THREE.Mesh(
    	LoopSubdivision.modify(new THREE.CylinderGeometry(1, 0.5, 1, 3, 1).translate(0, 0.5, 0), 3)
      .rotateY(Math.PI).rotateX(-Math.PI * 0.05).translate(0, 0, 0.125),
      new THREE.MeshLambertMaterial({color: 0xface8d})
    );
    head.position.set(0, 3, 0);
    this.head = head;
    
    body.add(head);
    this.add(body);
    
    this.animations = {
    	no: () => {
      	new TWEEN.Tween({val: 0})
        .to({val: 1}, 500)
        .onUpdate(val => {
        	this.head.rotation.y = Math.sin(val.val * Math.PI * 3) * 0.2;
        })
        .start();
      },
      yes: () => {
      	new TWEEN.Tween({val: 0})
        .to({val: 1}, 500)
        .onUpdate(val => {
        	this.head.rotation.x = Math.sin(val.val * Math.PI * 3) * 0.2;
        })
        .start();
      }
    }
    
  }
}

let scene = new THREE.Scene();
scene.background = new THREE.Color("#102");
let camera = new THREE.PerspectiveCamera(45, innerWidth / innerHeight, 1, 1000);
camera.position.set(5, 10, 5).setLength(10);
let renderer = new THREE.WebGLRenderer({
  antialias: true
});
renderer.setSize(innerWidth, innerHeight);
document.body.appendChild(renderer.domElement);

window.addEventListener("resize", event => {
  camera.aspect = innerWidth / innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(innerWidth, innerHeight);
})

let controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;

let light = new THREE.DirectionalLight(0xffffff,...