JSFiddle - React, Tailwind, and code Playground

by Josh Pullen

HTML

<script src="https://unpkg.com/[email protected]/build/three.min.js"></script>
<script src="https://unpkg.com/[email protected]/examples/js/controls/OrbitControls.js"></script>

CSS

body {
  margin: 0;
}

.sketchpadContainer {
  position: fixed;
  top: 10px;
  left: 10px;
}

#sketchpad {
  background: #a0a0a0;
  border-radius: 3px;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.4);
}

.sketchpadLabel {
  color: #ccc;
  text-shadow: 0 1px 4px black;
  text-align: center;
  font-family: sans-serif;
  font-size: 14px;
}

JavaScript

const scene = new THREE.Scene();

function sphere(u, v, vec) {
	u = u * 2 * Math.PI;
  v = v * Math.PI * 0.999 + 0.001;

	vec.x = Math.cos(u) * Math.sin(v);
  vec.y = Math.sin(u) * Math.sin(v);
  vec.z = Math.cos(v);
}

function cylinder(u, v, vec) {
	u = u * 2 * Math.PI;
  v = v * Math.PI;

	vec.x = Math.cos(u);
  vec.y = Math.sin(u);
  vec.z = Math.cos(v);
}

function torus(R = 1, r = 0.5) {
  return function(u, v, vec) {
    u = u * 2 * Math.PI;
    v = (0.5 - v) * 2 * Math.PI;

    vec.x = (R + r * Math.cos(v)) * Math.cos(u);
    vec.y = (R + r * Math.cos(v)) * Math.sin(u);
    vec.z = r * Math.sin(v);
  }
}

function morph(geom1, geom2, t) {
	return function(u, v, vec) {
    geom1(u, v, vec);

    let dest = new THREE.Vector3(0, 0, 0);
    geom2(u, v, dest);
    
    vec.multiplyScalar(1 - t);
    vec.addScaledVector(dest, t);
  }
}

function geomAtTime(t) {
	return morph(torus(0.5, 0.5), torus(1, 0.5), t);
}

// xy plane
const geometry = new THREE.ParametricGeometry(geomAtTime(0), 50, 50);

const material = new THREE.MeshStandardMaterial({
  color: 0xa0a0a0,
  side: THREE.DoubleSide
});

const mesh = new THREE.Mesh(geometry, material);

scene.add(mesh);

const camera = new THREE.PerspectiveCamera(
  40,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
camera.position.set(5, 5, 7);
camera.up = new THREE.Vector3(0, 0, 1);

const light = new THREE.AmbientLight(0x909090);
scene.add(light);

const light2 = new THREE.HemisphereLight(0xffffbb, 0x080820, 1);
scene.add(light2);

const renderer = new THREE.WebGLRenderer({
	antialias: true
});
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);

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

let t = 0;
function animate() {
  requestAnimationFrame(animate);
  
  t += 1;
  
  mesh.geometry = new THREE.ParametricGeometry(geomAtTime((Math.sin(t / 20) + 1) / 2), 50, 50);

  controls.update();

 ...