JSFiddle - React, Tailwind, and code Playground

by tfoller

HTML

<link type="text/css" rel="stylesheet" href="https://alikim.com/_v1_jsm/css/controls.css">

<script type="x-shader/x-vertex" id="vs">

  varying vec2 vuv;
  
  void main() {
     
    vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
    
    gl_Position = projectionMatrix * mvPosition;
    
    vuv = uv;
  }
</script>

<script type='x-shader/x-fragment' id='fs'>

  varying vec2 vuv;
  
  uniform float time;
  uniform sampler2D map;

  void main() {
  
  	vec2 uv = vuv;
    
    float amp = 0.3;
    float freq = 3.0;
    
    uv.x = 0.5 * (1.0 + amp * sin(freq * vuv.y + time)) + (1.0 - amp) * (uv.x - 0.5);
  
    vec3 clr = texture2D(map, uv).rgb;
    
    
    gl_FragColor = vec4(clr, 1.0);
  }
</script>

CSS

body {
	  margin: 0;
}

JavaScript

import * as THREE from 'https://alikim.com/_v1_jsm/three.module.js';
import * as CONTROLS from 'https://alikim.com/_v1_jsm/controls.js'

const get = id => document.getElementById(id);

const cont = document.documentElement;
const [w, h] = [cont.clientWidth - 20, cont.clientHeight - 20];

const renderer = new THREE.WebGLRenderer();
renderer.setSize(w, h);
document.body.appendChild(renderer.domElement);

const [near, far] = [0.1, 100];
const mid = (near - far) / 2;
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(50, w / h, near, far);
camera.position.set(10, 10, 10);

const hlp = new THREE.AxesHelper(3);
scene.add(hlp);

const render = () => {
  renderer.render(scene, camera);
};

const map = new THREE.TextureLoader().load(
  'https://source.unsplash.com/random/200x200',
  render,
);

const geo = new THREE.PlaneBufferGeometry(10, 10, 1, 1);
const msh = new THREE.Mesh(
  geo,
  new THREE.ShaderMaterial({
    vertexShader: get('vs').textContent,
    fragmentShader: get('fs').textContent,
    uniforms: {
			map: { value: map },
      time: { get value() { return 0.001 * performance.now() }},
    },
  })
);

scene.add(msh);

const state = { play: true };

	const maybeRender = () => { if (!state.play) render() };

	const framerate = 60;
	const fpsint = 1000 / framerate;
	let elapsed, now, then;

	const renderFrame = newtime => {

		if (!state.play) return;
		requestAnimationFrame(renderFrame);

		if (!newtime) then = newtime = window.performance.now();

		now = newtime;
		elapsed = now - then;

		if (!fpsint || elapsed > fpsint) {

			then = now - (elapsed % fpsint);

			render();
		}
	};

CONTROLS.create({
  cont: renderer.domElement,
  cam: camera,
  type: 'Orbital',
  overlay: true,
  lookAt: [0, 0, 0],
  callback: render
});

renderFrame();