Skip to component
Modulo

Textured glass

Textured glass with image depth, mouse parallax, and adjustable focus.

Preview

stay.jpeg

Glass

More settings

Installation

bunx shadcn@latest add @modulo/textured-glass

Requires an active Modulo key and a project configured for shadcn.

Registry setup

Set MODULO_REGISTRY_KEY to your key. Add this registry entry to components.json.

{
  "registries": {
    "@modulo": {
      "url": "<registry-url>/r/{name}.json",
      "headers": {
        "Authorization": "Bearer ${MODULO_REGISTRY_KEY}"
      }
    }
  }
}

Usage

import { TexturedGlass } from "@/components/textured-glass/TexturedGlass";

export default function Example() {
  return (
    <TexturedGlass
      src="/photo.jpg"
      alt="Portrait through textured glass"
      roughness={0.68}
      distortion={0.012}
    />
  );
}

React, Three.js, and Transformers.js. All six source files install together. Give the component an image URL and a bounded container.

Registry JSONDownload thumbnailDownload preview GIFDownload preview video

Interaction

Move the pointer across the image to change the camera angle and light. An estimated depth map shapes the photograph into a mesh behind the glass. Closer objects move differently from distant ones. Focus the image and use the arrow keys for keyboard control.

Focus depth ranges from distant objects at 0 to nearby objects at 1. Focus blur softens objects outside that depth. Object depth controls the mesh relief. Depth map displays the estimate, with brighter areas closer to you.

Uploads stay in your browser. The first upload downloads Depth Anything V2 Small and analyzes the image in a worker. Later uploads reuse the model. The default image uses its precomputed depth map. The demo accepts images up to 20 MB. Use default restores stay.jpeg. Save PNG captures the current view.

Depth is estimated from one photograph. Large movements can stretch object boundaries where hidden image content is unavailable.

Pause freezes the camera and light. Hidden tabs and offscreen previews stop rendering. Reduced motion keeps a still view while image uploads and settings remain available.

Reconstructed from Neilson Koerner-Safrata's video. The original shader is unpublished, so exact fidelity has not been verified.

API

PropTypeDefault
srcImage URL, including a browser object URL. Remote images need CORS access. Images larger than 4096 pixels are resized for the GPU.stringRequired
altAccessible description of the image.stringRequired
depthMapPrecomputed depth image URL. White is near, black is far. Skips automatic analysis.stringOptional
estimateDepthEstimate image depth locally with Depth Anything V2 Small in a worker. The first analysis downloads the model.booleantrue
roughnessGlass normal strength from 0 to 2.number0.68
distortionRefraction distance from 0 to 0.08, relative to image height.number0.012
blurImage blur radius from 0 to 30 CSS pixels.number5
textureScaleGlass texture repetitions from 0.25 to 8.number2.4
gridWire grid opacity from 0 to 1.number0.65
gridSizeSquare grid cells across image height, from 10 to 200.number90
lightIntensityReflected light intensity from 0 to 2.number0.75
lightSizeHighlight radius from 0.1 to 1.5, relative to image height.number0.5
speedAutomatic light speed from 0 to 3.number0.45
depthDistance between the image and glass plane, from 0 to 2 world units.number0.55
parallaxPointer camera movement from 0 to 1.5 world units.number0.7
depthStrengthDepth-map displacement from 0 to 2 world units. Zero flattens the image mesh.number0.8
focusDepthFocus distance from 0 for far objects to 1 for near objects.number1
focusBlurBlur away from the focus depth, from 0 to 30 CSS pixels.number12
showDepthDisplay the estimated depth map.booleanfalse
animateMove the light automatically. The pointer takes control while over the image.booleantrue
enabledTurn off the glass to display the original image.booleantrue
pausedFreeze light movement. Settings still apply.booleanfalse
classNameClass names for the container.string""
onReadyController after image loading, null on unmount.(controller | null) => voidOptional
onFrameTime, light position, visibility, pause, and reduced-motion status.(GlassFrame) => voidOptional
onErrorImage loading or WebGL initialization failure.(Error) => voidOptional
onDepthLocal depth loading progress, analysis, completion, or failure.(DepthState) => voidOptional
Controller methods

configure(settings) updates the effect. setImage(url) replaces the image without rebuilding the renderer. setLight(x, y) sets a normalized light position.

pause(), resume(), and reset() control the scene. estimateDepth() retries local image analysis. snapshot() reports camera position, depth-map status, settings and GPU resource counts. exportPNG() returns a PNG blob. The component calls destroy() on unmount.

Source

Checking source access...