JSFiddle - React, Tailwind, and code Playground

HTML

<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>
    
<script type="importmap">
	{
		"imports": {
			"three": "https://unpkg.com/three@0.160.0/build/three.module.js",
      "three/addons/": "https://unpkg.com/three@0.160.0/examples/jsm/"
		}
	}
</script>

<link rel="preconnect" href="https://fonts.googleapis.com" />
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
    <link
      href="https://fonts.googleapis.com/css2?family=Amita:wght@400;700&family=Danfo&family=Gloria+Hallelujah&family=Jacquard+12&family=Micro+5&family=Monoton&family=Orbitron:wght@400..900&family=Rock+Salt&family=Rubik+Moonrocks&family=Shadows+Into+Light&family=Silkscreen:wght@400;700&family=Yarndings+20&display=swap"
      rel="stylesheet"
    />

<div>
Type Something!
</div>

CSS

body {
	  margin: 0;
    overflow: hidden;
}

div {
  position: absolute;
  left: 0;
  bottom: 1em;
  text-align: center;
  width: 100%;
  color: #fff;
  font: 500 2em sans-serif;
}

JavaScript

//@ts-check

import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";

// init
const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
camera.position.z = 2;

const scene = new THREE.Scene();
scene.background = new THREE.Color(0xff6f61);

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

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

window.onresize = () => {
  camera.aspect = window.innerWidth / window.innerHeight;
  camera.updateProjectionMatrix();
  renderer.setSize(window.innerWidth, window.innerHeight);
};

// Logic -------
const spritesPool = [];
const animations = new Map();

window.addEventListener("keyup", (e) => {
  const sprite =
    spritesPool.pop() ??
    new THREE.Sprite(
      new THREE.SpriteMaterial({
        map: new THREE.CanvasTexture(createCanvas(500)),
      })
    );

  scene.add(sprite);

  const canvasTexture = sprite.material.map;
  drawLetter(canvasTexture, e.key);
  initAnimatedSprite(sprite);
});

const SPEED = 5;
const GRAVITY_FORCE = new THREE.Vector3(0, -9.8, 0);
const velocity = new THREE.Vector3(0, 0, 0);
const origin = new THREE.Vector3(0, 0, 0);

function initAnimatedSprite(sprite) {
  sprite.visible = true;
  sprite.scale.setScalar(0.3);

  const distance = camera.position.distanceTo(origin) * 1.1;

  sprite.position.set(
    THREE.MathUtils.randFloat(-0.5, 0.5) * distance,
    THREE.MathUtils.randFloat(-0.5, 0.5) * distance,
    THREE.MathUtils.randFloat(-0.5, 0.5) * distance
  );

  animations.set(sprite, spriteAnimationCallback);

  function spriteAnimationCallback(delta) {
    sprite.position.add(
      velocity.copy(GRAVITY_FORCE).multiplyScalar(delta * delta * SPEED)
    );

    if (sprite.position.y < -distance * 0.5) {
...