Liquid Glass on the Web

by Andrew Holloway

HTML

<div class="content-frame">
  <div class="button-bar">
    <div class="button">
      <div class="backdrop"></div>
    </div>
  </div>
</div>

<canvas id="test"></canvas>

CSS

/*
 * Reference:
 * - https://www.apple.com/newsroom/2025/06/apple-introduces-a-delightful-and-elegant-new-software-design/
 * - MDN: backdrop-filter, mix-blend-mode, linear-gradient
 * - https://codepen.io/konstantindenerz/pen/GgRxXdg
 * - The Big One: https://offscreencanvas.com/issues/webgl-glass-and-refraction/
 * - Using ThreeJS from the code panel extension
 * - combining WebGL with DOM elements: https://drei.docs.pmnd.rs/getting-started/introduction
 * - : https://discourse.threejs.org/t/combine-webglrenderer-with-css3drenderer/37899
 * - : https://threejs.org/docs/#examples/en/renderers/CSS3DRenderer
 * - : https://codesandbox.io/examples/package/three-css3drenderer
 * - query: apply threejs mesh physical material to dom element
 * - Example of all WebGL implementation: https://codesandbox.io/p/sandbox/simplest-orb-refraction-8zbhm?file=%2Fsrc%2Findex.js%3A59%2C9-59%2C18&from-embed (use chrome)
 * - Tutorial: https://tympanus.net/codrops/2021/10/27/creating-the-effect-of-transparent-glass-and-plastic-in-three-js/
 * - https://developer.mozilla.org/en-US/docs/Web/CSS/element$0
 * 
 * NOTE: need access to three.js r129 or higher for the transparency material (not supported in jsfiddle)
 * - https://github.com/archisvaze/liquid-glass$0 (chrome-only)
 *
 * SVG Approach
 * - https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/feDisplacementMap$0
 * - https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/feTurbulence$0
 * - https://developer.mozilla.org/en-US/docs/Web/SVG/Reference/Element/feGaussianBlur$0
 * - Example: https://v0.dev/chat/dynamic-frame-layout-1VUCCecq7Uy$0 (https://github.com/shuding/liquid-glass?tab=readme-ov-file$0)
 * - Example: https://liquid-glass-eta.vercel.app/ (https://github.com/archisvaze/liquid-glass?tab=readme-ov-file)
 */

html,
body {
  margin: 0;
  padding: 0;
}

#test {
  height: 250px;
  width: 250px;
  position: fixed;
  top: 0;
  left: 0;
}

.content-frame...

JavaScript

// Get the item to apply the renderer to (?)
let canvas, context, renderer, camera, scene;

init();
render();

function init() {
  canvas = document.querySelector("#test");
  context = canvas.getContext("webgl");

  // Setup Scene, etc.
  renderer = new THREE.WebGLRenderer({ canvas, context}); // alpha: true
  renderer.setPixelRatio(window.devicePixelRatio);
  renderer.setSize(250, 250);
  renderer.setClearColor(0x1f1e1c, 1);

  camera = new THREE.PerspectiveCamera(45, 1, 0.01, 100);
  camera.position.set(0, 0, 5);

  scene = new THREE.Scene();

  // https://gist.github.com/eddieantonio/8510106c0334ad3145fa2ee011d875e8
  const bgTexture = new THREE.TextureLoader().load("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20xmlns%3Axlink%3D%22http%3A//www.w3.org/1999/xlink%22%20width%3D%2232%22%20height%3D%2232%22%20viewBox%3D%220%200%2032%2032%22%3E%3Cpath%20id%3D%22a%22%20d%3D%22M0%200h16v16H0z%22%20style%3D%22fill%3A%23ebebeb%22/%3E%3Cuse%20xlink%3Ahref%3D%22%23a%22%20transform%3D%22translate%2816%2016%29%22/%3E%3C/svg%3E");
  const bgGeometry = new THREE.PlaneGeometry(5, 5);
  const bgMaterial = new THREE.MeshBasicMaterial({ map: bgTexture });
  const bgMesh = new THREE.Mesh(bgGeometry, bgMaterial);
  bgMesh.position.set(0, 0, -1);
  scene.add(bgMesh);

  // Render the shape we want to have the effect
  // const spGeom = new THREE.SphereGeometry(3, 32, 32);
  // create material for the shape
  // const material = new THREE.MeshPhysicalMaterial({
  //   roughness: 0,
  //   transmission: 1,
  //   thickness: 0.5,
  // });
  const geometry = new THREE.IcosahedronGeometry(1, 0);
  const material = new THREE.MeshPhysicalMaterial({
    roughness: 0,
    transmission: 0
  });
  const mesh = new THREE.Mesh(geometry, material);
  scene.add(mesh);

  // const sp1 = new THREE.Mesh(spGeom, material);
  // sp1.position.set(0, 0, 3);
  // scene.add(sp1);

  let dirLight = new...